【问题标题】:How to isolate state among different instances of functional component that uses useState hook?如何在使用 useState 钩子的功能组件的不同实例之间隔离状态?
【发布时间】:2019-10-27 23:48:18
【问题描述】:

示例是使用 useState 来保持点击计数器的简单功能组件。

逐步通过 Stepper MUI 组件,我想创建具有不同初始化值的示例组件的实例,例如在步骤 0,初始值 100,在步骤 1,初始值 111,在步骤 2,初始值 112。

在逐步执行每个步骤时,尽管传递了不同的初始值,示例功能组件仅将状态保持到第一个初始值,即 100。

文件是 /Components/Navigation/Stepper01a.js,在 StepContent 中引用的示例组件,在 Horizo​​ntalLinearStepper 组件中引用。整体代码来自 Material UI Stepper 组件的示例。我只是试图测试它以在每个步骤中创建具有不同初始值的其他功能组件的不同实例(在本例中为示例)。

示例组件:

function Example({ init }) {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = React.useState(init)

  return (
    <div>
      <p>init {init} </p>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  )
}

StepContent 组件:

function StepContent({ step }) {
  console.log("step", step)

  switch (step) {
    case 0:
      return <Example init={100} />
    case 1:
      return <Example init={111} />
    case 2:
      return <Example init={112} />
    default:
      return "Unknown step"
  }
}

Horizo​​ntalLinearStepper 组件:

export default function HorizontalLinearStepper() {
...
 <div>
     <StepContent step={activeStep} />
 </div>
...
}

查看运行示例https://mj3x4pj49j.codesandbox.io/ 代码https://codesandbox.io/s/mj3x4pj49j

在第 0 步单击下一步时,count 应设置为初始值 111,但仍为 100;在第 1 步单击下一步时,count 应设置为初始值 112,但仍为 100。似乎一旦在第 0 步将状态计数初始化为 100,则在第 1 步和第 2 步中使用相同的状态,即状态不是孤立的。

这个问题是不是因为违反了 Hooks 规则?

【问题讨论】:

    标签: reactjs react-hooks


    【解决方案1】:

    在您正在做的组件内部是正确的。初始值取自 props - 这是常见的做法。

    但是请注意,useState() 仅在初始渲染时应用 init 值(componentDidMountconstructor 用于基于类的组件)。您使用该组件的方式不是重新创建 &lt;Example 而是更新它。

    因此,换句话说,如果没有componentDidUpdate,您将拥有基于类的组件:React 更新现有的&lt;Example&gt; 而不是重新创建它并且不再应用componentDidMount(对于您的情况,它确实如此)不是用初始值初始化useState)。

    我看到了不同的处理方式。

    1. 强制 React 重新创建元素,而不是使用 key 进行更新。哈克但有效的方式:
    switch (step) {
      case 0:
        return <Example key="step-1" init={100} />
      case 1:
        return <Example key="step-2" init={111} />
      case 2:
        return <Example key="step-3" init={112} />
      default:
        return "Unknown step"
    }
    
    1. 您可以使用useEffect 作为componentDidUpdate 的挂钩版本:
    useEffect(() => {
      setCount(init);
    }, [init]);
    

    这是一个棘手的时刻,因为您正在计算点击次数。所以只写setCount(init) 可能不是一个好方法,会破坏你的计算。所以实际的代码可能要复杂得多。我不确定这里,因为不了解计数背后的逻辑。

    1. 通过提升状态(将count 推送到父组件),在init 更新后,您将无需更新任何内容。

    【讨论】:

      【解决方案2】:

      你需要给 React 一个提示,在不同的步骤中你需要不同的 Example 组件实例。这个提示可以这样完成:

      function StepContent({ step }) {
        console.log("step", step)
      
        switch (step) {
          case 0:
            return <Example init={100} key={0} />
          case 1:
            return <Example init={111} key={1} />
          case 2:
            return <Example init={112} key={2}/>
          default:
            return "Unknown step"
        }
      }
      

      原因是 React 在所有情况下都在虚拟 dom 中看到一个 Example,根据它的算法,它认为它是同一个组件但具有不同的 prop,所以他只是改变它的状态而不重新初始化它。

      【讨论】:

        猜你喜欢
        • 2019-08-15
        • 2019-04-26
        • 2021-08-24
        • 2019-09-23
        • 2022-01-20
        • 2021-09-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多