繁体   English   中英

在上下文中反应悬念

[英]React Suspense within a Context

那些日子我不得不为我的反应应用程序解决一个问题,我的所有数据都以 JSON 格式显示,这些数据由使用节点 FS 的快速 API提供服务,以便读取这些 JSON 并根据您的路径返回它们重新传入。

当我尝试渲染我的组件时,真正的问题出现了,如果没有 JSON 信息,这些组件将毫无用处,所以我必须一直等到我得到这些信息。

我的第一个想法是使用XMLHttpRequest同步调用 API ,但我在文档中看到它已被弃用 因此,我阅读了Suspense 和 Transitions的新功能,但我不理解它以及将它们放入我的 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,以避免加载所有组件,因此我在 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 Tricks的说明进行操作。

谢谢大家的帮助!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM