Según los documentos:
componentDidUpdate()se invoca inmediatamente después de que se realiza la actualización. Este método no se llama para el renderizado inicial.
Podemos usar el nuevo useEffect()gancho para simular componentDidUpdate(), pero parece que useEffect()se ejecuta después de cada renderizado, incluso la primera vez. ¿Cómo hago para que no se ejecute en el renderizado inicial?
Como puede ver en el ejemplo siguiente, componentDidUpdateFunctionse imprime durante el renderizado inicial pero componentDidUpdateClassno se imprimió durante el renderizado inicial.
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>
                    
                        javascript
                                reactjs
                                react-hooks
                                
                    
                    
                        Yangshun Tay
fuente
                
                fuente

count?Respuestas:
Podemos usar el
useRefgancho para almacenar cualquier valor mutable que nos guste , por lo que podríamos usarlo para realizar un seguimiento de si es la primera vezuseEffectque se ejecuta la función.Si queremos que el efecto se ejecute en la misma fase que lo
componentDidUpdatehace, podemos usaruseLayoutEffecten su lugar.Ejemplo
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>fuente
useRefconuseState, pero utilizando la incubadora provocó una re-render, que no está sucediendo cuando se asigna afirstUpdate.currentasí que supongo que esta es la única manera agradable :)componentDidUpdatecon ganchos, por eso lo usé.Puede convertirlo en ganchos personalizados , así:
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;Ejemplo de uso:
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> ); } // ...fuente
Hice un
useFirstRendergancho simple para manejar casos como enfocar una entrada de formulario:import { useRef, useEffect } from 'react'; export function useFirstRender() { const firstRender = useRef(true); useEffect(() => { firstRender.current = false; }, []); return firstRender.current; }Comienza como
true, luego cambia afalseenuseEffect, que solo se ejecuta una vez y nunca más.En su componente, utilícelo:
const firstRender = useFirstRender(); const phoneNumberRef = useRef(null); useEffect(() => { if (firstRender || errors.phoneNumber) { phoneNumberRef.current.focus(); } }, [firstRender, errors.phoneNumber]);Para su caso, solo usaría
if (!firstRender) { ....fuente
@ravi, el tuyo no llama a la función de desmontaje pasada. Aquí tienes una versión un poco más completa:
/** * Identical to React.useEffect, except that it never runs on mount. This is * the equivalent of the componentDidUpdate lifecycle function. * * @param {function:function} effect - A useEffect effect. * @param {array} [dependencies] - useEffect dependency list. */ export const useEffectExceptOnMount = (effect, dependencies) => { const mounted = React.useRef(false); React.useEffect(() => { if (mounted.current) { const unmount = effect(); return () => unmount && unmount(); } else { mounted.current = true; } }, dependencies); // Reset on unmount for the next mount. React.useEffect(() => { return () => mounted.current = false; }, []); };fuente
useEffect(() => {...});dependenciesparámetro cuando lo llama.@MehdiDehghani, su solución funciona perfectamente bien, una adición que debe hacer es desmontar, restablecer el
didMount.currentvalor afalse. Cuando intente usar este gancho personalizado en otro lugar, no obtendrá el valor de la caché.import React, { useEffect, useRef } from 'react'; const useDidMountEffect = (func, deps) => { const didMount = useRef(false); useEffect(() => { let unmount; if (didMount.current) unmount = func(); else didMount.current = true; return () => { didMount.current = false; unmount && unmount(); } }, deps); } export default useDidMountEffect;fuente
false.