反应在某种情况下悬念
那些日子,我不得不解决我的React应用程序的问题,我将所有数据显示在JSON中,这些数据由 Express API ,以读取这些JSON并返回它们取决于您经过的路径。
真正的问题是在我尝试渲染组件时,组件没有JSON 信息,所以我必须始终等到我得到那些。
我的第一个想法是用xmlhttprequest
同步地将API称为,但我在文档中看到它是 defboreed 。因此,我阅读了悬念和过渡的新功能,但我不了解它,也不像将它们放入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;
};
然后,我在组件中使用该模块,以获取为此,所需的数据:
// 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,以避免加载所有组件,因此我在我的“数据”上有“数据”变量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>
Those days I had to solve a problem for my react app, I have all my data to be displayed in JSONs, those are served by an express API using FS of node in order to read those JSONs and return them depending on the path you're passing in.
The real problem came when I tried to render my components, the components are useless without that JSON information, so I'll have to always wait till I get those.
My first idea was to call the API synchronously with XMLHttpRequest
but I saw in the docs that it is deprecated. So I read about the new features of Suspense and Transitions but I do not understand it as well as placing them into my DataContext. I'll share with you all code I think is relevant in order to let you see where I reached:
// 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);
});
Then I use that module in my 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;
};
And then, I use that "getData" in my components in order to get the data needed for that one:
// 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;
[...]
As you can see, I specify the json I want in every component in order to avoid loading all of them, so I have the "data" variable in my DataContext as a "cache", so if it is loaded I simply return it.
My problem is that I'm not able to make that work, it gets into a loop of calls and never getting on suspense (I think).
EDIT: I managed to capture an error log:
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
The JSX structure is:
<DataContext dataLoaded={data}>
<AppBase data={data} statusCode={statusCode} />
</DataContext>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在添加
data [path] = JSON
(插入诺言)之后,我已经通过添加以下行进行了解决,我不得不 thr promplot nope 才能告诉React React它正在加载诺言:我遵循 css技巧。
感谢大家的帮助!
I've solved it by adding the following lines, after adding the
data[path] = json
(inserting a promise) I had to throw that promise in order to tell React it is loading a promise:I followed the instructions from CSS Tricks.
Thanks everyone for you help!