J'ai parcouru plusieurs questions sur SO concernant les accessoires par défaut pour les composants fonctionnels et ils recommandent tous d'utiliser les paramètres par défaut ES6. Voici des liens vers ces questions.
Cependant, lorsque j'utilise cette méthode pour écrire des composants avec des effets s'exécutant sur le changement d'accessoires, j'obtiens un comportement indésirable avec les non-primitives. Par exemple, le code suivant entraînera une boucle infinie.
const Parent = () => {
let somethingUndefined;
return (
<div>
<Child prop={somethingUndefined} />
</div>
);
};
const Child = ({ prop = {a: 1} }) => {
const [x, setX] = React.useState(1);
React.useEffect(() => {
setX(x + 1);
}, [prop]);
return <div>{x}, {prop.a}</div>;
};
ReactDOM.render(<Parent />, document.getElementsByTagName('body')[0]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.11.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script>
J'ai essayé deux façons d'essayer de contourner le problème. Tout d'abord, en affectant simplement une variable différente qui contient la valeur par défaut, et en plaçant le prop non modifié dans le tableau de dépendances. c'est à dire
const Child = ({ prop }) => {
const [x, setX] = React.useState(1);
const defaultedProp = prop || {a: 1};
React.useEffect(() => {
setX(x + 1);
}, [prop]);
// Note we use prop and not defaultedProp here to avoid runnning into the issue above.
return <div>{x}, {defaultedProp.a}</div>;
};
Une autre méthode consisterait simplement à utiliser quelque chose comme (prop || {a:1})
à la place de prop
partout où vous l'utilisez, sauf dans le tableau de dépendances. c'est à dire
const Child = ({ prop }) => {
const [x, setX] = React.useState(1);
React.useEffect(() => {
setX(x + 1);
}, [prop]);
return <div>{x}, {(prop || {a: 1}).a}</div>;
};
Mais ces deux solutions semblent sous-optimales car cela demanderait beaucoup d'efforts inutiles (et un code volumineux).
defaultProps
est également une solution au problème de la boucle infinie mais c'est obsolète . Notez que l'exemple fourni dans ce rfc utilise également les paramètres par défaut ES6 dans le code.
Est-ce que je manque quelque chose? Existe-t-il un meilleur moyen d'utiliser les accessoires par défaut dans les composants fonctionnels avec état qui exécutent des effets sur le changement d'accessoires?
Je ne sais pas si cela est éligible pour une réponse, mais toutes vos préoccupations pourraient être résolues en déclarant votre valeur par défaut en tant que constante dans l'application. Cela signifie;
const Parent = () => {
const somethingUndefined;
return (
<>
<Child prop={somethingUndefined} />
</>
);
};
const Child = ({ prop = {a: 1} }) => {
const [x, setX] = React.useState(1);
React.useEffect(() => {
setX(x + 1);
}, [prop]);
return <div>{x}, {prop.a}</div>;
};
Vous pouvez changer le code ci-dessus en
const Parent = () => {
const somethingUndefined;
return (
<>
<Child prop={somethingUndefined} />
</>
);
};
const defaultPropValue = {a: 1};
const Child = ({ prop = defaultPropValue }) => {
const [x, setX] = React.useState(1);
React.useEffect(() => {
setX(x + 1);
}, [prop]);
return <div>{x}, {prop.a}</div>;
};
Cela ne provoquera pas de boucles infinies.
La différence parie ces deux: - Dans le premier, le prop
est initialisé à une nouvelle valeur c'est-à-dire, {a: 1}
et à chaque mise à jour d'état, ce sera un nouvel objet (le nouvel objet sera dans un nouvel emplacement mémoire), et il invoquera à nouveau le rappel.
Dans le second, nous avons initialisé et assigné le {a: 1}
à defaultPropValue
qui ne changera pas. Ensuite, nous avons assigné ce defaultPropValue
à prop
afin qu'à chaque re-rendu, la valeur assignée au prop
soit la même (ou à partir du même emplacement mémoire). Cela fonctionne donc comme prévu.
J'espère que l'idée est claire!
La useEffect()
s'exécutera la première fois et invoquera la setX()
puis:
setX()
mettra à jour l'état de x
, ce qui déclenchera le nouveau rendu du composant.prop
recevra un nouvel objet const Child = ({ prop = {a: 1} }) => {
useEffect()
s'exécutera à nouveau et invoquera la setX()
tout le processus se répète à nouveau, cela provoque une boucle infinie.
Au lieu de cela, vous pouvez passer une valeur par défaut à la propriété a
et l'utiliser dans le tableau de dépendances useEffect()
const Parent = () => {
let somethingUndefined; // babel complains if we use `const` without value
return (
<div>
<Child prop={somethingUndefined} />
<Child prop={{ a: 3 }} />
</div>
);
};
const Child = ({ prop = {} }) => {
const { a = 1 } = prop;
const [x, setX] = React.useState(1);
React.useEffect(() => {
setX(x + 1);
}, [a]);
return <div>{x}, {a}</div>;
};
ReactDOM.render(<Parent />, document.getElementsByTagName('body')[0]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.11.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script>