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


77

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

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


您可以在中添加代码吗setNameState
ManavM,

嗨,Manav,所以我们不能在每次道具更新时都使用useState将道具同步到状态吗?
METALHEAD '19

不,那条线只是初始化您的状态。如果您想每次使用道具更新状态时,都必须使用useEffect
ManavM

Answers:


110

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;

2
曾经有人讨论过用反模式中的props初始化状态,但是由于我们有钩子,我想事情已经改变了。但我不确定。你怎么看?
Imdadul Huq Naim

9
但是这样做可以再次重新呈现该组件。当我们将道具同步到状态时,在getDerivedStateFromProps中不是这种情况。
METALHEAD

8
为什么在返回中使用props.nameandprops.age等而不是nameState.namenameState.age等?
sutherlandahoy

7
这种方法看起来不错,但是此渲染组件不是两次吗?因为我们在useEffect中使用setState()。这不是热门演出吗?
METALHEAD'2

6
错误的答案,这将导致浪费的重影渲染。useEffect在这里没有使用任何业务。最简单的方法是:reactjs.org/docs/… 在某些情况下,有一些更高级的技术适用,例如编码useComputedState钩子。其他一些建议:reactjs.org/blog/2018/06/07/… 这是React钩子的产物。
AlexG

12

可以将这个一般想法归纳为:

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 (...);
}

11

propsin的值useState(props)在初始渲染期间使用,进一步的状态更新由setter完成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 docs

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

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

从本质上讲,我们可以通过消除附加的浏览器重绘阶段来优化性能,就像useEffect在将渲染提交到屏幕之后始终运行一样。

工作实例

这是一个人为的例子,上面的模式说明了-您可以props.name直接阅读真实代码。请参阅React博客文章以获取更多合适的派生状态用例。


但是,如果我们要获得一个全新的对象作为道具,并且需要将其同步到状态,那么这样做是否合适?我们如何在对象内部进行深层搜索以检查更改了什么和没有更改?
METALHEAD

1
不确定,您要做什么。尽管您必须手动编写深度比较逻辑,例如上面的代码:if (props.name.first !== nameState.first)。或使用深度比较助手
ford04 '20

6

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

演示版


1
这种方法看起来不错,但是此渲染组件不是两次吗?因为我们在useEffect中使用setState()。这不是热门演出吗?
METALHEAD '20

1
@METALHEAD:不会被调用两次,因为我们还检查了nameState是否已更改,然后仅应执行useEffect,请参见useEffect的第二个参数
Dhaval Patel

3
@DhavalPatel更改的道具触发了一个渲染,并setNameState()触发了另一个。在我看来就像两次。
Drazen Bjelovuk

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 react文档,在任何时候更新任何道具或组件中的任何更新时,都会一直调用useEffect。因此,您需要在更新useState之前先检查条件,然后再更新您的值,以使它不会连续进行重新渲染


0

我相信问题表明尝试使用一个概念变量或一组变量来做两种不同的事情。例如,尝试获取props.namename执行相同的操作。

因此,如果

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

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

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

如果const [name, setName] = useState(props.name)在重新渲染时进行了更新,那么状态变量name将始终是相同的props.name(状态变量将永远不变)(并且进一步尝试更改它会导致重新渲染)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.