Disons que j'ai une table avec des données de tri et que je souhaite la stocker dans un état (ou même 3 états séparés). Supposons que cet état puisse être modifié par l'enfant. Est-il possible de le faire de toute façon sans avoir 3 useEffects , Je voudrais voir s'il est possible d'obtenir le même résultat que ci-dessous avec un seul effet d'utilisation ?
import React, { useState, useEffect } from "react"; function Table({ initialSortDirection, initialSortParam, initialSortEnabled }) { const [currentSortData, setSortData] = useState({ sortDirection: initialSortDirection, sortParam: initialSortParam, hasSort: initialSortEnabled }); useEffect(() => { setSortData({ ...currentSortData, sortDirection: initialSortDirection }); }, [initialSortDirection]); useEffect(() => { setSortData({ ...currentSortData, sortParam: initialSortParam }); }, [initialSortParam]); useEffect(() => { setSortData({ ...currentSortData, hasSort: initialSortEnabled }); }, [initialSortEnabled]); return (<SomeComponent onChangeSort={setSortData} />) }
À l'ancienne, j'utiliserais probablement componentWillReceiveProps et comparez simplement nextProps pour voir si elles ont changé mais maintenant je rencontre des difficultés trouver un moyen concis de le faire "à la fois" et uniquement en cas de changement.
À titre d'exemple visuel, considérez l'image ci-dessous, vous pouvez changer le tri en cliquant sur la cellule ou en changeant les "boutons" .
EDIT 1
Supposons que d'autres choses pourraient affecter l'état et je ne veux pas remplacer un état mis à jour avec un accessoire initial inchangé. J'ai mis à jour le code en conséquence
MODIFIER 2 Ajout d'une image de livre de contes
3 Réponses :
Vous pouvez avoir un useEffect ()
qui écoute quelques changements d'états:
useEffect(() => { setSortData({ ...currentSortData, sortDirection: initialSortDirection, sortParam: initialSortParam, hasSort: initialSortEnabled }); }, [initialSortDirection, initialSortParam, initialSortEnabled]);
Ok, mon exemple n'était pas clair, mon problème avec celui-ci était que si j'avais d'autres choses qui modifient l'état, elles seraient écrasées. Mettra à jour ma question et votera pour votre réponse
Pourquoi modifieraient-ils l'état si vous utilisez ... currentSortData?
Disons que j'ai une cellule d'en-tête qui, lorsqu'elle est cliquée, doit être triée. Ensuite, la cellule d'en-tête onClick appellerait onChangeSort ({... someNewParam}) et que les valeurs initiales proviennent d'un autre composant. J'ai ajouté une photo avec cet exemple
Donc, si vous ajoutez les paramètres à la liste dans mon exemple, cela ne remplacera pas l'état des autres clés. La raison en est que lorsque vous utilisez l'opérateur {...} (spread), il prendra les états actuels et ne remplacera que ceux modifiés.
Oui, le problème était que je n'avais pas précisé que c'était le $ SOMETHING initial inchangé qui écraserait si un seul des $ SOMETHING initial changeait
Est-ce le comportement que vous recherchez?
Voici comment je le ferais avec un seul useEffect()
.
Je conserverais le props
dernières valeurs (du rendu précédent) dans un useRef
et vérifieraient les différences sur chaque propriété et décideraient si je devais ou non mettre à jour l ' état
. Après cela, je mets à jour les valeurs ref
vers les accessoires
actuels pour être comparés aux futurs accessoires
lors du prochain rendu et ainsi de suite. p>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script> <div id="root"></div>
function App() { const [initialState, setInitialState] = React.useState({ initialProp1: 'A', initialProp2: 'A' }); return( <Table {...initialState} setInitialState={setInitialState} /> ); } function Table({initialProp1, initialProp2, setInitialState}) { const [myState, setMyState] = React.useState({ prop1: initialProp1, prop2: initialProp2 }); const lastProps = React.useRef({ initialProp1, initialProp2 }); React.useEffect(()=>{ if (lastProps.current.initialProp1 !== initialProp1) { console.log('1 changed'); setMyState((prevState)=>{ return({ ...prevState, prop1: initialProp1 }); }); } if (lastProps.current.initialProp2 !== initialProp2) { console.log('2 changed'); setMyState((prevState)=>{ return({ ...prevState, prop2: initialProp2 }); }); } lastProps.current = { initialProp1, initialProp2 } }); function changeState() { setMyState((prevState) => { return({ ...prevState, prop2: 'B' }); }); } function changeProps() { setInitialState({ initialProp1: 'A', initialProp2: 'C' }); } return( <React.Fragment> <div>This is Table <b>props</b> initialProp1: {initialProp1}</div> <div>This is Table <b>props</b> initialProp2: {initialProp2}</div> <div>This is Table <b>state</b> prop1: {myState.prop1}</div> <div>This is Table <b>state</b> prop2: {myState.prop2}</div> <button onClick={changeState}>Change Table state</button> <button onClick={changeProps}>Change props that comes from parent</button> </React.Fragment> ); } ReactDOM.render(<App/>, document.getElementById('root'));
Ouais cela fonctionne, le problème est qu'il reste encore 2 useEffects. Mon objectif principal serait d'en avoir un seul qui pourrait mettre à jour l'état uniquement pour les initiales modifiées
Pourquoi votre initialState
changerait-il après le rendu du composant? De plus, une fois que l'état initial a changé, voulez-vous qu'il écrase un état qui a été défini via un clic d'en-tête de colonne, par exemple? Lequel a la priorité la plus élevée?
Parce qu'il peut être contrôlé sur le monde extérieur. La priorité la plus élevée est le initialprops s'il change l'état doit être écrasé et s'il n'y a pas de nouveau initialProps l'état changerait en interne. Ce n'était qu'un exemple pour la question, existe-t-il un formulaire pour savoir quelle dépendance a changé sur useEffect?
Prenons l'exemple du livre d'histoires. Si je change sur les potards ci-dessous il devrait se mettre à jour en perdant l'état, si je ne fais rien c'est l'état qui resterait
@FabioCosta J'ai mis à jour ma réponse. Voyez si cela fonctionne pour vous.
Exactement! Je n'ai pas pensé à utiliser une référence pour conserver les anciens accessoires. Merci!
Heureux d'avoir pu aider. Je suis brésilienne aussi, vivant au Portugal. Étudier pour devenir développeur Web et lancer mes propres projets (sites Web, applications Web). Bon codage à vous!
Je suis tombé sur cela alors que j'étais confronté au problème similaire , mais je n'étais pas satisfait de la réponse de cbdevelopers pour son utilisation de useRef
car j'avais l'impression que vous ne devriez pas avoir besoin ce. Un gars sympathique de reactflux a souligné une solution plus élégante:
const Table = (props) => { const { initialSortDirection, initialSortParam, initialSortEnabled } = props; const [currentSortData, setSortData] = useState({ sortDirection: initialSortDirection, sortParam: initialSortParam, hasSort: initialSortEnabled }); useEffect(() => { setSortData((prevSortData) => ({ ...prevSortData, sortDirection: initialSortDirection })); }, [initialSortDirection]); useEffect((prevSortData) => { setSortData(() => ({ ...prevSortData, sortParam: initialSortParam }); }, [initialSortParam]); useEffect(() => { setSortData((prevSortData) => ({ ...prevSortData, hasSort: initialSortEnabled })); }, [initialSortEnabled]); return (<SomeComponent onChangeSort={setSortData} />) }
Je sais que vous voulez tout fusionner en un seul, mais je ne recommanderais pas cela. Vous souhaitez séparer les préoccupations , en produisant toujours l'effet correct lors de la mise à jour des accessoires.
https : //reactjs.org/docs/hooks-effect.html#tip-use-multiple-effects-to-separate-concerns
Sachez que la solution OP est fausse, comme si deux les accessoires se mettent à jour en même temps, seul le dernier changement d'état persiste lors de l'utilisation de plusieurs effets.
J'espère que cela aidera quelqu'un.
Je suppose que vos accessoires (
initialSortDirection
,initialSortParam
,initialSortEnabled
) ont toujours une valeur?Oui, supposons qu'ils seraient correctement initialisés ou qu'ils auraient une valeur par défaut
Craignez-vous que si vous cliquez sur une cellule d'en-tête pour trier, ce tri écrasera votre état initial qui est venu à travers les accessoires?
@ cbdev420 non, c'est le comportement souhaité. Le problème est qu'un état modifié serait écrasé par un OLD initialState si un seul des changements initialState. Un bon exemple serait que si je désactive le tri en modifiant le initialSortEnabled, je pourrais perdre le sortDirection et sortParam actuels.
Pourquoi perdriez-vous les autres paramètres si vous étalez les autres propriétés:
setSortData ({... currentSortData, hasSort: initialSortEnabled});
. Vous ne changeriez que la propriétéhasSort
. En passant, dans ce cas, je recommanderais la forme fonctionnelle desetState ()
pour travailler avec un état qui dépend de l'état précédent:setSortData ((prevState) => {return ({ ... prevState, hasSort: initialSortEnabled});}
Vous perdriez si vous n'aviez qu'un seul useEffect car il répondrait à tous les paramètres mais vous ne sauriez pas lequel a changé. Je pense que cela se résume à "Avec un seul useEffect est-il possible de détecter avec le déclencheur changé?"