Encontré que state.elements se cambió en la consola, incluso si aún no los envío. ¿Cuál es la razón?
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> </> ); }
Sí, es posible cambiar de estado sin envío en useReducer.
Como cualquier estado en React, se puede mutar. El estado useReducer
no es una excepción.
const changeList=()=>{ const elementsArray = Array.from(state.elements); elementsArray[0][0] = 'Tiger'; // <-- state mutation }
Copia de la matriz por referencia, por lo que aunque elementsArray
es una copia de la matriz state.elements
, los elementos de la matriz siguen siendo referencias a los elementos originales que aún están en state.elements
. Establecer elementsArray[0][0]
en el valor "Tiger"
tiene el mismo efecto que state.elements[0][0]
se establece en "Tiger"
.
Las mutaciones en React son un gran no-no, un antipatrón. Especialmente no mutas el estado o los accesorios. El estado y los accesorios deben tratarse como objetos inmutables. Esta es la razón por la que las actualizaciones de estado siempre requieren la creación de nuevas referencias de objetos .
Un ejemplo de actualización inmutable:
Aquí copia superficialmente cada nivel de profundidad de estado que se está actualizando. La sintaxis Spread para la copia superficial de propiedades de objeto y Array.prototype.map
para copiar elementos de matriz en una nueva matriz.
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", } });