代码之家  ›  专栏  ›  技术社区  ›  Deng Zhebin

componentWillReceiveProps,componentDidUpdate for React钩子

  •  1
  • Deng Zhebin  · 技术社区  · 7 年前

    我遇到两个挑战:

    • 派生状态 不鼓励,但一些边缘案例仍然需要它。
      对于一个带有反应钩的功能部件来说,

    参见下面的代码示例:

    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;
    • 至于componentdiddupdate,componentdiddupdate有它的对应项当使用React Hook时,你必须像这样使用它,

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

      useffect的第二个参数确保只在prop更改时执行代码,但是 如果我想根据多个道具的变化来完成各自的任务呢 ? 如何完成

    参见下面的代码示例:

    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;
    0 回复  |  直到 6 年前
        1
  •  28
  •   Fabien Sa Shami    6 年前

    componentWillReceive 道具可以使用 useEffect

    即:

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

    componentDidUpdate 只要省略依赖数组 使用效果

    即:

    export default (props) => {
    
        useEffect( () => {
            console.log('counter updated');
        })
    
        return <div>Hi {props.counter}</div>
    }
    
        2
  •  7
  •   Tholle    7 年前

    你可以用 useMemo 钩住一个计算并把 props.count 在数组中作为第二个参数指定,以便在值更改时重新计算该值。

    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>

    useEffect 只有当一个单独的道具改变时才会运行的钩子。

    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"));
    <
    <div id=“root”></div>
        3
  •  5
  •   simmer    5 年前

    setCount 将触发重新渲染。使用 useEffect 具有 [count] 因为dependencies数组将确保钩子只调用 设置计数 什么时候 count 的值更改。

    这就是你去替换任何东西的方法 componentWillReceiveProps 否则,您可能会以旧的基于类的React样式编写逻辑。我找到了“ Each Render Has Its Own Props and State “有用的原则:如果你只想在特定道具改变时触发重新渲染,你可以有几个 使用效果

    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]) 
    
        4
  •  2
  •   Kevin F.    7 年前

    在您的场景中,不需要使用或重新实现 getDerivedStateFromProps

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

    此处演示: https://codesandbox.io/embed/qzn8y9y24j?fontsize=14

    您可以阅读更多关于解决此类情况的不同方法,而不必使用 getDerivedStateFromProps获取状态 https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html

    如果你真的需要使用一个单独的状态,你可以使用这样的东西

    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>;
    };
    
        5
  •  1
  •   jpmarks    6 年前

    const Component = (...props) => {
       // useState, useReducer if have
       useMemo(() => {
         // componentWillReceiveProps
       },[...props]);
       // ...other logic and stuff
       useEffect(() => {
         // componentDidUpdate
       }, [...props]);
    };
    
        6
  •  1
  •   kwoxer    6 年前

    简单地使用这样的效果。

    useEffect( () => {
        props.actions.fetchSinglePost(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.
    
        7
  •  0
  •   Ryan Cogswell    7 年前

    我知道你的“派生国”的例子是故意简单的,但是因为派生国的合法案例太少,所以很难对替换提出建议,除非是在个案的基础上,因为这取决于你使用派生国的原因。在您提供的特定示例中,没有理由在类case中使用派生状态,因此在hook case中仍然没有理由(可以只在本地派生值而不将其放入state)。如果派生值很昂贵,可以使用 useMemo 正如托尔所说。如果这些不符合您所想到的更实际的情况,那么您需要呈现一个真正需要派生状态的更具体的情况。

    componentDidUpdate 例如,如果你想为不同的道具做什么是独立的,那么你可以为每个道具使用不同的效果(即多个) useEffect 确切地 companyName 如果更改 groupName 也没有像你说的那样改变 else if ),则可以使用 refs 不 this related question 组名 公司名称

    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

        8
  •  0
  •   ford04    6 年前

    Derived state for Hooks=有条件地和 直接地

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

    useEffect . 以上模式由 React Strict Mode (无警告):

    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>

    附注1 componentWillReceiveProps 已经有相当一段时间了。 getDerivedStateFromProps 是类组件在派生状态方面的继承者。

    附注2 : Check preferred solutions


    2.)如果我想基于多个各自的道具更改来执行各自的任务,该怎么办?

    你要么离开

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