在 React 中,根据初始获取的结果获取数据

Posted

技术标签:

【中文标题】在 React 中,根据初始获取的结果获取数据【英文标题】:In React, fetch data conditional on results of an initial fetch 【发布时间】:2020-12-25 07:39:33 【问题描述】:

我们编写了一个自定义数据获取钩子useInternalApi,它类似于useDataApi 最底部的fairly decent tutorial 钩子,用于使用 React 钩子获取数据。我们的应用会获取大量体育数据,特别是,我们正在尝试为我们的用例找出正确的数据获取模式,这非常简单:

获取特定实体的一般信息(例如 NCAA 会议) 使用随该实体返回的信息(特定会议中团队的团队 ID 数组),并获取数组中每个团队的信息。

为此,我们的代码将如下所示:

import `useInternalApi` from '../path-to-hooks/useInternalApi';
// import React... and other stuff

function ComponentThatWantsTeamInfo( conferenceId ) 
    // use data fetching hook
    const [conferenceInfo, isLoading1, isError1] = useInternalApi('conferenceInfo',  conferenceId: conferenceId )

    // once conferenceInfo loads, then load info from all teams in the conference
    if (conferenceInfo && conferenceInfo.teamsArray) 
        const [teamInfos, isLoading2, isError2] = useInternalApi('teamInfo',  teamIds: conferenceInfo.teamIds )
    

在上面的例子中,conferenceId 是一个整数,teamIds 是一个整数数组,useInternalApi 函数的两个参数的组合创建了一个唯一的端点 url 来从中获取数据。目前存在的两个主要问题是:

    我们的 useInternalApi 钩子在 if 语句中被调用,这在 #1 钩子规则中是不允许的。 useInternalApi 当前构建为仅对特定端点进行一次提取。目前,它无法处理上述的一组 teamId。

什么是正确的数据获取模式?理想情况下,teamInfos 将是一个对象,其中每个键都是会议中一个团队的teamId。特别是,是否更好:

    创建一个新的内部挂钩,它可以处理一个团队 ID 数组,将进行 10 到 20 次提取(或根据teamsArray 的长度所需的次数),并将使用 Promise.all() 返回综合结果。 保持 useInternalApi 挂钩不变,只需调用它 10 到 20 次,每个团队一次。

编辑

我不确定是否需要 useInternalApi 的底层代码来回答这个问题。我尽量避免创建很长的帖子,但在这种情况下,代码可能很重要:

const useInternalApi = (endpoint, config) => 
    // Set Data-Fetching State
    const [data, setData] = useState(null);
    const [isLoading, setIsLoading] = useState(true);
    const [isError, setIsError] = useState(false);

    // Use in lieu of useEffect
    useDeepCompareEffect(() => 
        // Token/Source should be created before "fetchData"
        let source = axios.CancelToken.source();
        let isMounted = true;

        // Create Function that makes Axios requests
        const fetchData = async () => 
            // Set States + Try To Fetch
            setIsError(false);
            setIsLoading(true);
            try 
                const url = createUrl(endpoint, config);
                const result = await axios.get(url,  cancelToken: source.token );
                if (isMounted) 
                    setData(result.data);
                
             catch (error) 
                if (isMounted) 
                    setIsError(true);
                
             finally 
                if (isMounted) 
                    setIsLoading(false);
                
            
        ;

        // Call Function
        fetchData();

        // Cancel Request / Prevent State Updates (Memory Leaks) in cleanup function
        return () => 
            isMounted = false; // set to false to prevent state updates / memory leaks
            source.cancel(); // and cancel the http request as well because why not
        ;
    , [endpoint, config]);

    // Return as length-3 array
    return [data, isLoading, isError];
;

【问题讨论】:

丢弃 if 语句并根据conferenceInfo && conferenceInfo.teamsArray 条件中断钩子 - 如果条件失败,则将 return 语句放在钩子的第一行。或者更确切地说,无论您做什么,都不要调用 api 10 到 20 次 【参考方案1】:

在我看来,如果你需要有条件地使用钩子,你应该在一个单独的组件中使用那个钩子,然后有条件地渲染那个组件。

我的理解,如果我错了,请纠正我,是初始 API 调用返回一个 id 数组,您需要根据该 id 获取每个团队的数据吗?

我会这样做。


import `useInternalApi` from '../path-to-hooks/useInternalApi';
// import React... and other stuff

function ComponentThatDisplaysASpecificTeam(props)
    const teamId = props.teamId;
    const [teamInfo] = useInternalApi('teamInfo',  teamId );

    if(! teamInfo)
        return <p>Loading...</p>
    

    return <p>do something with teamInfo...</p>



function ComponentThatWantsTeamInfo( conferenceId ) 
    // use data fetching hook
    const [conferenceInfo, isLoading1, isError1] = useInternalApi('conferenceInfo',  conferenceId: conferenceId )

    if (! conferenceInfo || ! conferenceInfo.teamsArray) 
        return <p>this is either a loading or an error, you probably know better than me.</p>
    

    // Let the data for each team be handled by its own component. This also lets you not have to use Promise.all
    return (
        <div>
            conferenceInfo.teamIds.map(teamId => ( 
                <ComponentThatDisplaysASpecificTeam teamId=teamId />
            ))
        </div>
    )


【讨论】:

这看起来很棒!只是为了确认一下,这不被认为是一种“绕过在某种情况下调用钩子的骇人听闻的方法”吗?乍一看,这种方法似乎没有什么问题。 据我所知。有条件地渲染组件没有任何问题。

以上是关于在 React 中,根据初始获取的结果获取数据的主要内容,如果未能解决你的问题,请参考以下文章

在 apollo-react 问题中默认获取更多分页或仅网络策略

在 React Redux 应用程序中,我在哪里从服务器获取初始数据?

(通用 React + redux + react-router)如何避免在初始浏览器加载时重新获取路由数据?

React 获取数据丢失

ReferenceError:在初始化React Collapse Component之前无法访问词法声明'useStyles',axios获取数据材料ui useStyles

React 和 Socket.io:能够获取初始数据 - 但是当有新帖子出现时视图不会更新