Equivalent to componentDidUpdate using React hooks

前端 未结 4 1364
暗喜
暗喜 2020-12-13 06:10

tldr; How do I simulate componentDidUpdate or otherwise use the key prop with an array to force my component to be reset?

相关标签:
4条回答
  • 2020-12-13 06:49

    use a custom hook

    export const useComponentDidUpdate = (effect, dependencies) => {
      const hasMounted = useRef(false);
    
      useEffect(
        () => {
          if (!hasMounted.current) {
            hasMounted.current = true;
            return;
          }
          effect();
        }, 
        dependencies
      );
    };
    
    

    Effect will not run after the initial render. Thereafter, it depends on the array of values that should be observed. If it's empty, it will run after every render. Otherwise, it will run when one of it's values has changed.

    0 讨论(0)
  • 2020-12-13 06:54

    In short, you want to reset your timer when the reference of the array changes, right ? If so, you will need to use some diffing mechanism, a pure hooks based solution would take advantage of the second parameter of useEffect, like so:

    function RefresherTimer(props) {
      const [startedAt, setStartedAt] = useState(new Date());
      const [timeRemaining, setTimeRemaining] = useState(getTimeRemaining(startedAt, props.delay));
    
      //reset part, lets just set startedAt to now
      useEffect(() => setStartedAt(new Date()),
        //important part
        [props.listOfObjects] // <= means: run this effect only if any variable
        // in that array is different from the last run
      )
    
      useEffect(() => {
        // everything with intervals, and the render
      })
    }
    

    More information about this behaviour here https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects

    0 讨论(0)
  • 2020-12-13 06:55

    A way to remount a component is to provide new key property. It's not necessarily a string but it will be coerced to a string internally, so if listOfObjects is a string, it's expected that key is compared internally with listOfObjects.toString().

    Any random key can be used, e.g. uuid or Math.random(). Shallow comparison of listOfObjects can be performed in parent component to provide new key. useMemo hook can be used in parent state to conditionally update remount key, and listOfObjects can be used as a list of parameters that need to be memoized. Here's an example:

      const remountKey = useMemo(() => Math.random(), listOfObjects);
    
      return (
        <div>
          <RefresherTimer delay={3000} callback={() => console.log('refreshed')} key={remountKey} />
        </div>
      );
    

    As an alternative to remount key, child component could be able to reset own state and expose a callback to trigger a reset.

    Doing shallow comparison of listOfObjects inside child component would be an antipattern because this requires it to be aware of parent component implementation.

    0 讨论(0)
  • 2020-12-13 06:56

    The useRef creates an "instance variable" in functional component. It acts as a flag to indicate whether it is in mount or update phase without updating state.

    const mounted = useRef();
    useEffect(() => {
      if (!mounted.current) {
        // do componentDidMount logic
        mounted.current = true;
      } else {
        // do componentDidUpdate logic
      }
    });
    
    0 讨论(0)
提交回复
热议问题