React 设计模式困境 - 数字表单组件中的步进器



所以我们都知道不受控制的组件通常是一件坏事,这就是为什么我们通常希望在更高级别的组件(通常是某种容器(上管理输入(或输入组(的状态。例如,<Form />组件管理状态并将状态作为值传递给其<Input />组件。它还传递允许输入更新状态的函数,例如handleChange()

但是在实现我自己的<NumericInput />组件时,它让我想到从根本上说,这个组件不是自力更生的。它是可重用的,但需要大量重复(与 DRY 心态相反(,因为在我的应用程序中,我想使用此组件的任何地方,我都必须实现这些状态值、一个 handleChange 函数,在我的<NumericInput />的情况下,两个额外的函数来控制步进箭头。

如果我(或接管我代码的人(想使用这个<NumericInput />,但他们忘记运行到不同的容器组件并复制stepUp()stepDown()函数作为 props 传递,那么将只有两个不起作用的箭头。我知道这个模型允许我们的组件灵活,但它们似乎也更容易出错,并且依赖于其他地方的其他组件。同样,它也是重复的。我是否考虑不正确,还是有更好的方法来管理这个问题?

我认识到这更像是一个理论/设计问题,但我在下面包含我的代码以供参考:

数字输入:

const NumericInput = ({label, stepUp, stepDown, ...props}) => (
<>
{label && <Label>{label}</Label>}
<InputContainer>
<Input type={props.type || "number"} {...props} />
<StepUp onClick={stepUp}>
//icon will go here
</StepUp>
<StepDown onClick={stepDown}>
//icon will go here
</StepDown>
</InputContainer>
</>
);

形式.js

const Form = (props) => {
const [value, setValue] = useState('');
const handleChange = (e) => {
setValue(e.target.value);
}
const stepUp = () => {
setValue(value++);
}
const stepDown = () => {
setValue(value--);
}
return (
<NumericInput 
stepUp={stepUp}
stepDown={stepDown}
handleChange={handleChange}
label="Numeric"
)
}

让我们试着把你的问题归结一下:

[NumericInput] 是可重用的,但需要大量重复(与 DRY 心态相反(,因为在我的应用程序中,我想使用此组件的任何地方,我都必须实现这些状态值、一个 handleChange 函数,以及在我的情况下,两个附加函数来控制步进箭头。

您唯一的重复是为<NumericInput />定义一个value和一个handleChange回调属性。value prop 包含您的数字输入值,并从拥有该状态的父容器组件传入。您需要回调来触发来自子组件(受控组件(的更改请求。当您需要提升状态或将组件划分为容器和表示组件时,这是常用方法。

stepUp/stepDown方法是NumericInput的实现细节,因此您可以从Form中删除它们。Form只想知道是否触发了值更改以及新更改的数值是什么。

关于存储状态的位置:可能有一个原因,为什么你想在Form组件中保存状态。用户在按下提交按钮之前在(可能(多个字段中输入一些内容。此时,表单需要其子字段的所有状态,以根据给定的输入触发进一步处理。惯用的 React 方法是将状态提升到表单以获得信息。

我知道这个模型允许我们的组件灵活,但它们似乎也更容易出错,并且依赖于其他地方的其他组件。同样,它也是重复的。

表示组件不太容易出错并且是独立的,因为它不关心状态!当然,通过在 child 中公开回调和值道具,您可以编写更多的样板代码。最大的优点是,它使无状态组件更具可预测性和可测试性,并且您可以将注意力转移到具有状态的复杂组件上,例如在调试时。

为了简化从Form传递到多个子组件的 prop,您还可以将所有事件处理程序合并到单个事件处理程序中,并为所有输入传递状态。下面是一个简单的示例,说明如何处理这些组件:

数字输入.js:

const NumericInput = ({ label, value, onValueChanged }) => {
const handleStepUp = () => {
onValueChanged(value + 1);
};
const handleStepDown = () => {
onValueChanged(value - 1);
};
return (
<>
...
<Input value={this.state.value} />
<StepUp onClick={handleStepUp}></StepUp>
<StepDown onClick={handleStepDown}></StepDown>
</>
);
};

形式.js:

const Form = (props) => {
const [value, setValue] = useState(0);
const handleValueChanged = (value) => {
setValue(value);
}
return (
<NumericInput 
onValueChanged={handleValueChanged}
value={value}
label="Numeric"
)
}

将 setValue 函数移动到 NumericInput 组件

通过组件管理状态。

return (
<NumericInput 
setValue={setValue}
label="Numeric"
/>
)

我建议你使用钩子

最新更新