componentWillReceiveProps, componentDidUpdate for React Hook

后端 未结 8 1139
闹比i
闹比i 2020-12-25 11:00

I run into two challenges:

  • Even if, as per React guideline, derived state is discouraged, but some edge cases still need it.
    In terms of
相关标签:
8条回答
  • 2020-12-25 11:11

    1.) What is the equivalent implementation with React Hook, If I do need derived state?

    Derived state for Hooks = set state conditionally and directly in the render phase:

    constComp = (props) => {
      const [derivedState, setDerivedState] = useState(42);
      if (someCondition) {
        setDerivedState(...);
      }
      // ...
    }
    

    This updates state without an additional commit phase as opposed to useEffect. Above pattern is supported by React Strict Mode (no warnings):

    const App = () => {
      const [row, setRow] = React.useState(1);
    
      React.useEffect(() => {
        setTimeout(() => {
          setRow(2);
        }, 3000);
      }, []);
    
      return (
        <React.StrictMode>
          <Comp row={row} />
        </React.StrictMode>
      );
    }
    
    const Comp = ({ row }) => {
      const [isScrollingDown, setIsScrollingDown] = React.useState(false);
      const [prevRow, setPrevRow] = React.useState(null);
    
      console.log("render, prevRow:", prevRow)
    
      if (row !== prevRow) {
        console.log("derive state");
        // Row changed since last render. Update isScrollingDown.
        setIsScrollingDown(prevRow !== null && row > prevRow);
        setPrevRow(row);
      }
    
      return `Scrolling down: ${isScrollingDown}`;
    };
    
    ReactDOM.render(<App />, document.getElementById("root"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.development.js" integrity="sha256-4gJGEx/zXAxofkLPGXiU2IJHqSOmYV33Ru0zw0TeJ30=" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.development.min.js" integrity="sha256-9xBa2Hcuh2S3iew36CzJawq7T9iviOAcAVz3bw8h3Lo=" crossorigin="anonymous"></script>
    <div id="root"></div>

    Note 1: componentWillReceiveProps has been deprecated for quite some time. getDerivedStateFromProps is the class components' successor in terms of derived state.

    Note 2: Check preferred solutions before you resort to derived state.


    2.) What if I want to do respective tasks based on multiple respective props changes?

    You can either leave useEffect deps out completely or preferably add another prop dep:

    React.useEffect(() => {
      return () => { };
    }, [parentProp, secondProp]);
    
    0 讨论(0)
  • 2020-12-25 11:14

    In your scenario, you don't need to use or re-implement getDerivedStateFromProps at all. You just need to create a new variable to get the new form of data. Using state in this scenario will just cause another re-rendering which is not good performance wise.

    import React from 'react';
    
    const App = ({ count }) => {
      const derivedCount = count > 100 ? 100 : count;
    
      return (
        <div>Counter: {derivedCount}</div>
      );
    }
    
    App.propTypes = {
      count: PropTypes.number.isRequired
    }
    

    Demo here: https://codesandbox.io/embed/qzn8y9y24j?fontsize=14

    You can read more on different ways to solve these kind of scenarios without using getDerivedStateFromProps here: https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html

    If you really need to use a separate state, you can use something like this

    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>;
    };
    
    0 讨论(0)
  • 2020-12-25 11:17

    You can use the useMemo hook to store a calculation and put props.count in the array given as second argument to recalculate the value when it changes.

    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>

    The easiest way to do separate effects when separate props change is to create multiple useEffect hooks that are only run when one of the separate props 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>

    0 讨论(0)
  • 2020-12-25 11:19

    setCount will trigger a re-render. Using useEffect with [count] as the dependencies array will ensure that the hook will only calls setCount when count's value changes.

    This is how you go about replacing whatever componentWillReceiveProps logic you might have otherwise written in the old class-based React style. I find the "Each Render Has Its Own Props and State" principle useful: if you want to trigger a re-render only when specific props change, you can have several useEffect hooks.

    useEffect(() => {
      count > 100 ? setCount(100) : setCount(count)
    }, [count]) 
     
    useEffect(() => {
      console.log('groupName has changed');
      // do something with groupName
    }, [groupName])
        
    useEffect(() => {
      console.log('companyName has changed');
      // do something with companyName
    }, [companyName]) 
    
    0 讨论(0)
  • 2020-12-25 11:25

    The react hook equivalent to the old componentWillReceive props can be done using the useEffect hook, just specifying the prop that we want to listen for changes in the dependency array.

    I.e:

    export default (props) => {
    
        useEffect( () => {
            console.log('counter updated');
        }, [props.counter])
    
        return <div>Hi {props.counter}</div>
    }
    

    For componentDidUpdate just by omitting the dependency array, the useEffect function will be called after every re-render.

    I.e:

    export default (props) => {
    
        useEffect( () => {
            console.log('counter updated');
        })
    
        return <div>Hi {props.counter}</div>
    }
    
    0 讨论(0)
  • 2020-12-25 11:30

    If you use the useMemo hook on top of your component and have it dependent on all your props, it runs before everything everytime props change. useEffect is triggered after the updated render and since dependent on all props it triggers after a rerender depending on all props.

    const Component = (...props) => {
       // useState, useReducer if have
       useMemo(() => {
         // componentWillReceiveProps
       },[...props]);
       // ...other logic and stuff
       useEffect(() => {
         // componentDidUpdate
       }, [...props]);
    };
    
    0 讨论(0)
提交回复
热议问题