Я мутирую? Если да, какие решения доступны?

У меня есть простой компонент, который создает список отчетов из массива объектов, я пытаюсь добавить функцию фильтрации и сортировки (которая работает - до некоторой степени), функция сортировки работает, но я боюсь, что меняю состояние, несмотря на попытку скопировать исходное состояние в новый массив.

Мой фильтр работает в течение первого раза, но тогда он не будет фильтровать другие результаты, потому что состояние было изменено, или потому что я не могу фильтровать через начальное состояние? Это меня смущало в течение многих часов, любая помощь очень ценится.

Спасибо большое

constructor(props) {
    super(props);
    this.state = {
        reports: props.data
    };
    this.handleSortBy = this.handleSortBy.bind(this);
    this.handleFilterType = this.handleFilterType.bind(this);
}

handleSortBy(event) {
    const copy = [...this.state.reports];
    if (event.target.value === 'A-Z') {
        return this.setState({
            reports: copy.sort((a, b) => a.name.localeCompare(b.name))
        });
    }
    if (event.target.value === 'Z-A') {
        this.setState({
            reports: copy
                .sort((a, b) => a.name.localeCompare(b.name))
                .reverse()
        });
    }
}

handleFilterType(event) {
    this.setState({
        reports: this.state.reports.filter(item => {
            return item.type === event.target.value;
        })
    });
}

Заранее спасибо:)

2 ответа

Решение

Один простой способ сделать это в зависимости от вашего варианта использования - это заменить this.state.reports с this.props.data в ваших двух функциях обновления. (Хотя, если вы сделаете это таким образом, вы не сможете применить сортировку и фильтр одновременно)

Но я согласен с ModestLeech, что лучше хранить фильтр в состоянии. Для этого измените код фильтра на что-то вроде:

handleFilterType(event) {
    this.setState({
        filter: event.target.value;
        })
    });
}

И в вашем методе рендеринга, если вы используете карту, вы можете изменить карту на this.state.reports.filter(item => (this.state.filter===undefined || item.type===this.state.filter)).map(...),

РЕДАКТИРОВАТЬ:

Есть два типа мутаций, о которых вам нужно беспокоиться с помощью React.

Один не мутирует ваш реквизит (то есть мутирует состояние, которое ваш компонент не контролирует). Поскольку Javascript передает массивы и объекты по ссылке, если бы вы sort на this.props.data это фактически изменило бы массив, который существует в родительском компоненте вашего компонента (или дедушке, или где бы он ни был определен). Хорошей новостью является то, что ваш оригинальный код уже избежал этого - возможно, случайно. handleSortBy сделал копию массива перед тем, как изменить его, и handleFilterType использования Array.prototype.filter, который создает новый массив.

Вторая мутация, о которой вам нужно беспокоиться - это изменение локального состояния без использования setState, призвание this.state.reports.sort(...) в render изменит локальное состояние, но React не узнает и не выполнит повторную визуализацию. Это не проблема, поскольку вы сортируете прямо перед тем, как вам это нужно, но прямое изменение состояния опасно, потому что это может привести к ошибкам, когда вы, как разработчик, думаете, что что-то должно или не должно измениться при определенных условиях, но реальность другая,

Ваша первоначальная проблема с filter не имеет ничего общего со случайной мутацией. Проблема заключалась в том, что, позвонив this.setState первый раз с фильтром переписал this.state.reports и не было никакого способа вернуть его. Также была проблема, что если this.props.data При изменении родительского элемента новые отчеты никогда не будут отображаться в этом компоненте, поскольку вы читаете реквизиты только при первом создании компонента.

Рекомендованный способ React - не хранить что-либо в состоянии, исходящем от подпорки, если только вы не используете подпорку для установки начального состояния и все будущие обновления будут поступать изнутри компонента. Но если предположить, что этот компонент в конечном итоге не владеет списком отчетов, в конечном итоге было бы проще сделать копию this.props.data в render и примените свой сортировку и фильтр к нему. Затем, если что-либо будет добавлено или удалено выше в дереве компонентов, вам не нужно будет писать какую-либо специальную логику для обновления локального состояния этого компонента.

Я думаю, что проблема в том, что каждый раз, когда вы фильтруете, вы удаляете элементы из state.reportsи никогда не получит их от props.data,

Вместо этого вы должны восстановить state.reports от props.data каждый раз, когда вы фильтруете / сортируете.

Или лучшим способом, IMO, было бы сохранить настройки фильтрации / сортировки в stateи выполнить фильтрацию / сортировку props.data в render на основании текущих настроек.

Другие вопросы по тегам