web-dev-qa-db-fra.com

Plusieurs appels au composant de mise à jour d'état à partir de useState dans le composant entraînent plusieurs restitutions.

J'essaie React pour la première fois et tout semblait aller jusqu'à ce que je réalise que lorsque je récupère des données et que je mets à jour deux variables d'état différentes (indicateur de chargement et de données), mon composant (une table de données) est rendu deux fois, même si les deux appels à la mise à jour d'état se passent dans la même fonction. Voici ma fonction api qui renvoie les deux variables à mon composant.

const getData = url => {

    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);

    useEffect(async () => {

        const test = await api.get('/people')

        if(test.ok){
            setLoading(false);
            setData(test.data.results);
        }

    }, []);

    return { data, loading };
};

Dans un composant de classe normal, vous ne feriez qu'un seul appel pour mettre à jour l'état qui peut être un objet complexe, mais la "méthode du crochet" semble être de scinder l'état en unités plus petites, dont un des effets secondaires semble être multiple. rend quand ils sont mis à jour séparément. Des idées comment atténuer cela?

2
jonhobbs

Vous pouvez combiner les états loading et data en un seul objet d’état. Vous pouvez ensuite effectuer un appel setState et il n’y aura qu’un rendu. 

Remarque: Contrairement à setState dans les composants de classe, la setState renvoyée de useState ne fusionne pas les objets avec l'état existant, elle remplace entièrement l'objet. Si vous souhaitez effectuer une fusion, vous devez lire l'état précédent et le fusionner vous-même avec les nouvelles valeurs. Reportez-vous à la docs .

Je ne m'inquiéterais pas trop des appels restants jusqu'à ce que vous ayez déterminé que vous aviez un problème de performances. Le rendu (dans le contexte React) et la validation des mises à jour du DOM virtuel sur le DOM réel sont deux choses différentes. Le rendu fait référence à la génération de DOM virtuels et non à la mise à jour du DOM du navigateur. React peut regrouper les appels setState et mettre à jour le DOM du navigateur avec le nouvel état final.

const {useState, useEffect} = React;

function App() {
  const [userRequest, setUserRequest] = useState({
    loading: false,
    user: null,
  });

  useEffect(() => {
    // Note that this replaces the entire object and deletes user key!
    setUserRequest({ loading: true });
    fetch('https://randomuser.me/api/')
      .then(results => results.json())
      .then(data => {
        setUserRequest({
          loading: false,
          user: data.results[0],
        });
      });
  }, []);

  const { loading, user } = userRequest;

  return (
    <div>
      {loading && 'Loading...'}
      {user && user.name.first}
    </div>
  );
}

ReactDOM.render(<App />, document.querySelector('#app'));
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<div id="app"></div>

Alternative - écrivez votre propre crochet de fusion d'État

const {useState, useEffect} = React;

function useMergeState(initialState) {
  const [state, setState] = useState(initialState);
  const setMergedState = newState => 
    setState(prevState => Object.assign({}, prevState, newState)
  );
  return [state, setMergedState];
}

function App() {
  const [userRequest, setUserRequest] = useMergeState({
    loading: false,
    user: null,
  });

  useEffect(() => {
    setUserRequest({ loading: true });
    fetch('https://randomuser.me/api/')
      .then(results => results.json())
      .then(data => {
        setUserRequest({
          loading: false,
          user: data.results[0],
        });
      });
  }, []);

  const { loading, user } = userRequest;

  return (
    <div>
      {loading && 'Loading...'}
      {user && user.name.first}
    </div>
  );
}

ReactDOM.render(<App />, document.querySelector('#app'));
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<div id="app"></div>

7
Yangshun Tay

Mise à jour par lots dans react-hookshttps://github.com/facebook/react/issues/14259

React gère actuellement les mises à jour d'état par lots si elles sont déclenchées depuis un événement basé sur React, comme un clic de bouton ou une modification d'entrée. Les mises à jour en lots ne seront pas groupées si elles sont déclenchées en dehors d'un gestionnaire d'événements React, comme un appel asynchrone.

0
Sureshraj