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

IT技术 reactjs react-hooks
2021-04-04 08:03:42

我正在尝试使用组件接收的props使用 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;

问题是在加载组件时设置状态。但是当它收到新的props时,状态并没有得到更新。在这种情况下如何更新状态?提前致谢。

6个回答

useStatehooks 函数参数仅使用一次,而不是每次props更改时使用。您必须使用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;
错误的答案,这将导致浪费的重渲染。useEffect 没有在这里使用的业务。最简单的方法是:reactjs.org/docs/... 在某些情况下还有更高级的技术适用,例如编写 useComputedState 钩子。其他一些建议:reactjs.org/blog/2018/06/07/... 这是react hooks 的一个令人讨厌的副产品。
2021-05-24 08:03:42
这种方法看起来不错,但是这个渲染组件不是两次吗?因为我们在 useEffect 中使用了 setState()。这不影响性能吗?
2021-05-26 08:03:42
@supra28 是的,但它实际上不会像这个答案中那样触及 DOM。
2021-06-06 08:03:42
为什么在返回时使用props.nameandprops.age等,而不是nameState.name&nameState.age等?
2021-06-07 08:03:42
但是这样做会再次正确地重新渲染组件。当我们将 props 同步到 state 时,getDerivedStateFromProps 中的情况并非如此。
2021-06-21 08:03:42

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
};

来自React 文档

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

[...] 渲染过程中的更新正是getDerivedStateFromProps概念上一直以来的样子。

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

工作示例

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

但是如果我们将一个全新的对象作为道具并且我们需要将它同步到状态,这是否有效。我们如何在对象内部进行深入搜索以检查哪些内容发生了变化,哪些内容没有发生变化?
2021-05-22 08:03:42
不确定,您正在尝试做什么。虽然你必须手动编写深比较逻辑,就像在上面的代码:if (props.name.first !== nameState.first)或者使用深度比较助手
2021-06-16 08:03:42
我希望我早上看到这个答案......我刚刚在这个问题上花了一整天。
2021-06-20 08:03:42

这个大体思路可以放到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 (...);
}
对于typescript,它会想要这个稍微笨拙的类型版本: export function useStateFromProp<T>(initialValue: T): [T, Dispatch<SetStateAction<T>>] { const [value, setValue] = useState(initialValue); useEffect(() => setValue(initialValue), [initialValue]); 返回 [值,设置值];}
2021-05-22 08:03:42

为此,您需要使用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;

演示

这种方法看起来不错,但是这个渲染组件不是两次吗?因为我们在 useEffect 中使用了 setState()。这不影响性能吗?
2021-05-30 08:03:42
@DhavalPatel 道具更改触发了渲染,并setNameState()触发了另一个渲染对我来说似乎是两次。
2021-06-11 08:03:42
@METALHEAD:它不会被调用两次,因为我们还检查了 nameState 是否改变了,那么只有 useEffect 应该执行,参见 useEffect 的第二个参数
2021-06-19 08:03:42
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 react 文档,每当更新任何props或组件中的任何更新时,都会调用 useEffect 。所以你需要在更新 useState 之前检查条件,然后更新你的值,这样它就不会连续重新渲染