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

MET*_*EAD 30 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;
Run Code Online (Sandbox Code Playgroud)

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

Shu*_*tri 44

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

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

const Persons = (props) =>  {
   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;
Run Code Online (Sandbox Code Playgroud)

  • 但这样做可以重新正确地渲染组件。当我们将 props 同步到 state 时, getDerivedStateFromProps 中的情况并非如此。 (13认同)
  • 错误的答案,这将导致浪费、重影重新渲染。useEffect 与此处无关。最简单的方法是:https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-getdrivenstatefromprops 在某些情况下还有更先进的技术适用,例如编写 useCompulatedState 挂钩。其他一些建议:https://reactjs.org/blog/2018/06/07/you-probously-dont-need-driven-state.html 这是react hooks的一个令人讨厌的副产品。 (12认同)
  • 这种方法看起来很棒,但是这个渲染组件不是两次吗?因为我们在useEffect中使用了setState()。这不影响性能吗? (11认同)
  • 已经讨论过以反模式从 props 初始化状态,但由于我们有钩子,我想事情已经改变了。但我不确定。你怎么认为? (2认同)
  • @ImdadulHuqNaim 不,事情仍然保持不变,只是实施方式发生了变化。这取决于您是否需要从 props 派生 state。例如,可能存在一种情况,输入初始值由 props 提供,但它仍然在本地更改并在说提交时在父级中更新。 (2认同)
  • 为什么在返回中使用`props.name`和`props.age`等而不是`nameState.name`和`nameState.age`等? (2认同)
  • @supra28 是的,但它实际上不会触及 DOM,就像在这个答案中那样。 (2认同)

for*_*d04 21

props中值useState(props)仅用于在初始渲染,进一步状态更新与二传手完成setNameState

此外,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
};
Run Code Online (Sandbox Code Playgroud)

来自React 文档

[...]你可以更新状态正确的渲染过程。React 将在退出第一次渲染后立即重新运行具有更新状态的组件,因此它不会很昂贵。

[...] 渲染过程中的更新在getDerivedStateFromProps概念上一直都是如此。

本质上,我们可以通过摆脱额外的浏览器重绘阶段来优化性能,因为useEffect总是在渲染提交到屏幕后运行。

工作示例

这是一个说明上述模式的人为示例 - 在实际代码中,您可以props.name直接阅读。有关更合适的派生状态用例,请参阅React 博客文章

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
};
Run Code Online (Sandbox Code Playgroud)
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"));
Run Code Online (Sandbox Code Playgroud)


bsa*_*aka 16

这个大体思路可以放到hook中:

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 (...);
}
Run Code Online (Sandbox Code Playgroud)

  • 对于打字稿,它需要这个稍微笨拙的类型版本:export function useStateFromProp&lt;T&gt;(initialValue: T): [T, Dispatch&lt;SetStateAction&lt;T&gt;&gt;] { const [value, setValue] = useState(initialValue); useEffect(() =&gt; setValue(初始值), [初始值]); 返回[值,设置值];} (5认同)

Dha*_*tel 6

为此,您需要使用useEffect使您的代码看起来像。如果 pros 没有改变,你想避免再次重新渲染,那么你必须首先检查 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;
Run Code Online (Sandbox Code Playgroud)

演示

  • 这种方法看起来很棒,但是这个渲染组件不是两次吗?因为我们在useEffect中使用了setState()。这不影响性能吗? (3认同)
  • @DhavalPatel 属性的更改触发了一次渲染,而 `setNameState()` 则触发了另一个渲染。对我来说似乎是两次。 (3认同)