【问题标题】:api data fetching best practice in reactapi数据获取最佳实践反应
【发布时间】:2021-12-24 06:07:59
【问题描述】:

场景:

我有两个 api 调用,它们都对同一个状态有贡献,所以我最初所做的只是在同一个 useEffect 中等待两个 api 调用。但是,其中一个是相当慢的,所以我需要等待很长的时间来渲染页面。

const [loading, setLoading] = useState(true)

  useEffect(async() => {
            try{
               slow_api_call_promise = await slow_api_call
               quick_api_call_promise = await quick_api_call
               setLoading(false)
               let newState = quick_api_call_promise.data.merge(slow_api_call_promise.data)
               setState(newState)
               
            }catch(e){
              setLoading(false) 
              //show error message
            }
            
        },[])
    
    return <React.Fragment>
       {loading ? <SpinnerComponent />: <Actual Page />}

    </React.Fragment>

幸运的是,快速的实际上提供了我初始渲染所需的大部分状态,而慢速的只贡献了部分页面状态。所以为了渲染体验,我把它们分成了两个 useEffect 并为它们设置了不同的加载状态。它似乎工作。但这对我来说看起来很傻,它会渲染两次。有没有更好的方法,一种优化的方法来解决这个问题。

const [loadingWhole, setLoadingWhole] = useState(true)
const [loadingPart, setLoadingPart] = useState(true)

useEffect(async() => {
        quick_api_call_promise = await quick_api_call
        setLoadingWhole(false)
    },[])

    useEffect(async() => {
        slow_api_call_promise = await slow_api_call
        setLoadingPart(false)
    },[])

【问题讨论】:

  • 只是不要 await 缓慢的 api 调用......它仍然会渲染两次,但你的代码会少很多重复
  • "这看起来很傻,它会渲染两次" - 但这正是你想要的?
  • 通知您cannot pass an async function to useEffect anyway。所以是的,只需在一个效果中进行两次调用。
  • @MikeAbeln 我必须选择这种方式,因为较慢的 api 调用大约需要 10 秒,用户不会容忍第一次绘制时间为 10 秒
  • @Bergi 是的,我需要两个 api,而较慢的那个真的很慢。所以第一个 api 将返回我想要的大部分内容,对于第二个 api,我在它试图加载的东西上放了一个加载器,所以从逻辑上讲,会有两个渲染。但只是想知道是否还有其他更清洁的方法,例如保湿,或者将这两种 useEffect 合二为一。

标签: reactjs promise render use-effect race-condition


【解决方案1】:

是的,您可以保留一个效果,只需在您获取快速响应后先执行setState

const [state, setState] = useState(null);
const [loadingWhole, setLoadingWhole] = useState(true);
const [loadingPart, setLoadingPart] = useState(true);

async function fetchResults() {
    const quickResult = await callQuickApi();
    setState(quickResult.data);
    setLoadingPart(false);

    const slowResult = await callSlowApi();
    let newState = merge(quickResult.data, slowResult.data);
    setState(newState);
    setLoadingWhole(false);
}

useEffect(async() => {
    fetchResults().catch(err => {
        setLoadingPart(false);
        setLoadingWhole(false);
        //show error message
    });
},[]);

顺便说一句,您可能想考虑只使用一个而不是 4 个单独的 useState 钩子,它只能处于 5 个状态而不是多种组合(即使 !loadingWhole &amp;&amp; loadingPart 没有意义):

  • 加载中
  • 错误
  • 部分响应 + 加载
  • 部分响应 + 错误
  • 完整回复

【讨论】:

  • 会批量更新吗?
  • 取决于反应版本 iirc。它可能会将setStatesetLoading 一起批处理,也可能不会,这就是我建议使用单一状态的原因。它永远不会批处理await 之前发生的第一次更新以及await 之后发生的第二次更新。
  • 非常感谢!你能解释一下为什么它永远不会用第二次更新来批处理第一次更新吗?我所知道的是反应将在生命周期方法或事件处理程序(反应处理事情)中进行批量更新。或者你能指点我一些文档或示例或源代码吗?
  • 因为在第一个await之后,它不再是react处理事情,你已经离开了生命周期方法。它是运行代码并进行setState 调用的(隐式)承诺处理程序。 (如果您尝试将代码从 async/await 重写为 .then() 语法,可能会更明显。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-03-22
  • 2020-05-13
  • 1970-01-01
  • 2016-07-23
  • 1970-01-01
  • 2018-09-19
  • 2011-12-13
相关资源
最近更新 更多