【问题标题】:React.useState does not reload state from propsReact.useState 不会从 props 重新加载状态
【发布时间】:2019-07-18 19:56:44
【问题描述】:

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

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

codepen

【问题讨论】:

    标签: reactjs react-hooks


    【解决方案1】:

    根据ReactJS documentation about Hooks

    但是,如果当组件在屏幕上时,friend 属性发生了变化,会发生什么?我们的组件将继续显示不同朋友的在线状态。这是一个错误。由于取消订阅调用会使用错误的朋友 ID,因此我们还会在卸载时导致内存泄漏或崩溃。

    您在这里唯一的交互应该发生在道具更改上,这似乎不起作用。您可以(仍然根据文档)使用 componentDidUpdate(prevProps) 主动捕捉道具的任何更新。

    PS:我没有足够的代码来判断,但是你不能在你的 Avatar(props) 函数中主动 setUser() 吗?

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        【解决方案3】:

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

        如果你想在 prop 更改时更新状态,请使用 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>);
        }
        

        Working demo

        【讨论】:

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

        我们使用useState为我们的变量设置初始值的功能组件,如果我们通过props传递初始值,它将始终设置相同的初始值,直到你不使用useEffect钩子,

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

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

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

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

        React.useEffect(FunctionYouWantToRunAfterEveryRender)
        
        

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

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

        你注意到我现在正在传递 [props.user] useEffect 只会在 props.user 更改时触发这个 FunctionYouWantToRunAfterEveryRender 函数

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

        【讨论】:

          【解决方案5】:

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

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

          【讨论】:

            【解决方案6】:

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

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2015-07-05
              • 1970-01-01
              • 1970-01-01
              • 2016-11-11
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-11-26
              相关资源
              最近更新 更多