Em quais casos, ignorar o operador de propagação em useReducer causaria bugs?
Na maioria dos useReducer
exemplos 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 ...state
nã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
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 return
do 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.
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',
}