web-dev-qa-db-fra.com

componentWillReceiveProps, componentDidUpdate pour React Hook

Je me heurte à deux défis:

  • Même si, conformément à la directive React, état dérivé est déconseillé, mais certains cas Edge en ont toujours besoin).
    En termes de composant fonctionnel avec React Hook, Quelle est l'implémentation équivalente avec React Hook, si j'ai besoin de état dérivé? qui dans le composant de classe, sera mis à jour dans componentWillReceiveProps sur chaque rendu parent

voir l'exemple de code ci-dessous:

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: props.count > 100 ? 100 : props.count,
    }

  }

  /*What is the equivalent implementation when React Hook is used here componentWillReceiveProps*/
  componentWillReceiveProps(nextProps) {
    if (nextProps.count !== this.props.count) {
      this.setState({
        count: nextProps.count > 100 ? 100 : nextProps.count
      });
    }
  }

  render() {
    return ( <
      div > {
        this.state.count
      } <
      /div>
    );
  }
}

export default App;
  • Quant à componentDidUpdate, componentDidUpdate a sa partie routeur lorsque React Hook est utilisé, vous devez l'utiliser comme,

    React.useEffect(() => {
      return () => {
    
       };
    }, [parentProp]);
    

    le deuxième paramètre pour useEffect s'assure que le code n'est exécuté que lorsque les accessoires changent, mais et si je veux faire des tâches respectives en fonction de plusieurs changements d'accessoires respectifs? comment faire avec useEffect?

voir l'exemple de code ci-dessous:

class App extends Component {


  /*What is the equivalent implementation when functional component with React Hook is used here */
  componentDidUpdate(prevProps, prevState) {
    if (prevProps.groupName !== this.props.groupName) {
      console.log('Let'
        's say, I do want to do some task here only when groupName differs');
    } else if (prevProps.companyName !== this.props.companyName) {
      console.log('Let'
        's say,I do want to do some different task here only when companyName differs');
    }

  }


  render() {
    /*for simplicity, render code is ignored*/
    return null;
  }
}

export default App;
18
Deng Zhebin

Le hook réactif équivalent aux anciens accessoires componentWillReceive peut être fait en utilisant le crochet useEffect, en spécifiant simplement l'accessoire que nous voulons écouter pour les changements dans le tableau de dépendances.

Quelque chose comme:

export default (props) => {


    useEffect( () => {
        console.log('counter updated');
    }, [props.counter])


    return <div>Hi {props.counter}</div>
}

Pour componentDidUpdate simplement en omettant le tableau de dépendances, la fonction useEffect sera appelée après chaque rendu.

6
fgonzalez

Vous pouvez utiliser le crochet useMemo pour stocker un calcul et mettre props.count dans le tableau donné comme deuxième argument pour recalculer la valeur lorsqu'elle change.

const { useState, useEffect, useMemo } = React;

function App() {
  const [count, setCount] = useState(50);

  useEffect(() => {
    setTimeout(() => {
      setCount(150);
    }, 2000);
  }, []);

  return <DisplayCount count={count} />;
}

function DisplayCount(props) {
  const count = useMemo(() => props.count > 100 ? 100 : props.count, [props.count]);

  return <div> {count} </div>;
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

<div id="root"></div>

La façon la plus simple de faire des effets séparés lorsque des accessoires séparés changent est de créer plusieurs crochets useEffect qui ne sont exécutés que lorsque l'un des accessoires séparés change.

const { useState, useEffect } = React;

function App() {
  const [groupName, setGroupName] = useState('foo');
  const [companyName, setCompanyName] = useState('foo');

  useEffect(() => {
    setTimeout(() => {
      setGroupName('bar');
    }, 1000);
    setTimeout(() => {
      setCompanyName('bar');
    }, 2000);
  }, []);

  return <DisplayGroupCompany groupName={groupName} companyName={companyName} />;
}

function DisplayGroupCompany(props) {
  useEffect(() => {
    console.log("Let's say, I do want to do some task here only when groupName differs");
  }, [props.groupName])
  useEffect(() => {
    console.log("Let's say,I do want to do some different task here only when companyName differs");
  }, [props.companyName])

  return <div> {props.groupName} - {props.companyName} </div>;
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

<div id="root"></div>
6
Tholle

setCount déclenchera un nouveau rendu, en utilisant useEffect et le tableau de dépendances [count] spécifiera que seule "surveiller" la mise à jour de count lorsque la valeur de count est différente. Donc, cela peut être en quelque sorte remplacé componentWillReceiveProps. Nous pouvons dire que " chaque rendu a ses propres accessoires et états ". Si vous souhaitez déclencher le rendu dépend du changement d'accessoires, vous pouvez alors utiliser plusieurs effets.

/*componentWillReceiveProps*/
  useEffect(() => {
       count > 100 ? setCount(100) : setCount(count)
    }, [count]) 

  useEffect(() => {
        console.log(`Let's say, I do want to do some task here only when groupName differs`);
    }, [groupName])
    useEffect(() => {
        console.log(`Let''s say,I do want to do some different task here only when companyName differs`);
    }, [companyName]) 
3
Thien Ly

Dans votre scénario, vous n'avez pas du tout besoin d'utiliser ou de réimplémenter getDerivedStateFromProps. Il vous suffit de créer une nouvelle variable pour obtenir la nouvelle forme de données. L'utilisation de state dans ce scénario provoquera simplement un nouveau rendu qui n'est pas bon en termes de performances.

import React from 'react';

const App = ({ count }) => {
  const derivedCount = count > 100 ? 100 : count;

  return (
    <div>Counter: {derivedCount}</div>
  );
}

App.propTypes = {
  count: PropTypes.number.isRequired
}

Démo ici: https://codesandbox.io/embed/qzn8y9y24j?fontsize=14

Vous pouvez en savoir plus sur les différentes façons de résoudre ce type de scénarios sans utiliser getDerivedStateFromProps ici: https://reactjs.org/blog/2018/06/07/you-probably-dont-need -derived-state.html

Si vous avez vraiment besoin d'utiliser un état séparé, vous pouvez utiliser quelque chose comme ça

import React, { useState } from 'react';

const App = ({ count }) => {
  const [derivedCounter, setDerivedCounter] = useState(
    count > 100 ? 100 : count
  );

  useEffect(() => {
    setDerivedCounter(count > 100 ? 100 : count);
  }, [count]); // this line will tell react only trigger if count was changed

  return <div>Counter: {derivedCounter}</div>;
};
2
Kevin F.

simplement en utilisant useEffect comme ceci.

useEffect( () => {
    props.actions.fetchSignlePost(props.match.params.id); > I'm dispatching an action here.
}, [props.comments]) > and here to watch comments and call the action in case there is any change.
1
Hasan Zahran

Je me rends compte que votre exemple "d'état dérivé" est intentionnellement simple, mais comme il y a si peu de cas légitimes d'état dérivé, il est difficile de faire une recommandation sur le remplacement, sauf au cas par cas, car cela dépend de la raison pour laquelle vous utilisent l'état dérivé. Dans l'exemple particulier que vous avez fourni, il n'y avait aucune raison d'utiliser l'état dérivé dans le cas de classe et il n'y a donc toujours aucune raison dans le cas du hook (la valeur peut simplement être dérivée localement sans la mettre dans l'état). Si la valeur dérivée est coûteuse, vous pouvez utiliser useMemo comme le présente Tholle. Si ceux-ci ne correspondent pas aux cas les plus réalistes que vous avez en tête, vous devrez présenter un cas plus spécifique qui nécessite vraiment un état dérivé.

En ce qui concerne votre exemple componentDidUpdate, si ce que vous voulez faire pour les différents accessoires est indépendant, vous pouvez utiliser des effets séparés pour chacun (c'est-à-dire plusieurs appels useEffect). Si vous voulez faire exactement ce qui est dans votre exemple (c'est-à-dire ne faire que quelque chose pour un companyName changer si groupName n'a pas également changé comme indiqué par votre else if), vous pouvez alors utiliser refs pour des conditions plus sophistiquées. Vous ne devez pas ne pas muter la référence pendant le rendu (il y a toujours la possibilité que le rendu soit rejeté/refait une fois que le mode simultané est pris en charge), donc l'exemple utilise le dernier effet pour mettre à jour les refs. Dans mon exemple, j'utilise une référence pour éviter de faire un travail d'effet sur le rendu initial (voir la réponse de Tholle dans cette question connexe ) et pour détecter si groupName a changé ou non pour décider si ou ne pas faire de travail basé sur un changement de companyName.

const { useState, useEffect, useRef } = React;

const DerivedStateFromProps = ({ count }) => {
  const derivedCount = count > 100 ? 100 : count;

  return (
    <div>
      Derived from {count}: {derivedCount}{" "}
    </div>
  );
};
const ComponentDidUpdate = ({ groupName, companyName }) => {
  const initialRender = useRef(true);
  const lastGroupName = useRef(groupName);
  useEffect(
    () => {
      if (!initialRender.current) {
        console.log("Do something when groupName changes", groupName);
      }
    },
    [groupName]
  );
  useEffect(
    () => {
      if (!initialRender.current) {
        console.log("Do something when companyName changes", companyName);
      }
    },
    [companyName]
  );
  useEffect(
    () => {
      if (!initialRender.current && groupName === lastGroupName.current)
        console.log(
          "Do something when companyName changes only if groupName didn't also change",
          companyName
        );
    },
    [companyName]
  );
  useEffect(
    () => {
      // This effect is last so that these refs can be read accurately in all the other effects.
      initialRender.current = false;
      lastGroupName.current = groupName;
    },
    [groupName]
  );

  return null;
};
function App() {
  const [count, setCount] = useState(98);
  const [groupName, setGroupName] = useState("initial groupName");
  const [companyName, setCompanyName] = useState("initial companyName");
  return (
    <div>
      <div>
        <DerivedStateFromProps count={count} />
        <button onClick={() => setCount(prevCount => prevCount + 1)}>
          Increment Count
        </button>
      </div>
      <div>
        <ComponentDidUpdate groupName={groupName} companyName={companyName} />
        groupName:{" "}
        <input
          type="text"
          value={groupName}
          onChange={event => setGroupName(event.target.value)}
        />
        <br />
        companyName:{" "}
        <input
          type="text"
          value={companyName}
          onChange={event => setCompanyName(event.target.value)}
        />
        <br />
        change both{" "}
        <input
          type="text"
          onChange={event => {
            const suffix = event.target.value;
            setGroupName(prev => prev + suffix);
            setCompanyName(prev => prev + suffix);
          }}
        />
      </div>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<div id="root"></div>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

Edit Derived state and componentDidUpdate

0
Ryan Cogswell