React Native替换Dummy DB为API数据:类型错误求助
我完全懂你的困扰——作为React Native和JS新手,想把现成模板里的本地Dummy数据换成PHP API返回的JSON,但因为异步请求的特性,直接导出Promise导致类型不匹配,没法无缝兼容原来的组件。下面给你一步步的解决方案,保证和原Dummy DB的导出效果完全一致:
问题根源
原来的Dummy DB是同步导出数组(比如DoctorModel[]),组件可以直接导入后调用pop/push/map等数组方法;而fetch是异步操作,返回的是Promise,如果直接导出异步函数,类型就变成了() => Promise<any>,自然缺少数组的属性和方法,导致类型错误。
解决方案:预加载缓存 + 同步导出
核心思路是:在App启动时预加载API数据并缓存,然后通过getter属性导出和原Dummy同名的变量,让组件可以像之前一样直接导入使用,无需修改现有代码。
步骤1:创建API数据模块(替换原Dummy文件)
假设原Dummy文件是dummyData.ts,现在新建apiData.ts:
// 导入你的Model类型 import { DoctorModel, TreatmentModel } from './path-to-your-models'; // 1. 封装API请求并做类型校验 const fetchDoctors = async (): Promise<DoctorModel[]> => { try { const response = await fetch('https://your-php-api-domain/doctors-api'); if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`); const rawData = await response.json(); // 可选但推荐:用类型校验库(如Zod)确保数据符合Model结构 // 示例Zod校验(需先安装zod:npm install zod) // const DoctorSchema = z.object({ id: z.string(), name: z.string(), ... }); // return z.array(DoctorSchema).parse(rawData); // 基础类型断言(如果暂时不用校验库) return rawData as DoctorModel[]; } catch (err) { console.error('获取医生数据失败:', err); // 降级到空数组或原Dummy数据,避免App崩溃 return []; } }; const fetchTreatments = async (): Promise<TreatmentModel[]> => { // 和fetchDoctors逻辑一致,替换API地址和类型 const response = await fetch('https://your-php-api-domain/treatments-api'); if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`); const rawData = await response.json(); return rawData as TreatmentModel[]; }; // 2. 缓存加载后的数组数据 let cachedDoctors: DoctorModel[] | null = null; let cachedTreatments: TreatmentModel[] | null = null; // 3. 导出初始化函数,在App启动时调用 export const initApiData = async () => { try { // 并行请求多个API,提升加载速度 [cachedDoctors, cachedTreatments] = await Promise.all([ fetchDoctors(), fetchTreatments(), ]); } catch (err) { console.error('初始化API数据失败:', err); // 降级处理 cachedDoctors = []; cachedTreatments = []; } }; // 4. 用getter属性导出和原Dummy同名的变量,确保返回数组类型 export const get doctors(): DoctorModel[] { if (!cachedDoctors) { throw new Error('请先调用initApiData()初始化API数据!'); } return cachedDoctors; } export const get treatments(): TreatmentModel[] { if (!cachedTreatments) { throw new Error('请先调用initApiData()初始化API数据!'); } return cachedTreatments; }
步骤2:在App入口初始化数据
打开你的根组件(比如App.tsx),在组件挂载时调用初始化函数,处理加载状态:
import { useState, useEffect } from 'react'; import { ActivityIndicator, Text, View } from 'react-native'; import { initApiData } from './apiData'; // 导入原来的组件 import YourMainScreen from './path-to-your-main-screen'; const App = () => { const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState<string | null>(null); useEffect(() => { const loadData = async () => { try { await initApiData(); } catch (err) { setError(err instanceof Error ? err.message : '未知错误'); } finally { setIsLoading(false); } }; loadData(); }, []); // 加载中显示加载指示器 if (isLoading) { return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <ActivityIndicator size="large" color="#007AFF" /> </View> ); } // 错误处理 if (error) { return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <Text>加载失败:{error}</Text> </View> ); } // 数据加载完成后渲染原App内容 return <YourMainScreen />; }; export default App;
步骤3:无缝替换原Dummy导入
原来的组件里如果是这样导入的:
import { doctors, treatments } from './dummyData';
现在只需要把路径改成新的apiData.ts:
import { doctors, treatments } from './apiData';
之后你就可以像之前一样直接使用doctors.map(...)、treatments.push(...)等数组操作,完全兼容原来的代码!
可选优化:类型安全增强
如果想更严谨地保证API返回的数据符合你的Model类型,推荐使用Zod或io-ts这类运行时类型校验库,避免后端返回格式错误导致的类型问题。比如用Zod定义Schema后,在fetchDoctors里用z.array(DoctorSchema).parse(rawData)来校验数据,不符合结构会直接抛出错误,方便你快速定位问题。
内容的提问来源于stack exchange,提问作者Daniel Faulkner

