即使它违反了钩子的规则,useCallback React 钩子也可以有条件地使用吗?

Din*_*yan 5 javascript reactjs react-hooks

我试图找出一种方法来通过指定特定的道具来记忆 React 组件

例如,如果您使用React.memo- 它会根据所有道具记住组件。

我想要实现的是能够将特定道具作为依赖项传递给 util(例如,SuperMemo),并且组件将根据这些道具进行记忆。该方法与重构非常相似——在导出之前组合组件。

这是一个示例代码

import React from "react";

const isFunction = value =>
  value &&
  (Object.prototype.toString.call(value) === "[object Function]" ||
    "function" === typeof value ||
    value instanceof Function);

export const memo = (Comp, resolver) => {
  if (isFunction(resolver)) {
    const Memoized = props => {
      const deps = resolver(props);
      if (deps && deps.length) {
        // eslint-disable-next-line react-hooks/rules-of-hooks
        return React.useCallback(React.createElement(Comp, props), deps);
      }

      return React.createElement(Comp, props);
    };

    Memoized.displayName = `memoized(${Comp.name})`;
    return Memoized;
  }

  return React.memo(Comp);
};

export default memo;

Run Code Online (Sandbox Code Playgroud)

以下是它将如何用于组合组件

import Todo from "./Todo";
import memo from "../memo";

export default memo(Todo, props => [props.text]);
Run Code Online (Sandbox Code Playgroud)

我这里有一个可用的代码和盒子 — memo-deps

这是我观察到的——

  • 我不应该React.useCallback在条件语句中使用或任何钩子,因为 React 需要知道调用钩子的顺序,并且在条件语句中使用它可能会在运行时弄乱顺序
  • 但是React.useCallback在我的情况下,在有条件的情况下工作非常整洁,因为我知道顺序在运行时将保持不变
  • 我在渲染过程中没有在条件语句中使用钩子,而是在有条件的导出过程中组合组件
  • 我正在考虑将 React 组件视为普通的 JavaScript 函数,并尝试像记住常规 JavaScript 函数一样记住它
  • 我可以轻松替换React.useCallbacklodash.memoize,最终结果几乎相同
  • 我不想使用外部库,例如lodash.memoize或构建记忆化的自定义实现,而React.useCallback几乎为我完成了工作

这是我不确定发生了什么的地方(这些是我的问题)-

  • React 组件并不是真正的普通 JavaScript 函数,我无法记住它们 lodash.memoize
  • lodash.memoize并且React.useCallback当我尝试记住 React 组件时不一样
  • 即使在React.memo使用时,React 也会在确定渲染之前执行该函数(也许是为了检查 prevProps 还是 newProps?)
  • 即使我的实现违反了 React 的规则,它是否还可以?(在条件语句中使用钩子)
  • React.createElement如果不是,我还能怎么记住React.useCallback

至于我为什么要这样做的原因——

我不想每次将处理程序(带有值和事件的闭包)传递给包装在React.memo. 我希望能够以声明方式为组件编写 memoize 依赖项。

Moh*_*ami 6

React.memo 接受一个函数作为第二个参数来进行自定义道具比较。

默认情况下,它只会浅比较 props 对象中的复杂对象。如果您想控制比较,您还可以提供自定义比较函数作为第二个参数。

您可以在您的 util 函数中使用它,如下所示:

export const memoWithSecondParam = (Comp, deps = []) => {
  return React.memo(Comp, (prevProps, nextProps) => {
    return deps.every(d => prevProps[d] === nextProps[d])
  });
};
Run Code Online (Sandbox Code Playgroud)

并这样称呼它:

export default memoWithSecondParam(Todo, ["text"]);
Run Code Online (Sandbox Code Playgroud)

  • 打破钩子规则可能会起作用(就像在您的codesandbox中一样),但它会使您的代码变得脆弱,并且如果反应团队决定改变事物的实现方式,则将来可能会破坏。我不建议这样做。如果您找到使用公共 API 的解决方案,请坚持使用它 (2认同)