React生命周期与React Hooks中的useEffect函数

3
我目前正在将我的应用程序文件从包含componentDidMount等生命周期事件的class更改为使用useEffect钩子的function。对于大多数文件,我没有看到任何问题,但是在下面这个文件中,我遇到了性能问题,应用程序会冻结。控制台中没有任何错误或警告,但我的机器和Chrome浏览器在此选项卡上增加了内存使用量。

我错过了什么?

旧的基于类的文件代码:

listener: any;

componentDidMount() {
  const { firebase } = this.props;
  this.listener = firebase.onAuthUserListener(
    (authUser: any) => {
      localStorage.setItem('authUser', JSON.stringify(authUser));
      this.setState({ authUser });
    },
    () => {
      localStorage.removeItem('authUser');
      this.setState({ authUser: null });
    }
  );
}

componentWillUnmount() {
  this.listener();
}

使用钩子(导致性能问题)的新功能

const listener = () => {
  firebase.onAuthUserListener(
    (authUser: any) => {
      localStorage.setItem('authUser', JSON.stringify(authUser));
      setState(authUser);
    },
    () => {
      localStorage.removeItem('authUser');
      setState(null);
    }
  );
};

useEffect(() => {
  listener();
  return () => {
    listener();
  };
});

值得一提的是,我在使用React时还使用了TypeScript。


糟糕,我忘记了。我的原始代码复制/粘贴出了问题。 - Darren
3个回答

2

onAuthUserListener 返回一个取消订阅的函数。当组件卸载时应该使用它。

在你的代码中,你没有返回取消订阅的函数。

const listener = () => {
  firebase.onAuthUserListener(..)    // problem here no return
}

因此,在 useEffect 中,您应该正确地分配它并在 useEffect 的返回值中使用它。

const [user, setUser] = React.useState(null);

useEffect(
  () => {
    //             v------ proper assignment.
    const listener = firebase.onAuthUserListener(
      (authUser: any) => {
        localStorage.setItem('authUser', JSON.stringify(authUser));
        setUser(authUser);
      },
      () => {
        localStorage.removeItem('authUser');
        setUser(null);
      }
    );

    return () => listener();
  }
  , [] // no deps
);

谢谢Joseph。这是对我最有效的方法。不过,我已经将firebase添加到依赖项中。 - Darren

1
这可能是错误的位置:
useEffect(() => {
  listener();
  return () => {
    listener();  <--- here
  };
});

问题是,需要改变哪个状态才能触发listener();的调用?

我认为是这样的:

const listener = () => {
  firebase.onAuthUserListener(
    (authUser: any) => {
      localStorage.setItem('authUser', JSON.stringify(authUser));
      setState(authUser);
    },
    () => {
      localStorage.removeItem('authUser');
      setState(null);
    }
  );
};

应该转换为React Hook。干杯!

0

useEffect 需要作为第二个参数传入一个依赖数组,否则它会在每次渲染时都运行。由于您正在更新状态,这只会导致重复执行。

useEffect(() => {
  listener();
  return () => {
    listener();
  };
}, []);

文档

您可以使用像Runaway Effects或ESLint这样的工具进行静态分析,以便事先找出这些问题。


谢谢。那是我的最初想法,但我收到了这个警告 Line 37:8: React Hook useEffect has a missing dependency: 'listener'. Either include it or remove the dependency array react-hooks/exhaustive-deps - Darren
如果我将listener添加到依赖项中,就会得到“listener”函数使useEffect Hook(位于第37行)的依赖项在每次渲染时更改。将其移动到useEffect回调内部。或者,将“listener”定义包装到自己的useCallback()Hook中react-hooks/exhaustive-deps - Darren
ESLint 认为 useEffect 中使用的所有内容都可能会发生变化并引入错误,只需将 listener 添加到依赖项数组中即可。您可以在文档中找到更多信息。 - Agney
是的,这就是我决定的。原因是listener被重复使用并从另一个文件中提取。将其添加到useEffect中并删除依赖关系似乎已经起作用了。现在正在测试。 - Darren

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接