Dans quels cas ignorer l'opérateur de propagation dans useReducer provoquerait-il des bogues?
Dans la plupart des useReducer
exemples que j'ai vus, l'opérateur de diffusion a été utilisé pour préserver les états. Cependant, dans toutes mes pratiques, l'ignorer n'a jamais causé de problèmes. Il semble que le réducteur soit capable de préserver l'état lui-même sans l'opérateur d'étalement. Vérifiez l'exemple suivant:
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>
</>
)
}
Dans cet exemple, la suppression ...state
ne pose aucun problème, aucun changement d'état, aucune erreur de console, etc.
Dans cette question, j'ai demandé: est-il nécessaire d'utiliser l'opérateur de propagation dans useReducer , ignorer l'opérateur de propagation a causé des problèmes dans useState
, mais toujours pas de problèmes avec useReducer
.
Quelqu'un peut-il me donner des exemples d'ignorance de l'opérateur de diffusion causant des problèmes useReducer
? Un réducteur peut-il conserver son état?
Réponses
Voici un exemple en cours montrant que si vous ne remplacez pas toutes les propriétés d'objet dans un réducteur, elles seront perdues. Comparez les valeurs initiales enregistrées par la console avec le résultat imprimé.
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>
Notez la signature de la fonction réductrice (state, action) => newState
. Cette fonction renvoie le nouvel état. Donc, tout ce que vous return
du réducteur est la nouvelle valeur d'état dans son intégralité. Cela signifie que sans étendre l'objet d'état racine, vous perdrez toutes les valeurs non définies explicitement dans le nouvel objet.
Pour répondre directement à votre question, ne pas diffuser l'état précédent sur les mises à jour du réducteur entraînera des bogues lorsque votre état est un tableau ou un objet, et vous ne remplacez pas chaque valeur de cette structure manuellement.
Si vous définissez toujours chaque propriété d'objet ou renvoyez l'état précédent, vous ne rencontrerez jamais de bogue. C'est le scénario que vous avez donné dans la question et c'est pourquoi il semble inutile.
Dans votre cas spécifique, cela n'affecterait pas ou ne créerait pas de problèmes car vous modifiez les valeurs d'état (couleur et bgColor), si vous aviez un cas qui ne change que la couleur, par exemple, ignorer l'opérateur de propagation laisserait l'état sans valeur bgColor, cela peut causer des problèmes si votre application s'attend à avoir un bgColor à tout moment.
case 'blue':
return {
color: 'blue',
}