【问题标题】:How do deal with React hooks returning stale event handlers如何处理返回陈旧事件处理程序的 React 钩子
【发布时间】:2021-04-07 15:59:45
【问题描述】:

我试图弄清楚如何处理由钩子返回的陈旧事件处理程序。首先,当组件首次呈现时,它会向 api 发出异步请求以获取凭据。然后在对话框中按下提交按钮以创建资源时使用这些凭据。问题是即使在获取凭据之后,对话框提交按钮单击事件处理程序的凭据也未定义。

凭据.js

import { useEffect } from 'react';
import { api } from './api';

export const useCredentials = (setCredentials) => {
  useEffect(() => {
    const asyncGetCredentials = async () => {
      const result = await api.getCredentials();
      if (result) {
        setCredentials(result);
      }
    };
    asyncGetCredentials().then();
  }, []);

  return credentials;
}

useComponent.js

import { useEffect, useRef, useCallback, useState } from 'react';
import { useCredentials } from './credentials';
import { createResource } from './resources';
import { useDialog } from './useDialog';

export const useComponent = () => {
  const { closeDialog } = useDialog();
  const [credentials, setCredentials] = useState();
  useCredentials(setCredentials);

  const credentialsRef = useRef(credentials);

  useEffect(() => {
    // logs credentials properly after they have been fetched
    console.log(credentials) 
    credentialsRef.current = credentials;
  }, [credentials]);

  const createResourceUsingCredentials = useCallback(
    async function () {
      // credentials and credentialsRef.current are both undefined
      // even when the function is called after the credentials
      // have already been fetched.
      console.log(credentials);
      console.log(credentialsRef.current);

      createResource(credentialsRef.current); 
    }, [credentials, credentialsRef, credentialsRef.current]
  );

  const onDialogSubmit = useCallback(
    async function () {
      await createResourceUsingCredentials();
      closeDialog();
    }, [
      credentials,
      credentialsRef,
      credentialsRef.current,
      createResourceUsingCredentials,
    ],
  );

  return {
    onDialogSubmit,
  }
}


【问题讨论】:

    标签: javascript reactjs react-hooks closures


    【解决方案1】:

    试试这个方法

    export const useCredentials = (setCredentials) => { 
    
      useEffect(() => {
        const asyncGetCredentials = async () => {
          const result = await api.getCredentials();
          if (result) {
            setCredentials(result);
          }
        };
        asyncGetCredentials().then();
      }, []); 
    }
    
    
    export const useComponent = () => {
      const { closeDialog } = useDialog();
      const [credentials, setCredentials] = useState(); // new add
      useCredentials(setCredentials); 
    
      ....
    }
    

    【讨论】:

    • 感谢@Nooruddin,我已根据您的建议编辑了问题中的代码,但我仍然在闭包中得到陈旧的未定义值。
    【解决方案2】:

    为什么要增加复杂性,总是返回函数并在函数内部检查credentials

    export const useComponent = () => {
        const { closeDialog } = useDialog();
        const credentials = useCredentials();
    
        // correctly logs undefined at first and updated credentials
        // when they are asynchronously received from the api.
        console.log(credentials);
    
        async function createResourceUsingCredentials() {
            createResource(credentials);
        }
    
        let onClickDialogSubmit = async () => {
            if (credentials) {
                await createResourceUsingCredentials();
                closeDialog();
            }
        };
    
        return {
            onClickDialogSubmit,
        }
    }
    

    【讨论】:

    • 我正在增加复杂性来解决过时的关闭问题。您的解决方案不起作用。
    【解决方案3】:

    我发现问题出在 useCredentials 挂钩实现中。如果请求已经在进行中,它会阻止对 api 凭据的任何进一步请求。由于此功能的执行不力,如果渲染了使用该挂钩的超过 1 个组件,则只有首先渲染的组件才会获得更新的凭据。我更改了 useCredentials 挂钩,以便它订阅全局状态(具有凭据),这样无论哪个组件启动请求,所有组件都会在请求完成时获得凭据。 https://simbathesailor007.medium.com/debug-your-reactjs-hooks-with-ease-159691843c3a 对调试这个问题有很大帮助。

    【讨论】:

      猜你喜欢
      • 2020-02-17
      • 2018-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-17
      • 1970-01-01
      • 2013-12-24
      相关资源
      最近更新 更多