如何使用React钩子将道具同步到状态:setState()

时间:2019-02-11 07:35:59

标签: reactjs react-hooks

我正在尝试使用组件接收的道具使用React hook setState()来设置状态。我尝试使用以下代码:

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

const Persons = (props) =>  {

    // console.log(props.name);

   const [nameState , setNameState] = useState(props)

   console.log(nameState.name);
   console.log(props.name);

   return (
            <div>
                <p>My name is {props.name} and my age is {props.age}</p>
                <p>My profession is {props.profession}</p>
            </div>
        )

}

export default Persons;

问题在于加载组件时正在设置状态。但是,当它收到新的道具时,状态不会更新。在这种情况下如何更新状态?预先感谢。

6 个答案:

答案 0 :(得分:11)

useState钩子函数参数仅使用一次,而不是每次prop更改时使用。您必须使用useEffect钩子来实现所谓的componentWillReceiveProps/getDerivedStateFromProps功能

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

const Persons = (props) =>  {

    // console.log(props.name);

   const [nameState , setNameState] = useState(props)

   useEffect(() => {
       setNameState(props);
   }, [props])

   return (
            <div>
                <p>My name is {props.name} and my age is {props.age}</p>
                <p>My profession is {props.profession}</p>
            </div>
        )

}

export default Persons;

答案 1 :(得分:11)

props中的useState(props)值仅用于during the initial render,进一步的状态更新由设置器setNameState完成。

此外,更新derived state时不需要useEffect

const Person = props => {
  const [nameState, setNameState] = useState(props.name);
  // update derived state conditionally without useEffect
  if (props.name !== nameState) setNameState(props.name);
  // ... other render code
};

来自React docs

[...]您可以在渲染期间更新状态 。在退出第一个渲染器后,React将立即重新运行具有更新状态的组件,这样就不会太昂贵了。

[...]渲染期间的更新恰恰是getDerivedStateFromProps从概念上一直是这样。

实质上,我们可以通过消除附加的浏览器重绘阶段来优化性能,因为useEffect始终在将渲染提交到屏幕后运行。

工作示例

这是一个精心设计的示例,说明了上述模式-在实际代码中,您将直接阅读props.name。有关更合适的派生状态用例,请参见React blog post

const Person = props => {
  const [nameState, setNameState] = React.useState(props.name);
  // Here, we update derived state without useEffect
  if (props.name !== nameState) setNameState(props.name);

  return (
    <p>
      <h3>Person</h3>
      <div>{nameState} (from derived state)</div>
      <div>{props.name} (from props)</div>
      <p>Note: Derived state is synchronized/contains same value as props.name</p>
    </p>
  );
};

const App = () => {
  const [personName, setPersonName] = React.useState("Lui");
  const changeName = () => setPersonName(personName === "Lukas" ? "Lui" : "Lukas");

  return (
    <div>
      <Person name={personName} />
      <button onClick={changeName}>Change props</button>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>

答案 2 :(得分:2)

为此,您需要使用useEffect,以便代码看起来像。 如果要避免在专业人员没有变化的情况下再次重新渲染,则必须首先检查useEffect,然后将props设置为当前变量。

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

const Persons = props => {
  // console.log(props.name);

  const [nameState, setNameState] = useState(props);

  console.log(nameState.name);
  console.log(props.name);
  useEffect(
    () => {
      if (nameState !== props.name) {
        setNameState(props.name);
      }
    },
    [nameState]
  );
  return (
    <div>
      <p>
        My name is {props.name} and my age is {props.age}
      </p>
      <p>My profession is {props.profession}</p>
    </div>
  );
};

export default Persons;

Demo

答案 3 :(得分:2)

可以将这个一般想法挂入钩子:

export function useStateFromProp(initialValue) {
  const [value, setValue] = useState(initialValue);

  useEffect(() => setValue(initialValue), [initialValue]);

  return [value, setValue];
}


function MyComponent({ value: initialValue }) {
  const [value, setValue] = useStateFromProp(initialValue);

  return (...);
}

答案 4 :(得分:1)

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

const Persons = props => {
  // console.log(props.name);

  const [nameState, setNameState] = useState(props);

  console.log(nameState.name);
  console.log(props.name);
  useEffect(
    () => {
      if (nameState !== props) {
        setNameState(props);
      }
    },
    [nameState]
  );
  return (
    <div>
      <p>
        My name is {props.name} and my age is {props.age}
      </p>
      <p>My profession is {props.profession}</p>
    </div>
  );
};

export default Persons;

根据Hooks反应文档,在任何时候更新任何道具或组件中的任何更新时,都会一直调用useEffect。因此,您需要在更新useState之前先检查条件,然后再更新您的值,以使其连续不断地不重新呈现

答案 5 :(得分:0)

我相信问题表明尝试使用一个概念性变量或一组变量来做两种不同的事情。例如,尝试让props.namename做同样的事情。

如果

const [name, setName] = useState(props.name)

还不够,您发现自己试图在函数中稍后将props.name强制进入状态变量name,然后name可能正在被重载。尝试设置另一个状态变量-例如。 updatedName,看看情况是否更好。

原始示例没有演示此问题,因为除了在日志语句中,从未使用过状态变量。

如果const [name, setName] = useState(props.name)在重新渲染时进行了更新,则状态变量name毫无意义,因为状态变量始终与props.name相同(进一步尝试对其进行更改将导致重新渲染。)