【问题标题】:Authentication listeners when refactoring to React hooks重构为 React 钩子时的身份验证侦听器
【发布时间】:2019-04-05 12:08:38
【问题描述】:

在从 React 类重构为 React 钩子时,我在弄清楚如何更改身份验证处理组件时遇到了一些麻烦。

这是我的中的相关代码:

  state = {
    user: null
  }

  componentDidMount() {

    authGetUser(user => {
      if (user !== this.state.user) {
        this.setState({user})
      }
    })
  }

  componentWillUnmount() {

    authUnsubscribe()
  }

  handleAuthClick = () => {

    if (this.state.user) {
      authSignOut()
    } else {
      authSignIn()
    }
  }

这里是 钩子

  const [user, setUser] = useState<firebase.User | null>(null)

  useEffect(() => {
    return authUnsubscribe() // runs on mount and unmount only
  }, [])

  useEffect(() => {
    authGetUser(usr => setUser(usr))
  }, [])

  const handleAuthClick = () => {

    if (user) {
      authSignOut()
    } else {
      authSignIn()
    }
  }

另外,这里是我的其他相关方法:

const authGetUser = (callback: (user: firebase.User | null) => void) => {

  initFirebase()
  authUnsubscribe()
  userUnsubscribe = firebaseAuth.onAuthStateChanged(callback)
}

export const authUnsubscribe = () => {

  if (userUnsubscribe) {
    userUnsubscribe()
  }
}

const authSignIn = () => {

  googleAuth.signIn().then((googleUser: any) => {

    var credential = firebase.auth.GoogleAuthProvider.credential(googleUser.getAuthResponse().id_token)

    firebaseAuth.signInAndRetrieveDataWithCredential(credential)
  })
}

const authSignOut = () => {

  googleAuth
  .signOut()
  .then(firebaseAuth.signOut())
}

这两个例子都有效。但是,当我注销并使用 hooks 版本登录时,我在控制台中收到一条错误消息,提示

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.

这表明清理工作不正确。

是的,我知道我可以继续使用具有有效类的版本。但是我想通过解决这个问题来更好地理解 React 钩子。

有什么想法吗?

【问题讨论】:

  • 您在第一个useEffect 中直接调用authUnsubscribe,但您想返回一个应在卸载时运行的函数。 useEffect(() =&gt; authUnsubscribe, [])
  • 我尝试了你的建议。它仍然可以正常工作,包括控制台中的错误消息...

标签: reactjs firebase-authentication react-hooks


【解决方案1】:

这不适合你吗?您可以使用单个 useEffect()

React Hooks API DOCs

useEffect(
  () => {
    const subscription = props.source.subscribe();
    return () => {
      // Clean up the subscription
      setUser(null); // <--- TRY DOING SOMETHING LIKE THIS
      subscription.unsubscribe();
    };
  },
  [],
); 

清理功能在组件从 UI 中移除之前运行 以防止内存泄漏。此外,如果一个组件呈现多个 次(就像他们通常做的那样),之前的效果会在 执行下一个效果。在我们的示例中,这意味着一个新的 每次更新都会创建订阅。为了避免在 每次更新,请参阅下一节。


如果你想运行一个效果并只清理一次(在挂载和 unmount),您可以传递一个空数组 ([]) 作为第二个参数。这个 告诉 React 你的效果不依赖于 props 的任何值 或状态,因此它永远不需要重新运行。这不作为特殊处理 case — 它直接遵循依赖项数组的方式 有效。

【讨论】:

  • 你的没错,现在useEffect(() =&gt; { authGetUser(usr =&gt; setUser(usr)); return authUnsubscribe; }, []) 更干净了,谢谢。但是,我仍然在控制台中收到错误...
  • 查看我对答案所做的编辑。在退订之前尝试这样做:setUser(null);
【解决方案2】:

通常,当我们有异步请求并且之前卸载组件时会发生这种情况,发生内存泄漏。显然,这不会发生在基于类的组件中,因为我们有componentDidMount()componentWillUnmount() 钩子,所以比useEffect() 更有信心我们已经操纵了状态,所以我认为你需要找出原因应用程序卸载并且有解决方案。

您应该使用一个useEffect() 而不是两个这样的:

useEffect(() => {
  authGetUser(usr => setUser(usr))
  return authUnsubscribe() // runs on mount and unmount only
}, [])

【讨论】:

    猜你喜欢
    • 2012-09-15
    • 2018-10-17
    • 1970-01-01
    • 1970-01-01
    • 2017-11-04
    • 2021-04-26
    • 1970-01-01
    • 2021-05-22
    • 1970-01-01
    相关资源
    最近更新 更多