我有两个自定义的 React 钩子,可以获取不同的数据并解析它。两个钩子都返回相同类型的对象。我有一个显示该数据的组件,并且根据“变体”道具,我希望它决定安装这两个钩子中的哪一个。
示例代码:
const useJohn = () => {
// fetch & parse some data
return { parsedData: { date: new Date(), name: 'John Doe' } }
}
const useJane = () => {
// fetch & parse some data
return { parsedData: { date: new Date(), name: 'Jane Smith' } }
}
const dataDisplay = ({ variant }: { variant: 'John' | 'Jane' }) => {
const hook = variant === 'John' ? useJohn : useJane
const { parsedData } = hook()
return <div>{`${parsedData.date.toString()} ${parsedData.name}`}</div>
}
我没有从这种模式中收到任何警告/错误,一切似乎都工作正常。这是有条件地使用自定义挂钩的合法方式吗?或者这个模式有什么我不知道的问题吗?
好吧,首先,我们假设 useJohn 和 useJane 都在这里调用一些效果。
如果他们这样做了,就会做:
const hook = variant === 'John' ? useJohn : useJane
const { parsedData } = hook()
是错误的,并且会失败!
如果它还没有失败,那是因为你没有更新组件的 prop。但是一旦 prop 发生变化,被调用的钩子就会改变,并且你将破坏一个反应不变量,即组件应该在每次渲染时渲染完全相同数量的钩子调用。
也就是说,这会失败:
const MyComponent = () => {
const [data, setData] = useState<'John' | 'Jane'>('John');
useEffect(() => {
setTimeout(() => setData('Jane'), 1000)
}, [setData])
<dataDisplay variant={{variant: data}} />
你应该如何解决这个问题?要么创建一个高阶组件来处理 John 与 Jane 的决策,这样您就有两个组件:一个使用 John,另一个始终使用 Jane,并使用相同的显示组件:
const DecideJohnVsJane = (choice: 'John' | 'Jane') => {
switch (choice) {
case "John": return <JohnComponent />
case "Jane": return <JaneComponent />
}
}
const JohnComponent = () => {
const parsedData = useJohn();
return <DisplayComponent data={parsedData} />
}
const JaneComponent = () => {
const parsedData = useJane();
return <DisplayComponent data={parsedData} />
}
const DisplayComponent = (parsedData) => <div>{`${parsedData.date.toString()} ${parsedData.name}`}</div>
另一种可能更简单的方法是创建单一效果:
const useJaneOrJohn = (choice: 'John' | 'Jane') => {
switch (choice) {
case 'John':
// fetch the john specific stuff
return parsedData
case 'Jane':
// fetch the jane specific stuff
return parsedData
}
}
哪个最好实际上取决于您的问题中未共享的上下文,并且该选择取决于您。