reactjs 如何使用React钩子将 prop 同步到状态:设置状态()

ufj5ltwl  于 2022-12-18  发布在  React
关注(0)|答案(8)|浏览(171)

我尝试使用React钩子setState()来设置状态,使用组件接收的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);
 
   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;

问题是组件加载时状态是设置好的,但是收到新的 prop 时状态没有更新,这种情况下如何更新状态呢?

5q4ezhmt

5q4ezhmt1#

useState挂钩函数参数只使用一次,而不是每次属性更改时都使用。必须使用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;
km0tfn4u

km0tfn4u2#

useState(props)中的props值仅在初始渲染期间使用,进一步的状态更新使用设置器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文件:
[...]你可以在渲染过程中更新状态正确**。React将在退出第一次渲染后立即 * 重新运行状态更新后的组件**,因此成本不会很高。
[...]渲染期间的更新正是X1 M4 N1 X在概念上一直以来的样子。
本质上,我们可以通过消除额外的浏览器重绘阶段来优化性能,因为useEffect总是在渲染提交到屏幕之后运行。

工作示例

这是一个人为的例子来说明上面的模式--在真实的代码中,你可以直接读取props.name。有关更合适的派生状态用例,请参见React blog post
x一个一个一个一个x一个一个二个x

gudnpqoy

gudnpqoy3#

这一大致思路可以归纳为:

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

ghg1uchk4#

为此,你需要使用useEffect,这样你的代码看起来就像。因为你想避免在pros没有改变的情况下再次重新渲染,所以你必须首先检查useEffect,然后将props设置为current变量。

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

7gcisfzg

7gcisfzg5#

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更新或component更新时,useEffect都会被调用,所以你需要在更新useState之前检查条件,然后更新你的值,这样它就不会不断地重新渲染

zbsbpyhn

zbsbpyhn6#

我想了一个避免useEffect的替代解决方案。相反,它使用两个useState。我为您将其放入一个自定义挂钩中:

export function useStateFromProp(propValue) {
  const [value,          setValue         ] = useState(propValue);
  const [propValueState, setPropValueState] = useState(propValue);

  if (propValueState != propValue) {
    setPropValueState(propValue);
    setValue(propValue);
  }

  return [value, setValue];
}

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

  return (...);
}

主要的好处是,现在通常由useEffect触发的重新呈现发生在任何子组件被重新呈现之前,所以这会更快。
免责声明:我还没有测试这个。我做了一些谷歌搜索,发现这篇支持文章:https://pretagteam.com/question/in-react-hooks-when-calling-setstate-directly-during-render-is-the-rerender-guaranteed-to-run-before-the-render-of-children

vvppvyoh

vvppvyoh7#

如果需要从 prop 和其他状态计算状态**,而不需要额外的重新渲染**,请考虑:
a)使用useMemo吊钩。

const Component = ({ name }) => {
  const [surname, setSurname] = useState('');

  const fullName = useMemo(() => {
     return name + ' ' + surname;
  }, [name, surname])

  ...
}

B)如果不是很重,计算内部抹灰:

const Component = ({ name }) => {
  const [surname, setSurname] = useState('');

  const fullName = name + ' ' + surname;

  ...
}

c)对于需要比较prev props并且应该能够从另一个地方更新state的困难情况,可以使用refs,尽管看起来不太好:

const Component = ({ name }) => {
  const prevNameRef = useRef()
  const derivedState = useRef();

  if (prevNameRef.current !== name) {
    derivedState.current = ...
    prevNameRef.current = name;
  }
 
  // some other place
  derivedState.current = ...
}

6ju8rftf

6ju8rftf8#

我相信这个问题表明有人试图使用一个或一组概念上的变量来做两件不同的事情,例如试图让props.namename做同样的事情。
所以如果

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

这还不够,您发现自己试图将props.name强制到函数后面的状态变量name中,然后可能name被重载。尝试设置另一个状态变量-例如updatedName,看看是否效果更好。
原始示例没有演示这个问题,因为状态变量除了在日志语句中使用之外,从不使用。
如果const [name, setName] = useState(props.name)在每次重新呈现时更新,则具有状态变量name将没有意义,因为它将总是与props.name相同(并且进一步尝试改变它将导致重新呈现)。

相关问题