web-dev-qa-db-fra.com

Faire que le crochet React useEffect ne soit pas exécuté lors du rendu initial

Selon les docs:

componentDidUpdate() est appelé immédiatement après la mise à jour. Cette méthode n'est pas appelée pour le rendu initial.

Nous pouvons utiliser le nouveau useEffect() hook pour simuler componentDidUpdate(), mais il semble que useEffect() soit exécuté après chaque rendu, même la première fois. Comment puis-je l'obtenir pour ne pas s'exécuter sur le rendu initial?

Comme vous pouvez le voir dans l'exemple ci-dessous, componentDidUpdateFunction est imprimé lors du rendu initial mais componentDidUpdateClass n'a pas été imprimé lors du rendu initial.

function ComponentDidUpdateFunction() {
  const [count, setCount] = React.useState(0);
  React.useEffect(() => {
    console.log("componentDidUpdateFunction");
  });

  return (
    <div>
      <p>componentDidUpdateFunction: {count} times</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        Click Me
      </button>
    </div>
  );
}

class ComponentDidUpdateClass extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  }

  componentDidUpdate() {
    console.log("componentDidUpdateClass");
  }

  render() {
    return (
      <div>
        <p>componentDidUpdateClass: {this.state.count} times</p>
        <button
          onClick={() => {
            this.setState({ count: this.state.count + 1 });
          }}
        >
          Click Me
        </button>
      </div>
    );
  }
}

ReactDOM.render(
  <div>
    <ComponentDidUpdateFunction />
    <ComponentDidUpdateClass />
  </div>,
  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>
20
Yangshun Tay

Nous pouvons utiliser le hook useRef pour stocker toutes les valeurs mutables que nous aimons , afin de pouvoir le garder en mémoire si c'est la première fois que la fonction useEffect est en cours courir.

Si nous voulons que l'effet s'exécute dans la même phase que componentDidUpdate, nous pouvons utiliser useLayoutEffect .

Exemple

const { useState, useRef, useLayoutEffect } = React;

function ComponentDidUpdateFunction() {
  const [count, setCount] = useState(0);

  const firstUpdate = useRef(true);
  useLayoutEffect(() => {
    if (firstUpdate.current) {
      firstUpdate.current = false;
      return;
    }

    console.log("componentDidUpdateFunction");
  });

  return (
    <div>
      <p>componentDidUpdateFunction: {count} times</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        Click Me
      </button>
    </div>
  );
}

ReactDOM.render(
  <ComponentDidUpdateFunction />,
  document.getElementById("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>
37
Tholle

Vous pouvez le transformer en crochets personnalisés , comme suit:

import React, { useEffect, useRef } from 'react';

const useDidMountEffect = (func, deps) => {
    const didMount = useRef(false);

    useEffect(() => {
        if (didMount.current) func();
        else didMount.current = true;
    }, deps);
}

export default useDidMountEffect;

Exemple d'utilisation:

import React, { useState, useEffect } from 'react';

import useDidMountEffect from '../path/to/useDidMountEffect';

const MyComponent = (props) => {    
    const [state, setState] = useState({
        key: false
    });    

    useEffect(() => {
        // you know what is this, don't you?
    }, []);

    useDidMountEffect(() => {
        // react please run me if 'key' changes, but not on initial render
    }, [state.key]);    

    return (
        <div>
             ...
        </div>
    );
}
// ...
2
Mehdi Dehghani