【问题标题】:Confused about useEffect对 useEffect 感到困惑
【发布时间】:2019-12-30 06:02:27
【问题描述】:

我正在构建我的第一个自定义 React Hook,但我对我认为代码的一个简单方面感到困惑:

export const useFetch = (url, options) => {
  const [data, setData] = useState();
  const [loading, setLoading] = useState(true);
  const { app } = useContext(AppContext);
  console.log('** Inside useFetch: options = ', options);

  useEffect(() => {
    console.log('**** Inside useEffect: options = ', options);
    const fetchData = async function() {
      try {
        setLoading(true);
        const response = await axios.get(url, options);
        if (response.status === 200) {
          setData(response.data);
        }
      } catch (error) {
        throw error;
      } finally {
        setLoading(false);
      }
    };
    fetchData();
  }, []);

  return { loading, data };
};

我将两个参数传递给 useFetch:一个 url 和一个 headers 对象,其中包含如下所示的 AWS Cognito 授权密钥:Authorization: eyJraWQiOiJVNW...(为简洁起见)

当我这样做时,options 上的对象确实存在于 useFetch 附近,但在 useEffect 构造中它是空的。然而,url 字符串在两种情况下都正确填充。

这对我来说毫无意义。可能有人知道为什么会这样吗?

【问题讨论】:

  • 我相信这是一个异步计时问题。我在useEffect 中添加了options 作为依赖项,现在它似乎可以工作了。也就是说,现在有 2 次调用 API 端点,第一次调用返回 401 错误。我想我可以在异步函数调用周围放置一个 if ... then 构造,尽管这本质上看起来是错误的。
  • 我的代码底部现在看起来像这样:``` if (options) { fetchData(); }; }, [选项]); ``` 但是,该函数被父组件调用了两次。我还不知道这是因为我一直在讨论的代码中的缺陷还是父组件中的某些问题。
  • 正如我在下面的回答中所解释的,我很确定问题来自使用此挂钩的组件,而不是挂钩本身。您能否提供更多代码来展示您如何使用它?

标签: javascript react-hooks use-effect


【解决方案1】:

下面的代码实现显示它按预期工作。

async/await 已转换为 Promise,但应该具有相同的行为。

“Inside use fetch”输出3次:

  1. 安装上 (useEffect(()=>..., [])
  2. 第一次状态更改后 (setLoading(true))
  3. 第二次状态改变后 (setLoading(false))

“内部使用效果”在mount时输出1次(useEffect(()=>..., []

由于它不适合您,这可能意味着当组件安装时,选项尚不可用。

当你说当你把选项作为依赖项时,你确认了这一点,useEffect 被调用了两次,第一次获取失败(很可能是因为缺少选项)。

我很确定您会发现使用自定义挂钩的组件父项中的选项存在问题。

const axios = {
    get: (url, options) => {
      return new Promise(resolve => setTimeout(() => resolve({ status: 200, data: 'Hello World' }), 2000));
    }
  };
  
  const AppContext = React.createContext({ app: null });
  
  const useFetch = (url, options) => {
    const [data, setData] = React.useState();
    const [loading, setLoading] = React.useState(true);
    const { app } = React.useContext(AppContext);
    console.log('** Inside useFetch: options = ', JSON.stringify(options));
  
    React.useEffect(() => {
      console.log('**** Inside useEffect: options = ', JSON.stringify(options));
      const fetchData = function () {
          setLoading(true);
          const response = axios.get(url, options)
            .then(response => {
              if (response.status === 200) {
                setData(response.data);
              }
              setLoading(false);
            })
            .catch(error => {
              setLoading(false);
              throw error;
            });

      };
      fetchData();
    }, []);
  
    return { loading, data };
  };
  
  const App = ({url, options}) => {
    const { loading, data } = useFetch(url, options);
    return (
      <div
          style={{
              display: 'flex', background: 'red',
              fontSize: '20px', fontWeight: 'bold',
              justifyContent: 'center', alignItems: 'center',
              width: 300, height: 60, margin: 5
          }}
      >
        {loading ? 'Loading...' : data}
      </div>
    );
  };
  
  ReactDOM.render(
    <App
        url="https://www.dummy-url.com"
        options={{ headers: { Authorization: 'eyJraWQiOiJVNW...' } }}
    />,
    document.getElementById('root')
  );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root" />

【讨论】:

  • 非常感谢您的回复!因此options 填充了一个AWS Access Token 标头字符串,该字符串在此处为每个会话永久存储:const { appStore } = useContext(AppContext);useEffect 构造上方我这样做:``` var options; if (appStore.awsConfig) { 选项 = appStore.awsConfig; } ``` 为什么appStore.awsConfig 值不能立即可用,我仍然感到困惑......但事实并非如此! 似乎上下文 API 数据存储具有异步性质,这是我没有预料到的。
  • 我不认为上下文是异步的。这可能是因为会话在更高的祖先挂载时被填充。一个组件在他的所有子组件都挂载后挂载,所以当使用你的钩子挂载的组件时,他的祖先还没有挂载....
猜你喜欢
  • 1970-01-01
  • 2019-09-13
  • 2012-07-22
  • 2013-05-13
  • 2020-04-16
  • 2023-03-08
  • 2019-08-04
  • 2022-01-20
  • 2016-09-05
相关资源
最近更新 更多