译:在 React 中使用回调函数实现更好的组件解耦

原文:https://darios.blog/posts/using-callbacks-in-react
作者:Dario Djuric
译者:ChatGPT 4 Turbo

想象你有一个入职组件,它根据当前步骤显示 WelcomeStepTermsOfServiceStepCompleteStep。一个初学者开发者可能会这样实现:

export type Step = 'welcome' | 'terms-of-service' | 'complete';

export default function OnboardingSection() {
  const [step, setStep] = useState<Step>('welcome');

  return (
    <Box>
      {step === 'welcome' && (
        <WelcomeStep setStep={setStep} />
      )}
      {step === 'terms-of-service' && (
        <TermsOfServiceStep setStep={setStep}  />
      )}
      {step === 'complete' && <CompleteStep />}
    </Box>
  );
}

WelcomeStep 组件有一个按钮,点击时,会更新状态为 welcome

export function WelcomeStep(props: { setStep: (newStep: Step) => void }) {
  return (
    <Box>
      <Box>欢迎来到我们的平台!</Box>
      <Box>
        <Button onClick={() => props.setStep('terms-of-service')}>下一步</Button>
      </Box>
    </Box>
  );
}

这种方法没有什么特别的问题,并且能够正确工作。然而,我们引入了 WelcomeStep 组件和 OnboardingSection 组件之间的紧密耦合。

为什么这是一个问题?

这种方法有几个问题。首先,这打破了父组件的封装,因为它现在将组件的内部状态泄露给了其子组件。暴露了设置器可能会使问题难以排查,因为你无法控制设置器的使用位置。一个子组件可能以父组件没有预期或控制的方式更新状态。

这也可能导致不必要的重新渲染,因为子组件现在控制着父组件的重新渲染。当只有父组件控制状态变化时,它对状态更新的时间有更多的控制。

最后,子组件现在更难测试,因为由于两者之间的高度耦合,它总是需要与父组件配对使用。

解决方案

解决方案是让子组件提供一个回调函数给父组件。虽然在这个示例中流程保持不变,但现在你使得子组件更易于测试和在不同的上下文中重用。让子组件暴露类似 onClickNext 的东西意味着它可以很容易地在 OnboardingSection 以外的组件中使用,使得组件更易于测试。

以标准的 input 元素为例。你在该元素的接口上看不到任何设置器函数。相反,有一个 value 属性和一个 onChange 属性。你向下传递值,并向上通知改变。这使得接口更简单,使得这个组件在非常不同的用例中可重用。

这是我们组件的修订版本:

export default function OnboardingSection() {
  const [step, setStep] = useState<Step>('welcome');

  return (
    <Box>
      {step === 'welcome' && (
        <WelcomeStep onClickNext={() => setStep('terms-of-service')} />
      )}
      {step === 'terms-of-service' && (
        <TermsOfServiceStep onClickNext={() => setStep('complete')} />
      )}
      {step === 'complete' && <CompleteStep />}
    </Box>
  );
}

我们现在在拥有设置器的组件中调用设置器,减少了该组件与子组件之间的耦合。这种方法的另一个好处是,如果你需要的话,现在你可以在 onClickNext 处理函数中拥有更复杂的逻辑。

在设计组件时,总是试图在设计它们的接口时多加一些努力。试着把组件孤立地看待,好像你不知道它们将如何或在哪里被使用。你会发现,你将开始设计出更简单、更易于使用和测试的组件。