是否可以在 useReducer 中不分派地更改状态?

Is it possible to change state without dispatch in useReducer?

我发现 state.elements 在控制台中已更改,即使我还没有发送。 这是什么原因?

const initialState = { elements: [['apple','banana'],['rabbit','cat']] };

function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = React.useReducer(reducer, initialState);
    
  const changeList=()=>{
    const elementsArray = Array.from(state.elements);
    elementsArray[0][0]='Tiger'
  }

  return (
    <>
      Count: {state.elements}
      <button onClick={changeList}>Change List without dispatch</button>
    </>
  );
}

是的,在 useReducer 中可以在不分派的情况下更改状态。

就像 React 中的任何状态一样,它是可以改变的。 useReducer州也不例外。

const changeList=()=>{
  const elementsArray = Array.from(state.elements);
  elementsArray[0][0] = 'Tiger'; // <-- state mutation
}

数组通过引用复制,所以即使 elementsArraystate.elements数组的副本,数组元素仍然是对原始元素的引用仍在 state.elements。将 elementsArray[0][0] 设置为值 "Tiger" 与将 state.elements[0][0] 设置为 "Tiger" 具有相同的效果。

React 中的突变是一个巨大的禁忌,一种反模式。你尤其不要改变状态或道具。状态和道具将被视为不可变对象。这就是为什么状态更新总是需要创建 新对象引用

不可变更新示例:

在这里你浅拷贝了正在更新的每个级别的状态深度。浅层复制对象属性的 Spread 语法,以及 Array.prototype.map 将数组元素浅层复制到新数组中。

function reducer(state, action) {
  switch (action.type) {
    case "update_animal":
      const { index1, index2, value } = action.payload;
      return {
        ...state,
        elements: state.elements.map((outerEl, indexOuter) => 
          indexOuter === index1
            ? outerEl.map((innerEl, indexInner) => 
              indexInner === index2 ? value : innerEl
            )
            : outerEl
        )
      };

    ... other cases ...

    default:
      return state;
  }
}

...

dispatch({
  type: "update_animal",
  payload: {
    index1: 0,
    index2: 0,
    value: "Tiger",
  }
});