¿En qué casos ignorar el operador de propagación en useReducer causaría errores?

Nov 25 2020

En la mayoría de los useReducerejemplos que vi, el operador de propagación se ha utilizado para preservar estados. Sin embargo, en todas mis prácticas, ignorarlo nunca ha causado ningún problema. Parece que el reductor puede preservar el estado en sí mismo sin el operador de propagación. Mira el siguiente ejemplo:

const initialState = {
    color: 'black',
    bgColor: 'white'
}

const reducer = (state, action) => {
    switch (action.type) {
        case 'dawn':
            return {
                ...state,
                color: 'white',
                bgColor: 'purple'
            }
        case 'reset':
            return initialState
        default:
            return {
                state
            }
    }
}

const UseReducerColorSetter = () => {
    const [state, dispatch] = useReducer(reducer, initialState);
    const { color, bgColor } = state;
    return (
        <>
            <div style={{ color: `${color}`, backgroundColor: `${bgColor}` }} className='card'>
                Hello
            </div>
            <button onClick={() => dispatch({ type: 'dawn' })}>Dawn mode</button>
            <button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
        </>
    )
}

En este ejemplo, la eliminación ...stateno causa ningún problema, ningún cambio de estado, ningún error de consola, etc.

En esta pregunta pregunté: ¿ es necesario usar el operador de propagación en useReducer? Ignorar el operador de propagación causó problemas en useState, pero aún no hay problemas con useReducer.

¿Alguien puede proporcionarme algunos ejemplos de cómo ignorar el operador de propagación que causa problemas en useReducer? ¿Puede un reductor conservar el estado?

Respuestas

1 BrianThompson Nov 24 2020 at 23:53

A continuación se muestra un ejemplo en ejecución que muestra que si no reemplaza todas las propiedades del objeto en un reductor, se perderán. Compare los valores iniciales registrados por la consola con el resultado impreso.

const initialState = {
  color: "black",
  bgColor: "white"
};

const reducer = (state, action) => {
  switch (action.type) {
    case "dawn":
      return {
        bgColor: "purple" // Updated to only return one property to illustrate the problem
      };
    case "reset":
      return initialState;
    default:
      return {
        state
      };
  }
};

function App() {
  const [state, dispatch] = React.useReducer(reducer, initialState);

  React.useEffect(() => {
    console.log("Initial: ", state)
    dispatch({ type: "dawn" });
  }, []);

  return <div>{JSON.stringify(state)}</div>;
}

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Observe la firma de la función reductora (state, action) => newState . Esta función devuelve el nuevo estado. Entonces, lo que sea que returnobtenga del reductor es el nuevo valor estatal en su totalidad. Esto significa que sin difundir el objeto de estado raíz, perderá los valores que no se establezcan explícitamente en el nuevo objeto.

Para responder a su pregunta directamente, no difundir el estado anterior en las actualizaciones del reductor causará errores cuando su estado sea una matriz u objeto, y no reemplace todos los valores en esa estructura manualmente.

Si siempre establece cada propiedad de objeto o devuelve el estado anterior, nunca encontrará un error. Este es el escenario que ha dado en la pregunta y es por eso que parece innecesario.

SergioVargas Nov 24 2020 at 23:28

En su caso específico, no afectaría ni crearía problemas porque está cambiando los valores del estado (color y bgColor), si tuviera un caso que solo cambia de color, por ejemplo, ignorar el operador de propagación dejaría el estado sin un valor bgColor, esto puede causar problemas si su aplicación espera tener un bgColor en todo momento.

 case 'blue':
            return {
                color: 'blue',
            }