为了防止将回调传递给子组件,我改用useReducer
。 这避免了在每个父渲染上重新渲染子组件的问题,但缺点似乎是父组件和子组件之间的紧密耦合。 通过紧密耦合,我的意思是孩子需要明确地意识到由父母定义的减速器所期望的动作的形状。
例如,假设有一个日期选择器组件。 在某些时候,此组件需要将新的日期/时间值传递给调用组件,以便可以保存(或以某种方式使用)数据。 通过回调,我们可以有一个简单的道具,比如saveDate={onSaveDate}
. 日期选择器通过说"我期待这些道具"来定义合同。 具体来说,我希望有一个签名为newDate => {}
的saveDate
道具。 这个流程对我来说很有意义。
使用useReducer
,父级将dispatch
传递给日期选取器,并且日期选取器需要了解如何创建与化简器期望匹配的操作。 这可以通过在某处的模块中定义动作创建者并将其导入日期选择器来解决,但这对我来说感觉倒退。 如果将从应用程序中的各个组件调用日期选取器,则所有组件都需要就此接口(操作的形状)达成一致。 这似乎不仅将一个组件与日期选取器耦合,而且将使用日期选取器的所有组件相互耦合。
那么,我忽略了什么,有什么策略可以解决这个问题? 对于它的价值,我回到了使用回调,其中更干净的代码比重新渲染的性能问题更有意义。
我建议在父组件中使用操作类型进行调度,如下所示:
const Parent = () => {
const [state, dispatch] = useReducer(datepickerReducer, initialState)
// might wanna useCallback here if your DatePicker is pure
const changeDate = newDate => dispatch({ type: 'CHANGE_DATE', newDate })
return <DatePicker onChange={changedate} value={state} />
}
这样,您的组件与其他组件保持隔离,您可以像在钩子之前一样使用它。虽然,如果你经常使用datepickerReducer,每次重新定义changeDate会很烦人,所以我会有一个自定义钩子来做:
const useDatepicker = init => {
const [date, dispatch] = useReducer(datepickerReducer)
const changeDate = useCallback(newDate => dispatch({ type: 'CHANGE_DATE', newDate }), [])
// I prefer using an object, makes it more convenient to reach values that would have been at the end of the array
return { date, changeDate, /* You could have resetDate here aswell */ }
}
// USAGE
const { date, changeDate } = useDatepicker(new Date())