代码之家  ›  专栏  ›  技术社区  ›  Gate Study

React自定义钩子问题

  •  0
  • Gate Study  · 技术社区  · 11 月前

    我是React的新手。我在react中创建了一个自定义钩子,用于从api获取数据。下面是代码。

    import { useState, useEffect } from 'react';
    import useAxiosInstance from './useAxiosInstance';
    
    const useGetApi = (initialurl = null, initialparams = {}) => {
      const [data, setData] = useState(null);
      const [error, setError] = useState(null);
      const [url, setUrl] = useState(initialurl);
      const [params, setParams] = useState(initialparams);
      const [loading, setLoading] = useState(false);
      const [refreshKey, setRefreshKey] = useState(true); // used bool state to refresh the data due to simplicity.
      const axiosInstance = useAxiosInstance();
    
      useEffect(() => {
        if (!url) return;
        const fetchData = async () => {
          setLoading(true);
          try {
            const response = await axiosInstance.get(url, {params});
            setData(response.data);
            setLoading(false);
    
          } catch (err) {
            setError({
              message: err.response?.data?.message || err.message || 'An unexpected error occurred',
              details: err.response?.data || null,
            });
          } finally {
            setLoading(false);
          }
        };
    
    
        fetchData();
      }, [url, JSON.stringify(params), refreshKey]);
    
      const triggerRefresh = () => setRefreshKey(prev=>!prev);
    
      return { data, error, loading, setUrl, setParams, triggerRefresh };
    };
    
    export default useGetApi;
    

    这样做的问题是,即使将更新加载到false,数据仍然保持在初始状态(null)。更新数据变量需要一些时间吗?如何修复此问题,以便在加载再次变为false之前更新数据?

    在我使用这个钩子的组件中,如果我尝试下面的操作,它就不起作用了,数据最初是空的!

    //this fails
    import React from 'react';
    import useGetApi from '../hooks/requestMethods/useGetApi';
    
    const PostList = () => {
      // Initialize the useGetApi hook with the API URL
      const { data, error, loading, triggerRefresh } = useGetApi('https://jsonplaceholder.typicode.com/posts');
    
      // Handle loading state
      if (loading) {
        return <p>Loading posts...</p>;
      }
    
      // Handle error state
      if (error) {
        return <p>Error fetching posts: {error.message}</p>;
      }
    
      // Render the list of posts
      return (
        <div>
          <h1>Posts</h1>
          <button onClick={triggerRefresh} disabled={loading}>
            {loading ? 'Refreshing...' : 'Refresh Posts'}
          </button>
          <ul>
            { data.map(post => (
              <li key={post.id}>
                <h2>{post.title}</h2>
                <p>{post.body}</p>
              </li>
            ))}
          </ul>
        </div>
      );
    };
    
    export default PostList;
    

    错误:

    PostList.jsx:26 
     Uncaught TypeError: Cannot read properties of null (reading 'map')
        at PostList (PostList.jsx:26:16)
    @react-refresh:247 
     The above error occurred in the <PostList> component:
    
        at PostList (http://localhost:5175/src/pages/PostList.jsx?t=1735830812705:22:52)
        at RenderedRoute (http://localhost:5175/node_modules/.vite/deps/react-router-dom.js?v=8d379189:4069:5)
        at Routes (http://localhost:5175/node_modules/.vite/deps/react-router-dom.js?v=8d379189:4539:5)
        at div
        at Router (http://localhost:5175/node_modules/.vite/deps/react-router-dom.js?v=8d379189:4482:15)
        at BrowserRouter (http://localhost:5175/node_modules/.vite/deps/react-router-dom.js?v=8d379189:5228:5)
        at Provider (http://localhost:5175/node_modules/.vite/deps/chunk-6DOCI2SE.js?v=8d379189:1097:3)
        at App
    
    Consider adding an error boundary to your tree to customize error handling behavior.
    Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
    
    1 回复  |  直到 11 月前
        1
  •  -1
  •   WeDoTheBest4You    11 月前

    代码已经设置好了。条件属性访问将改进代码。请参阅下面的改进代码版本。

    {data?.map((post) => (
      <li key={post.id}>
         <h2>{post.title}</h2>
          <p>{post.body}</p>
       </li>
     ))}
    

    这里讨论了提出条件属性访问的原因。

    让我们逐步了解渲染过程:

    1. 在应用程序的初始渲染时。
    2. 调用自定义钩子。
    3. 它返回加载false和数据null。
    4. 由于加载为false,它跳过其立即返回。
    5. 渲染到JSX,它访问数据的map方法。

    ****由于数据现在为空,它最终将出错****

    请注意,useEffect是一个在组件渲染过程中不会被调用的事件。它仅在渲染后立即被调用。

    这意味着只有在返回JSX后才会调用useEffect。由于渲染时未执行useEffect,因此加载将保持为false。因此,由于数据为空,访问数据映射的JSX将失败。条件属性访问将保护访问并避免错误。

    现在,当初始渲染之后是useEffect时,在useEffect中调用的状态设置器将触发另一个渲染。这是第二次渲染。在此渲染过程中,加载将为真,如果没有错误,数据将有值。因此,条件访问将通过测试,它将继续执行映射功能,并成功准备JSX。 第二次渲染之后还将进行另一次useEffect,然后依赖性检查将阻止它运行其中的代码。

    请查看示例代码,以说明使用useEffect的操作顺序。

    App.js

    import React from 'react';
    
    export default function App() {
      const [someState, setSomeState] = React.useState();
    
      console.log('Rendering the component');
      React.useEffect(() => {
        console.log('Running the useEffect code');
        if (!someState) {
          setSomeState('X');
        }
      }, [someState]);
    
      return 'Please check console log to see the Rendering sequences';
    }
    

    试运行

    加载应用程序时生成的控制台日志

    // Rendering the component
    // Running the useEffect code
    // Rendering the component
    // Running the useEffect code