Em quais casos, ignorar o operador de propagação em useReducer causaria bugs?

Nov 25 2020

Na maioria dos useReducerexemplos que vi, o operador de propagação foi usado para preservar estados. No entanto, em todas as minhas práticas, ignorá-lo nunca causou problemas. Parece que o redutor é capaz de preservar o próprio estado sem o operador de propagação. Verifique o seguinte exemplo:

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>
        </>
    )
}

Neste exemplo, a remoção ...statenão causa problemas, nenhuma mudança de estado, nenhum erro de console, etc.

Nesta pergunta eu perguntei: é necessário usar o operador de spread em useReducer , ignorar o operador de spread causou problemas em useState, mas ainda não há problemas com useReducer.

Alguém pode me fornecer alguns exemplos de como ignorar o operador de propagação que está causando problemas no useReducer? Um redutor pode preservar o estado?

Respostas

1 BrianThompson Nov 24 2020 at 23:53

Aqui está um exemplo de execução que mostra que, se você não substituir todas as propriedades do objeto em um redutor, elas serão perdidas. Compare os valores iniciais registrados do console com o resultado impresso.

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 a assinatura da função redutora (state, action) => newState . Esta função retorna o novo estado. Portanto, o que quer que seja returndo redutor é o novo valor de estado em sua totalidade. Isso significa que, sem espalhar o objeto de estado raiz, você perderá todos os valores não definidos explicitamente no novo objeto.

Para responder sua pergunta diretamente, não espalhar o estado anterior nas atualizações do redutor causará bugs quando seu estado for um array ou objeto, e você não substitui cada valor nessa estrutura manualmente.

Se você sempre definir cada propriedade do objeto ou retornar ao estado anterior, nunca encontrará um bug. Este é o cenário que você deu na pergunta e é por isso que parece desnecessário.

SergioVargas Nov 24 2020 at 23:28

No seu caso específico, isso não afetaria ou criaria problemas porque você está alterando os valores de estado (cor e bgColor), se você tivesse um caso que apenas muda de cor, por exemplo, ignorar o operador de propagação deixaria o estado sem um valor de bgColor, este pode causar problemas se seu aplicativo espera ter um bgColor o tempo todo.

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