我们知道,规则是:
仅顶层呼叫挂钩。不要在循环,条件或嵌套函数中调用Hook。
所以我的问题是如何使用和设计昂贵的自定义挂钩?
给出此钩子:
const useExpensiveHook = () => {
// some code that uses other built-in hooks...
const value = computeExpensiveValue();
// some more code....
return value;
}
如果该规则不存在,我的客户代码将是:
const myComponent = ({isSuperFeatureEnabled}) => {
let value;
if (isSuperFeatureEnabled){
value = useExpensiveHook();
}
return <div>{value}</div>;
}
我想出的解决方案是使用一个标志让钩子知道应该像这样纾困:
const useExpensiveHook = ({enabled}) => {
// some code that uses other built-in hooks...
let value;
if(enabled) {
value = computeExpensiveValue();
}
else {
value = undefined;
}
// some more code....
return value;
};
和客户端代码:
const myComponent = ({isSuperFeatureEnabled}) => {
const value = useExpensiveHook({enabled : isSuperFeatureEnabled});
return <div>{value}</div>;
}
它将标志传递给昂贵的钩子是处理条件性钩子的正确方法吗?还有哪些其他选择?
答案 0 :(得分:0)
在原始示例中,挂钩初始值昂贵,而不是挂钩本身,可以有条件地调用computeExpensiveValue
:
const [value, setValue] = useState(enabled ? computeExpensiveValue() : undefined);
在当前列出的示例中,useExpensiveHook
不是钩子,而是某些函数;它不使用React钩子。
带引号的规则的目的是无条件调用内置的钩子,因为钩子的状态取决于它们的调用顺序:
if (flipCoin())
var [foo] = useState('foo');
var [bar] = useState('bar');
如果在下一个组件渲染中未调用useState('foo')
,则useState('bar')
成为第一个被调用并被视为useState
状态的foo
挂钩,而第二个{{1 }}丢失,这种不一致会在渲染器中触发错误。
如果可以保证保留挂钩调用的顺序,则使用条件是可以接受的,但是在实践中很少可行。即使看起来像useState
这样的恒定条件,它也可能在运行时的某些情况下发生变化,并导致难以调试的上述问题。
正确:
if (process.env.NODE_ENV === 'development')
不正确:
useEffect(() => {
if (varyingCondition)
computeExpensiveValue();
});
该规则仅适用于内置挂钩和直接或间接调用它们的函数(所谓的自定义挂钩)。只要if (varyingCondition)
useEffect(() => {
computeExpensiveValue();
});
在内部不使用内置挂钩,就可以有条件地对其进行调用,如“正确”的示例所示。
如果某个组件需要根据prop标志有条件地应用第三方挂钩,则应通过将其限制为初始prop值来确保该条件不会随时间改变:
computeExpensiveValue
这样const Component = ({ expensive, optionalValue }) => {
const isExpensive = useMemo(() => expensive, []);
if (isExpensive)
optionalValue = useExpensiveHook();
return ...
}
不会违反规则,而只是滥用组件。
由于应该知道在使用<Component expensive={flipCoin()} />
时是否需要昂贵的钩子,因此一种更干净的方法是在更高阶的组件中组合此功能,并根据所需的组件使用不同的组件:< / p>
<Component expensive/>
答案 1 :(得分:0)
import DialogContent from '@material-ui/core/DialogContent';
// ...
render () {
return (
<Modal open={this.state.modalOpened} onClose={() => this.setState({ modalOpened: false, modalContent: null })}>
<DialogContent>
{this.state.modalContent}
</DialogContent>
</Modal>
);
}
的参数仅使用一次,因此,如果您最初将useState
传递为false,它将永远不会执行enabled
。因此,您也需要添加一个computeExpensiveValue
调用。您可以改为设计钩子
useEffect