在上下文中反应悬念



那些天我必须为我的react应用程序解决一个问题,我有我所有的数据显示在json中,这些都是由express API提供的使用FS of node来读取json并根据传入的路径返回它们。

真正的问题来了,当我试图呈现我的组件,组件是无用的,没有JSON信息,所以我必须一直等到我得到那些。 我的第一个想法是同步调用APIXMLHttpRequest,但我在文档中看到它是弃用。所以我阅读了悬念和过渡的新功能但我不理解它以及将它们放入我的DataContext。我将与你分享所有我认为相关的代码,以便让你看到我到达的地方:
// jsonLoader.js
// Here I try to get the data with XMLHttpRequest, I replaced to sync to async
export const loadJSON = async (path) => {
const json = await readFile(path, 'application/json');
return json ? JSON.parse(json) : undefined;
};
const readFile = async (path, mimeType) =>
new Promise((resolve) => {
const xmlHttp = new XMLHttpRequest();
xmlHttp.open('GET', path, true);
if (!!mimeType && xmlHttp.overrideMimeType) xmlHttp.overrideMimeType(mimeType);
xmlHttp.send();
if (xmlHttp.status == 200 && xmlHttp.readyState == 4) resolve(xmlHttp.responseText);
else return resolve(undefined);
});

然后我在我的DataContext中使用该模块:

// DataContext.js
// I'm trying to serve a "useQuery" called "getData" in order to fetch the API
let data = {};
const index = (obj, path) => path.split('.').reduce((o, i) => o[i], obj);
const setter = (obj, path, value) => {
if (typeof path == 'string') return setter(obj, path.split('.'), value);
else if (path.length == 1 && value !== undefined) return (obj[path[0]] = value);
else if (path.length == 0) return obj;
else return setter(obj[path[0]], path.slice(1), value);
};
const DataContextInstance = createContext({
getData: async (...paths) => ({}),
getTranslations: () => ({}),
});
export const DataContext = ({dataLoaded, children}) => {
if (dataLoaded) data = dataLoaded;
const {lang} = useContext(UserContext);
const [prevLang, setPrevLang] = useState();
const loadData = (path) => {
if (Object.keys(data).length > 0) {
const foundData = index(data, path);
if (foundData?.then) throw foundData;
if (foundData) return data;
}
const filePath = `/data/${path || `translations/${lang}`}.json`;
const json = loadJSON(filePath).then((newData) => (data[path] = newData));
data[path] = json;
};
const getData = (...paths) => {
if (paths.every((p) => index(data, p))) return data;
paths.forEach((p) => loadData(p));
return data;
};
useEffect(() => {
if (lang === prevLang && Object.keys(data).length > 0) return;
if (Object.keys(data).length > 0) return;
loadData();
setPrevLang(lang);
}, [lang, prevLang, setPrevLang, data]);
const contextValue = useMemo(
() => ({getData, getTranslations: () => getData()}),
[data, lang]
);
return (
<DataContextInstance.Provider value={contextValue}>
<Suspense fallback={<span>Loading...</span>}>{children}</Suspense>
</DataContextInstance.Provider>
);
};
export const useDataContext = () => {
const context = useContext(DataContextInstance);
if (!context) throw new Error('Context must be used within a Provider');
return context;
};

然后,我使用"getData"在我的组件中,为了获得所需的数据对于那个:

// NavBar.js
// Here I use my hook to get the DataContext context and get the "getData" func
function NavBar() {
const {getData} = useDataContext();
const {pages} = getData('menu').menu;
[...]

正如你所看到的,我在每个组件中指定了我想要的json,以避免加载所有组件,所以我有"data"变量在我的DataContext作为"缓存",所以如果它被加载,我只需返回它.

我的问题是我不能让它工作,它进入了一个循环的电话,从来没有悬念(我认为)。

编辑:我成功捕获了一个错误日志:

Warning: Cannot update a component (`DataContext`) while rendering a different component (`AppBase`). To locate the bad setState() call inside `AppBase`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render

JSX结构为:

<DataContext dataLoaded={data}>
<AppBase data={data} statusCode={statusCode} />
</DataContext>

我通过添加以下行来解决它,在添加data[path] = json(插入承诺)之后,我不得不抛出承诺为了告诉React它正在加载一个承诺:

[...]
const filePath = `/data/${path || `translations/${lang}`}.json`;
if (!path) path = lang;
const json = loadJSON(filePath).then((newData) => (data[path] = newData));
data[path] = json;
if (data[path]?.then) throw data[path];
[...]

我遵循了CSS技巧中的说明。

谢谢大家的帮助!

相关内容

  • 没有找到相关文章

最新更新