React.useState 不会从 props 重新加载状态

IT技术 reactjs react-hooks
2021-03-25 03:06:14

我期待状态在props更改时重新加载,但这不起作用并且user变量不会在下次useState调用时更新,有什么问题?

function Avatar(props) {
  const [user, setUser] = React.useState({...props.user});
  return user.avatar ? 
         (<img src={user.avatar}/>)
        : (<p>Loading...</p>);
}

代码笔

6个回答

传递给 useState 的参数是初始状态,很像在类组件的构造函数中设置状态,并且不用于在重新渲染时更新状态

如果您想在props更改时更新状态,请使用useEffect钩子

function Avatar(props) {
  const [user, setUser] = React.useState({...props.user});

  React.useEffect(() => {
      setUser(props.user);
  }, [props.user])

  return user.avatar ? 
         (<img src={user.avatar}/>)
        : (<p>Loading...</p>);
}

工作演示

当在构造函数中设置初始状态时,很清楚地理解构造函数在类实例创建时被调用一次。使用钩子,每次函数调用时都会调用 useState 并且每次应该初始化状态,但是在那里发生了一些无法解释的“魔法”。
2021-05-23 03:06:14
如果状态也控制渲染,这将不起作用。我有一个基于道具渲染和基于状态渲染的功能组件。如果 state 告诉组件因为它改变而渲染,那么 useEffect 运行并将 state 设置回默认值。这对他们来说是非常糟糕的设计。
2021-05-23 03:06:14
这最终会设置两次初始状态,这很烦人。不知道能不能把初始状态设置为null,然后React.useEffect每次都用来设置初始状态。
2021-05-31 03:06:14
你为什么使用React.useState({...props.user})而不是仅仅使用React.useState(props.user)
2021-06-04 03:06:14
如果您需要将 props 合并到 state 中,则在使用 create-react-app 时会陷入无限循环,这要求 state 位于依赖项数组中
2021-06-14 03:06:14

我们用来useState为变量设置初始值的函数式组件,如果我们通过 props 传递初始值,它将始终设置相同的初始值,直到您不使用useEffecthook,

例如你的情况,这将完成你的工作

 React.useEffect(() => {
      setUser(props.user);
  }, [props.user])

传递给 useEffect 的函数将在渲染提交到屏幕后运行。

默认情况下,效果在每次完成渲染后运行,但您可以选择仅在某些值发生更改时触发它们。

React.useEffect(FunctionYouWantToRunAfterEveryRender)

如果您只向 useEffect 传递一个参数,它将在每次渲染后运行此方法,您可以FunctionYouWantToRunAfterEveryRender通过将第二个参数传递给 useEffect来决定何时触发此方法

React.useEffect(FunctionYouWantToRunAfterEveryRender, [props.user])

正如你注意到的,我正在传递 [props.user] 现在useEffect只会FunctionYouWantToRunAfterEveryRenderprops.user更改触发此功能

我希望这有助于您的理解让我知道是否需要任何改进谢谢

您可以为此创建自己的自定义简单挂钩。钩子在更改时更改了默认值。

https://gist.github.com/mahmut-gundogdu/193ad830be31807ee4e232a05aeec1d8

    import {useEffect, useState} from 'react';

    export function useStateWithDep(defaultValue: any) {
      const [value, setValue] = useState(defaultValue);
    
      useEffect(() => {
        setValue(defaultValue);
      }, [defaultValue]);
      return [value, setValue];
    }

#例子

const [user, setUser] = useStateWithDep(props.user);

传递给的参数React.useState()只是该状态的初始值。React 不会将其识别为更改状态,只会设置其默认值。您需要最初设置默认状态,然后有条件地调用setUser(newValue),这将被识别为新状态,并重新渲染组件。

我建议在没有某种条件的情况下谨慎更新状态,以防止它不断更新,因此每次收到props时都会重新渲染。您可能需要考虑将状态功能提升到父组件,并将父组件的状态作为props传递给此 Avatar。

我已经创建了这样的自定义钩子:

const usePrevious = value => {
   const ref = React.useRef();

   React.useEffect(() => {
       ref.current = value;
   }, [value]);

   return ref.current;
}

const usePropState = datas => {
    const [dataset, setDataset] = useState(datas);
    const prevDatas = usePrevious(datas);

    const handleChangeDataset = data => setDataset(data);

    React.useEffect(() => {
        if (!deepEqual(datas, prevDatas)) // deepEqual is my function to compare object/array using deep-equal
            setDataset(datas);
    }, [datas, prevDatas]);

    return [
        dataset,
        handleChangeDataset
    ]
}

使用:

const [state, setState] = usePropState(props.datas);