Избегайте мутации состояния с помощью Object.assign () внутри map () - PullRequest
0 голосов
/ 29 февраля 2020

В componentDidMount я сделал это:

     apps.forEach(app => {
            if (chosenAppId) {
                if (app.id === chosenAppId) {
                    this.setState({ map: this.props.map });
                }
            }
        });

и теперь, когда я делаю это в какой-то функции:

 this.setState({
            ...this.state.map,
            areas: this.state.map.areas.map(el =>
                el._id === area._id
                    ? Object.assign(el, {
                          chooseDevice: false,
                          editModal: true
                      })
                    : Object.assign(el, { chooseDevice: false })
            )
        });

У меня постоянное состояние притока, в этом случае я ожидал бы, что при перезагрузке this.state.map === this.props.map, но каким-то образом этот object.assign мутировал мое состояние редукса, а при перезагрузке все сохраняется в редукторе.

Я сузил это, что это имеет какое-то отношение к object.assign, потому что, если я .concat () что-то для this.state.map, это не меняет состояние приведения.

Как? Я действительно не понимаю. Никаких избыточных действий не отправляется, не знаю, как это может произойти.

Ответы [ 4 ]

3 голосов
/ 29 февраля 2020

Строка Object.assign(el, { chooseDevice: false }) будет видоизменяться el.

Похоже, вы скопировали это из реквизита (и, следовательно, скорее всего, из хранилища Redux) в состояние. Итак, это та же самая ссылка на объект, которая уже была в хранилище Redux, и поэтому вы изменяете значение, которое находится в хранилище.

Обратите внимание, что наш официальный пакет Redux Toolkit включает в себя мутацию ПО обнаружения по умолчанию, которое будет выдавать ошибки при случайном изменении значений.

1 голос
/ 29 февраля 2020

С Object.assign ()

Метод Object.assign() копирует все перечисляемые собственные свойства из одного или нескольких объектов source в объект target .

Object.assign(target, ...sources)

target

Целевой объект - к чему применять свойства источников, который возвращается после его изменения.

sources

Исходный объект (ы) - объекты, содержащие свойства, которые вы хотите применить.

Object.assign () - JavaScript | MDN

Если вы не хотите изменять предоставленный элемент, выберите целевой объект. Таким образом, el и дополнительные данные в третьем аргументе будут назначены новому объекту, вместо переопределения свойств el.

this.setState(prevState => ({
    ...prevState.map,
    areas: prevState.map.areas.map(el =>
        el._id === area._id
            ? Object.assign({}, el, {
                  chooseDevice: false,
                  editModal: true
              })
            : Object.assign({}, el, { chooseDevice: false })
    )
}));

с расширением объекта (компактным)

Если вы хотите сделать их более компактными, вы также можете превратить их в разворот объекта и использовать встроенный оператор if для условного изменения в editModal.

this.setState(prevState => ({
    ...prevState.map,
    areas: prevState.map.areas.map(el => ({
        ...el,
        chooseDevice: false,
        editModal: el._id === area._id ? true : el.editModal
    }))
}));

EDIT: this.state не следует использовать в setState

1 голос
/ 29 февраля 2020

Когда вы распространяете объект, он не клонирует существующие свойства, он просто копирует их в новый объект, то есть свойства экземпляра остаются, например,

    const obj = { numbers: [1, 2, 3], person: { name: 'Foo' } }
    const copy = { ...obj };
    copy.numbers.push(4);
    copy.person.name = 'Bar'
    console.log(obj.numbers) // [1,2,3,4]
    console.log(obj.person) // { name: 'Bar' }
    console.log(copy.numbers) // [1,2,3,4]
    console.log(copy.person) // { name: 'Bar' }

Обратите внимание, как исходный объект был обновлен путем изменений, внесенных в копию

Поэтому, когда вы распространяете свое состояние в локальное состояние, т.е.

 ...this.state.map

А затем используйте Object.assign в свойствах экземпляра, и вы непреднамеренно обновляете состояние Redux одновременно.

1 голос
/ 29 февраля 2020

Вы абсолютно правы по поводу root причины проблемы - Object.assign() изменял исходные элементы массива, на которые вы ссылались в map().

Чтобы устранить эту проблему, просто избавьтесь из Object.assign() изменяет ваше состояние:

 this.setState({
    ...this.state.map,
    areas: this.state.map.areas.map(el => ({
       ...el,
       chooseDevice: false,
       ...(el._id === area.id && {editModal: true})
    }))
 });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...