如何解决昂贵的自定义钩子?

时间:2019-02-21 14:37:10

标签: reactjs react-hooks

我们知道,规则是:

  

仅顶层呼叫挂钩。不要在循环,条件或嵌套函数中调用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>;
}

它将标志传递给昂贵的钩子是处理条件性钩子的正确方法吗?还有哪些其他选择?

2 个答案:

答案 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