React Native TypeScript组件动态导入JSON文件报错求助
React Native + TypeScript 动态导入JSON文件报错解决方案
问题根源
React Native的Metro打包器依赖静态编译分析确定打包资源范围。你使用的完全动态导入import(../../../assets/data/topics/${reference})无法被Metro静态解析——打包器无法提前预判reference对应的具体文件,因此抛出Invalid call错误,哪怕路径实际正确。
解决方法
方法1:缩小动态导入范围(推荐)
将基础路径固定,仅保留子路径/文件名作为动态部分,让Metro能扫描到目标目录下的所有文件并提前打包。同时注意JSON模块的默认导入在module.default中:
import React, { useEffect, useState } from "react"; import { View, Text } from "react-native"; import styles from "./styles"; type Props = { reference: string; // 示例:units/top-1-sec-1-unit-1.json }; type Block = { blockId: string; blockName: string; }; const BlockComponent = ({ reference }: Props) => { const [unit, setUnit] = useState<Block[]>([]); useEffect(() => { const loadData = async () => { try { // 固定基础路径,动态部分为reference const module = await import(`../../../assets/data/topics/${reference}`); // JSON文件默认导出在module.default中 setUnit(module.default); } catch (err) { console.error("加载数据失败:", err); } }; loadData(); }, [reference]); return ( <View style={styles.container}> {unit.map((block) => ( <View key={block.blockId} style={styles.blockBox}> <Text style={styles.boxText}>{block.blockName}</Text> </View> ))} </View> ); }; export default BlockComponent;
补充配置:如果Metro未自动识别JSON文件,在metro.config.js中添加:
module.exports = { resolver: { sourceExts: ['js', 'jsx', 'json', 'ts', 'tsx'], }, };
方法2:静态映射表(适合文件数量固定的场景)
提前导入所有可能用到的JSON文件,创建键为reference值的映射表,彻底规避动态导入问题:
import React, { useEffect, useState } from "react"; import { View, Text } from "react-native"; import styles from "./styles"; // 预导入所有JSON文件 import unit1 from "../../../assets/data/topics/units/top-1-sec-1-unit-1.json"; import unit2 from "../../../assets/data/topics/units/top-1-sec-1-unit-2.json"; // 按需添加更多文件... type Props = { reference: string; }; type Block = { blockId: string; blockName: string; }; // 建立reference与对应数据的映射 const DATA_MAP: Record<string, Block[]> = { "units/top-1-sec-1-unit-1.json": unit1, "units/top-1-sec-1-unit-2.json": unit2, }; const BlockComponent = ({ reference }: Props) => { const [unit, setUnit] = useState<Block[]>([]); useEffect(() => { const targetData = DATA_MAP[reference]; if (targetData) { setUnit(targetData); } else { console.error("未找到对应reference的数据:", reference); } }, [reference]); return ( <View style={styles.container}> {unit.map((block) => ( <View key={block.blockId} style={styles.blockBox}> <Text style={styles.boxText}>{block.blockName}</Text> </View> ))} </View> ); }; export default BlockComponent;
这种方式性能稳定,适合JSON文件数量有限且不会频繁新增的场景。
方法3:通过Asset系统加载
利用React Native的Asset系统加载JSON资源,适合大量动态资源的场景:
- 配置
react-native.config.js:
module.exports = { assets: ["./assets/data/topics/"], };
- 执行资源链接(RN 0.60+可跳过,但建议执行):
npx react-native link
- 修改组件代码:
import React, { useEffect, useState } from "react"; import { View, Text, Asset } from "react-native"; import styles from "./styles"; type Props = { reference: string; }; type Block = { blockId: string; blockName: string; }; const BlockComponent = ({ reference }: Props) => { const [unit, setUnit] = useState<Block[]>([]); useEffect(() => { const loadData = async () => { try { const asset = Asset.fromModule(`../../../assets/data/topics/${reference}`); await asset.downloadAsync(); const response = await fetch(asset.uri); const data = await response.json(); setUnit(data); } catch (err) { console.error("加载数据失败:", err); } }; loadData(); }, [reference]); return ( <View style={styles.container}> {unit.map((block) => ( <View key={block.blockId} style={styles.blockBox}> <Text style={styles.boxText}>{block.blockName}</Text> </View> ))} </View> ); }; export default BlockComponent;
内容的提问来源于stack exchange,提问作者Takodana
相关产品推荐
相关产品推荐

