如何在Fetch请求API时设置额外状态?自定义Hook列空数组问题
问题解决:自定义Hook useTable初始化时columns为空的处理
核心问题分析
- 异步请求时序问题:API请求是异步执行的,Hook初始化后会立刻返回初始状态的
columns(你代码里初始为undefined),直到请求完成才会更新columns。 - 代码冗余与潜在风险:
useRef的使用完全多余,直接在useEffect里调用loadDatabase即可。- 未处理API返回空数组的情况,
d[0]会直接导致报错。 data状态从未被更新,Hook返回的data && data[actualcategory]始终是undefined。
修复后的代码
const urls: string[] = [ "https://jsonplaceholder.typicode.com/posts", "https://jsonplaceholder.typicode.com/comments", "https://jsonplaceholder.typicode.com/albums", "https://jsonplaceholder.typicode.com/photos", "https://jsonplaceholder.typicode.com/todos", ]; // 补充类型定义(若未定义) type Column = { col: { title: string; disp: boolean; }; }; type DataItem = Record<string, any>; const useTable = (idurl: number, actualcategory: string) => { // 给columns设置初始空数组,避免返回undefined const [columns, setColumns] = useState<Column[]>([]); const [data, setData] = useState<DataItem[]>([]); // 新增加载/错误状态,方便外部组件处理对应场景 const [isLoading, setIsLoading] = useState<boolean>(true); const [error, setError] = useState<string | null>(null); const loadDatabase = async () => { setIsLoading(true); setError(null); try { const response = await fetch(urls[idurl]); if (!response.ok) throw new Error('请求失败'); const d = await response.json() as DataItem[]; if (d.length === 0) { setColumns([]); setData([]); return; } // 生成columns配置 const newColumns = Object.keys(d[0]).map(title => ({ col: { title, disp: true } })); setColumns(newColumns); setData(d); } catch (err) { setError(err instanceof Error ? err.message : '未知错误'); setColumns([]); } finally { setIsLoading(false); } }; useEffect(() => { loadDatabase(); }, [idurl, actualcategory]); // 补充idurl到依赖项,确保URL变化时重新请求 return { tableData: data, columns, isLoading, error } as const; }; export { useTable };
关键优化点
- 初始值明确化:给
columns初始化为空数组[],确保Hook初始化时返回的是明确的空数组而非undefined。 - 状态补充:新增
isLoading和error状态,让使用Hook的组件能处理加载中、错误场景。 - 依赖项修复:
useEffect依赖数组加上idurl,当请求的URL变化时自动重新发起请求。 - 异常处理:处理HTTP请求失败、返回空数组的情况,避免代码崩溃。
- 冗余代码移除:删掉不必要的
useRef,简化逻辑。
使用示例
const MyTable = () => { const { tableData, columns, isLoading, error } = useTable(0, 'posts'); if (isLoading) return <div>加载中...</div>; if (error) return <div>错误:{error}</div>; return ( <table> <thead> <tr> {columns.map((col, idx) => ( col.col.disp && <th key={idx}>{col.col.title}</th> ))} </tr> </thead> <tbody> {tableData.map((row, rowIdx) => ( <tr key={rowIdx}> {columns.map((col, colIdx) => ( col.col.disp && <td key={colIdx}>{row[col.col.title]}</td> ))} </tr> ))} </tbody> </table> ); };
内容的提问来源于stack exchange,提问作者artur nowakowski
相关产品推荐
相关产品推荐

