Проблема с обновлением состояния с помощью useReducer

#reactjs #state

#reactjs #состояние

Вопрос:

Использую React useReducer и пытаюсь выполнить обновление состояния и повторно отобразить react по мере внесения изменений.

JSX вызывает редуктор:

 <button onClick={() => dispatch({ type: "DECREMENT", item })} > </button>
  

и useReducer:

 const reducer = (state, action) => {
  const { item, type } = action;
  switch (type) {
    case "INCREMENT": {
      const newCart = state;
      const u = { ...item, quantity: item.quantity   1 };
      const index = newCart.findIndex(eachitem => item.id === eachitem.id);
      newCart.splice(index, 1, u);
      console.log("INCREMENT", newCart);
      return newCart;
    }
    case "DECREMENT": {
      const newCart = state;
      const u = { ...item, quantity: item.quantity - 1 };
      const index = newCart.findIndex(eachitem => item.id === eachitem.id);
      newCart.splice(index, 1, u);
      console.log("DECREMENT", newCart);
      return newCart;
    }
    case "REMOVE": {
      return state.filter(eachitem => item.id !== eachitem.id);
    }
    default: {
      return state;
    }
  }
};
  

Полный код можно найти ниже.

Редактировать codesandboxer-пример

Ответ №1:

Ваш редуктор не может обнаружить изменение состояния, поскольку вы возвращаете тот же объект. Изменение обнаружено путем сравнения предыдущего значения состояния и нового, путем === сравнения или Object.is() comparison, что в принципе одно и то же (не уверен, какое именно, можете попробовать найти в React docs).

Поэтому вместо:

 const newCart = state;
  

Вам нужно сделать:

 // shallowly copy the state so now Object.is(newState, prevState) returns false
const newCart = [...state];
...
return newCart
  

Комментарии:

1. Это выглядит лучше, чем возврат нового объекта 🙂👍

2. Имеет смысл. Я создал NewCart, чтобы избежать мутирования, но поскольку это объект, он указывает на тот же объект (и я его мутировал). Распространяя состояние на новый объект, оно создает неглубокую копию и фактически ссылается на новый объект. Спасибо

Ответ №2:

Вы устанавливаете newCart в качестве состояния, которое указывает на то же самое. Либо вы клонируете состояние, либо возвращаете новый экземпляр объекта.

 switch (type) {
    case "INCREMENT": {
      const newCart = state;
      const u = { ...item, quantity: item.quantity   1 };
      const index = newCart.findIndex(eachitem => item.id === eachitem.id);
      newCart.splice(index, 1, u);
      console.log("INCREMENT", newCart);

      return [...newCart];
    }
    case "DECREMENT": {
      const newCart = state;
      const u = { ...item, quantity: item.quantity - 1 };
      const index = newCart.findIndex(eachitem => item.id === eachitem.id);
      newCart.splice(index, 1, u);
      console.log("DECREMENT", newCart);
      return [...newCart];
    }
    case "REMOVE": {
      return state.filter(eachitem => item.id !== eachitem.id);
    }
    default: {
      return state;
    }
  }
};
  

Обновлена изолированная среда
Редактировать codesandboxer-пример