如何在使用useState钩子的功能组件的不同实例之间隔离状态?

时间:2019-06-13 11:34:20

标签: reactjs react-hooks

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

逐步执行Stepper MUI组件,我想创建具有不同初始值的Example组件实例,例如在步骤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上单击“下一步”时,期望将计数的初始值设置为111,但仍为100;在步骤1中单击“下一步”时,期望将计数的初始值设置为112,但仍为100。一旦在第0步将状态计数初始化为100,则在第1步和第2步使用相同的状态,即状态未隔离。

是否会由于某种原因违反钩子规则而发生此问题?

2 个答案:

答案 0 :(得分:2)

在组件内部,您做的正确。初始化值取自props-这是普遍做法。

但是请注意,useState()仅将初始化值应用于初始渲染(对于基于类的组件,componentDidMountconstructor)。使用该组件的方式不是重新创建<Example,而是对其进行更新。

因此,换句话说,您将获得不带componentDidUpdate的基于类的组件:React更新了现有的<Example>而不是重新创建它并且不再应用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更新后进行任何更新。

答案 1 :(得分:1)

您需要给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,因此他只是更改其状态而不重新初始化它。