【问题标题】:How to use a prop function inside of UseEffect?如何在 UseEffect 中使用 prop 函数?
【发布时间】:2020-10-09 08:48:17
【问题描述】:

我想在 UseEffect 中调用一个 prop 函数。以下代码有效:

useEffect(() => {
    props.handleClick(id);
  }, [id]);

但 lint 抱怨 props 不是依赖项。

如果我这样做,那么代码将不再有效,并且我有最大的重新渲染错误:

  useEffect(() => {
        props.handleClick(id);
      }, [id, props]);

如何解决 lint 问题?

示例代码:

父组件

const ParentGrid = ({rows, columns}) => {

  const [selection, setSelection] = useState(null);

  const handleClick = selectedRows => {
    setSelection(selectedRows.map(i => rows[i]));
  };

  return (
      <ChildGrid
        columns={columns}
        data={data}
        handleClick={handleClick}
      />

子组件

const ChildGrid = props => {
  const {data, handleClick, ...rest} = props;

  useEffect(() => {
    handleClick(selectedRows);
  }, [selectedRows]);

【问题讨论】:

  • 见这里:github.com/facebook/create-react-app/issues/6880。我用这条线// eslint-disable-next-line react-hooks/exhaustive-deps
  • 你不需要把不重要的地方改成useEffect依赖列表。
  • 是什么触发了id 在您的组件中发生更改?您可能不必使用useEffect 来实现您的目标。
  • @Hangindev 感谢您的提示,我对其进行了重构以避免useEffect
  • @SAKURA 不客气。只是让您知道最大重新渲染错误的原因是因为handleClick 触发了ParentGrid 的状态更改,这导致重新渲染并创建了一个新的handleClick 函数并导致重新运行useEffectChildGridarrow 中的回调...♻ 为了避免这种情况,重构是一种解决方案,或者您可以使用useCallback 创建一个记忆化的handleClick 函数,如@iamaatoh 建议的那样。 useCallback docs

标签: javascript reactjs frontend lint


【解决方案1】:

添加props.handleClick作为依赖

useEffect(() => {
  props.handleClick(id);
}, [id, props.handleClick]);

【讨论】:

  • 我试过了,但仍然收到错误:警告:超出最大更新深度。当组件在 useEffect 中调用 setState 时会发生这种情况,但 useEffect 要么没有依赖数组,要么每次渲染时依赖项之一发生变化
  • 你指向函数props.handleClick .. 不是props
  • 另外,当您将 handleClick 作为 prop 传递给该组件时,它是一个内联函数吗?
  • 这不是useEffect 的正确用例。在useEffect 依赖项中,您只需要更改值的重要位置。如果某些值在重新渲染之间没有改变,则做出反应以跳过应用效果。 reactjs.org/docs/…
  • 如果handleClick 是一个在父组件中带有useCallback 的记忆函数,则此useEffect 不会触发,因为handleClick 引用不会改变-恕我直言,这是更好的方法控制useEffect 何时运行。禁用 lint 不会引入更多错误,因为它 实际上使用了 handleClick 函数?
【解决方案2】:

你应该在 props 之外解构 handleClick

在组件的开头你可能有这样的东西:

const myComponent = (props) =&gt;

改成

const myComponent = ({ handleClick, id }) 基本上你可以取出任何你知道的真名的道具

然后像下面这样使用:

useEffect(() => {
    handleClick(id);
  }, [id, handleClick]);

或者可能您实际上并不需要该函数作为依赖项,所以这应该可以工作

useEffect(() => {
    handleClick(id);
  }, [id]);

【讨论】:

  • 所以这仍然对我不起作用,我尝试在useEffect之前拉出该功能:const {data, handleClick, ...rest} = props;如果我没有将它添加到依赖数组中,lint 会抱怨“React Hook useEffect 缺少依赖项:'handleClick'。要么包含它,要么删除依赖数组 react-hooks/exhaustive-deps”。如果我这样做,这是最大更新深度超出错误
  • @SAKURA 你为什么将handleClick 传递给useEffect 这听起来像是应该调用onClick 的那种函数?
  • 命名不好,但基本上该函数是在每次用户选择网格中的一行时将一些数据传递给父组件。我最终重构了我的代码以避免 useEffect,以避免 lint 错误
【解决方案3】:

一种正确的方法是添加props.handleClick 作为依赖项并在父项上记忆handleClick (useCallback),这样函数引用就不会在重新渲染之间不必要地更改。

通常不建议关闭 lint 规则,因为它可以帮助解决细微的错误(当前和未来)

在您的情况下,如果您从 deps 数组中排除 handleClick ,并且在父级上,该函数依赖于父级道具或状态,则当父级上的道具或状态发生更改时,您的 useEffect 将不会触发,尽管它应该,因为handleClick 功能现已更改。

【讨论】:

    【解决方案4】:

    我在这里看到很多奇怪和不正确的答案,所以我觉得有必要写这个。

    将函数添加到依赖数组时达到最大调用深度的原因是它没有稳定的标识。在 react 中,如果您不将函数包装在 useCallback 挂钩中,则会在每次渲染时重新创建函数。这将导致 react 看到你的函数在每次渲染时都发生了变化,因此每次都会调用你的 useEffect 函数。

    一种解决方案是将函数包装在父组件中定义的useCallback 中,并将其添加到子组件中的依赖数组中。使用您的示例,这将是:

    const ParentGrid = ({rows, columns}) => {
      const [selection, setSelection] = useState(null);
    
      const handleClick = useCallback((selectedRows) => {
        setSelection(selectedRows.map(i => rows[i]));
      }, [rows]); // setSelection not needed because react guarantees it's stable
    
      return (
        <ChildGrid
          columns={columns}
          data={data}
          handleClick={handleClick}
        />
      );
    }
    
    const ChildGrid = props => {
      const {data, handleClick, ...rest} = props;
    
      useEffect(() => {
        handleClick(selectedRows);
      }, [selectedRows, handleClick]);
    };
    

    (这假设父组件中的 rows 属性不会在每次渲染时更改)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-03
      • 2020-11-22
      • 2021-05-16
      • 1970-01-01
      • 2020-09-14
      • 2022-08-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多