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

vit*_*ter 30 reactjs react-hooks

我期望状态在道具更改时重新加载,但这不起作用,并且user变量在下次useState调用时不会更新,这是怎么回事?

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

码笔

小智 79

我已经看到这个问题的几乎所有答案都宣扬了一种错误的模式:由于调用内的prop 更改useEffect而更新状态。useEffect钩子用于将 React 组件与外部系统同步。使用它来同步React 状态可能会导致错误(因为其他效果引起的重新渲染可能会导致意外的状态更新)。更好的解决方案是通过父组件中的 prop 更改来触发协调:key<Avatar />

// App.jsx
function App() {
   // ...logic here
   return <Avatar initialUser={user} key={user.id} />
}

// Avatar.jsx
function Avatar({ initialUser }) {
 // I suppose you need this component to manage it's own state 
 // otherwise you can get rid of this useState altogether.
  const [user, setUser] = React.useState(initialUser);
  return user.avatar ? (
    <img src={user.avatar} />
  ) : (
    <p>Loading...</p>
  );
}
Run Code Online (Sandbox Code Playgroud)

key在这种情况下,您可以将该prop 视为useEffect 的依赖项数组,但您不会因useEffect组件渲染触发的意外调用而触发意外的状态更改。

您可以在这里阅读更多相关内容: Putting Props To State

useEffect有关脚枪的更多信息,请参见:

您可能不需要效果

  • 谢谢你!“你可能不需要效果”是一颗宝石! (7认同)

Shu*_*tri 71

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

如果您想在道具变更时更新状态,请使用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>);
}
Run Code Online (Sandbox Code Playgroud)

工作演示

  • 如果状态也控制渲染,则这不起作用。我有一个基于道具渲染和基于状态渲染的功能组件。如果状态因更改而通知组件进行渲染,则 useEffect 会运行并将状态设置回默认值。对他们而言,这确实是糟糕的设计。 (4认同)
  • 这最终会设置两次初始状态,这非常烦人。我想知道是否可以将初始状态设置为null,然后每次使用`React.useEffect`来设置初始状态。 (3认同)
  • 在构造函数中设置初始状态时,很容易理解,构造函数在类实例创建时被调用一次。对于钩子,每次函数调用时和每次它应该初始化状态时,都会调用useState,但是在那里发生了一些无法解释的“魔术”。 (2认同)
  • @igorsantos07 如果 props.user 发生变化,状态只会设置回 props 值更改,否则不会 (2认同)

Han*_*bib 11

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

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

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

Run Code Online (Sandbox Code Playgroud)

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

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

React.useEffect(FunctionYouWantToRunAfterEveryRender)

Run Code Online (Sandbox Code Playgroud)

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

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

Run Code Online (Sandbox Code Playgroud)

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

我希望这有助于您的理解,如果需要任何改进,请告诉我,谢谢


Mah*_*gdu 9

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

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

#例子

const [user, setUser] = useStateWithDep(props.user);
Run Code Online (Sandbox Code Playgroud)