React中如何使用useEffect内API请求获取的notebooks变量?
如何在React组件中使用从API获取的notebooks列表
核心问题分析
- 你直接给普通变量
notebooks赋值,这不是React状态变量,组件不会因为变量变化触发重新渲染,因此JSX中无法获取更新后的数据。 getAllNotebooks是异步函数,原代码未添加await,拿到的是Promise对象而非实际返回数据,后续的JSON序列化/反序列化操作完全无效。
解决方案步骤
1. 单组件状态管理方案(基础版)
如果仅在单个组件中处理数据,先通过useState定义状态变量存储notebooks数据:
import { useState, useEffect } from 'react'; function NotebookList() { // 定义状态,初始值设为空数组 const [notebooks, setNotebooks] = useState<[Notebook, Withdraw[]][]>([]); // 加载状态,用于显示加载提示 const [isLoading, setIsLoading] = useState(true); // 错误状态 const [error, setError] = useState<string | null>(null); useEffect(() => { // 将useEffect回调封装为async函数 const fetchNotebooks = async () => { try { setIsLoading(true); const idToken = await getIdToken(); // 等待异步函数返回结果 const fetchedNotebooks = await getAllNotebooks(idToken); // 更新状态,触发组件重新渲染 setNotebooks(fetchedNotebooks); setError(null); } catch (err) { setError('加载笔记本数据失败'); console.error(err); } finally { setIsLoading(false); } }; fetchNotebooks(); }, []); // 空依赖数组,确保仅在组件挂载时执行一次 // 在JSX中使用状态数据 if (isLoading) return <div>加载中...</div>; if (error) return <div>错误:{error}</div>; return ( <div> <h2>笔记本列表</h2> <ul> {notebooks.map(([notebook, withdraws], index) => ( <li key={index}> <p>序列号:{notebook.numSerie}</p> <p>状态:{notebook.isActive ? '活跃' : '非活跃'}</p> <p>提取记录:{withdraws.length}条</p> </li> ))} </ul> </div> ); }
2. 结合全局Provider方案(适配你的现有代码)
从你提供的withdraw_provider代码来看,你已在使用React Context做全局状态管理,组件需订阅Context中的状态:
第一步:修正Provider的Context定义
确保你的Context包含完整的状态与方法:
import { createContext, useContext, useState, ReactNode } from 'react'; interface WithdrawContextType { notebooks: [Notebook, Withdraw[]][]; getAllNotebooks: (idToken: string) => Promise<void>; error: string | null; isLoading: boolean; } const WithdrawContext = createContext<WithdrawContextType | undefined>(undefined); export function WithdrawProvider({ children }: { children: ReactNode }) { const [notebooks, setNotebooks] = useState<[Notebook, Withdraw[]][]>([]); const [error, setError] = useState<string | null>(null); const [isLoading, setIsLoading] = useState(false); async function getAllNotebooks(idToken: string){ try { setIsLoading(true); const notebooks = await getAllNotebooksUsecase.execute(idToken); setNotebooks(notebooks); setError(null); } catch (error: any) { console.log(`ERROR PROVIDER: ${error}`); setError(error.message); setNotebooks([]); } finally { setIsLoading(false); } } return ( <WithdrawContext.Provider value={{ notebooks, getAllNotebooks, error, isLoading }}> {children} </WithdrawContext.Provider> ); } // 自定义Hook,简化组件调用Context的逻辑 export function useWithdraw() { const context = useContext(WithdrawContext); if (!context) { throw new Error('useWithdraw必须在WithdrawProvider内部使用'); } return context; }
第二步:在组件中使用Context
import { useEffect } from 'react'; import { useWithdraw } from './path/to/withdraw-provider'; function NotebookList() { const { notebooks, getAllNotebooks, error, isLoading } = useWithdraw(); useEffect(() => { const fetchData = async () => { const idToken = await getIdToken(); await getAllNotebooks(idToken); }; fetchData(); }, [getAllNotebooks]); if (isLoading) return <div>加载中...</div>; if (error) return <div>错误:{error}</div>; return ( <div> <h2>笔记本列表</h2> <ul> {notebooks.map(([notebook, withdraws], idx) => ( <li key={idx}> <strong>序列号:{notebook.numSerie}</strong> <p>是否活跃:{notebook.isActive ? '是' : '否'}</p> <div> <h4>提取记录:</h4> {withdraws.map((withdraw, wIdx) => ( <div key={wIdx}> <p>邮箱:{withdraw.email}</p> <p>提取时间:{new Date(withdraw.withdrawTime).toLocaleString()}</p> </div> ))} </div> </li> ))} </ul> </div> ); }
额外代码修正建议
- 修正请求头错误:
withdraw_repository_http中Authorization头末尾多了一个单引号,应改为Bearer ${idToken},否则会导致授权失败。 - 补全useEffect语法:原useEffect缺少闭合括号,正确写法为
}, []);。 - 修复Provider中的错误逻辑:原代码中
const setError = error; setError(setError);属于重复命名且逻辑错误,改为setError(error.message);即可。
内容的提问来源于stack exchange,提问作者Luigi Trevisan
相关产品推荐
相关产品推荐

