子组件不可用的 React 上下文的异步值问题
Issue with async value for React context not available from child component
我正在尝试使用从 js promise 填充的 React 上下文。
我遇到的问题是,使用上下文的子组件(即 AnotherPage
)在上下文中的值可从根 [=13= 中调用的 useEffect
获得之前呈现] 页。因此,如果我尝试访问其中一个上下文属性,我将收到错误消息,因为它是 undefined/null
.
我如何 refactor/redesign 我的应用程序才能从子组件获得上下文?
这是我的应用程序的代码:
import React from 'react';
import { createContext, useEffect, useState, useContext } from 'react';
import './style.css';
const MyContext = createContext(null);
export default function App() {
console.log('App');
const [myObj, setMyObj] = useState();
useEffect(() => {
Promise.resolve({ id: 42, text: 'some value from promise' }).then((res) => {
console.log('resolved: ', res);
setMyObj(res);
});
}, []);
return (
<MyContext.Provider value={myObj}>
<div>
<h1>Hello StackBlitz!</h1>
<AnotherPage />
</div>
</MyContext.Provider>
);
}
const AnotherPage = () => {
console.log('AnotherPage');
const obj = useContext(MyContext);
console.log('myObj', obj);// undefined/null at first page render...
return <div>Hello from another page</div>;
};
这里是 Stackblitz:https://stackblitz.com/edit/react-xkxhxk?file=src/App.js
仅供参考,在我的真实应用程序中,从承诺中解析的实际对象比下面的 myObj
复杂得多。
您可以使用一些临时值而不是null来初始化上下文。
而不是做
const MyContext = createContext(null);
做
const MyContext = createContext({id: -1, text: 'yet to be initialized'});
因此您的子组件将使用这些值,直到 promise 更新上下文值。
我会这样解决:
const ASYNC_STATUS = {
Idle: 'idle',
Pending: 'pending',
Success: 'success',
Failed: 'failed',
};
export default function App() {
console.log('App');
const [myObj, setMyObj] = useState({
value: null,
status: ASYNC_STATUS.Idle,
});
useEffect(() => {
try {
setMyObj((prev) => ({
...prev,
status: ASYNC_STATUS.Pending,
}));
Promise.resolve({ id: 42, text: 'some value from promise' }).then(
(res) => {
console.log('resolved: ', res);
setMyObj({
value: res,
status: ASYNC_STATUS.Success,
});
}
);
} catch {
setMyObj((prev) => ({
...prev,
status: ASYNC_STATUS.Success,
}));
}
}, []);
return (
<MyContext.Provider value={myObj}>
<div>
<h1>Hello StackBlitz!</h1>
<AnotherPage />
</div>
</MyContext.Provider>
);
}
const AnotherPage = () => {
console.log('AnotherPage');
const { value, status } = useContext(MyContext);
console.log('myObj', value);
return (
<div>
{status === ASYNC_STATUS.Pending && <div>Loading...</div>}
{status === ASYNC_STATUS.Failed && <div>Failed!</div>}
{status === ASYNC_STATUS.Success && value && <div>here what you want to show</div>}
</div>
);
};
你无法逃避它,你必须在某处检查是否为空:
- 在每个消费者 (
useContext
) 用户中,最简单的方法是编写自定义挂钩并检查那里的可空性。
- 或者,在提供者内部,有条件地呈现子项
请注意,这不是“首次渲染”问题,因为这是一个承诺,您可以将其延迟足够长的时间,以便在首次渲染后更新。
export default function App() {
const [myObj, setMyObj] = useState();
useEffect(() => {
Promise.resolve({ id: 42, text: 'some value from promise' }).then((res) => {
setTimeout(() => {
setMyObj(res);
}, 2000);
});
}, []);
const children = useMemo(
() => (
<div>
<h1>Hello StackBlitz!</h1>
<AnotherPage />
</div>
),
[]
);
return (
<MyContext.Provider value={myObj}>{myObj && children}</MyContext.Provider>
);
}
我认为您可以在 promise 解决之前使用加载器。
这样子组件甚至不会在 promise 解析之前被渲染。
import React from 'react';
import { createContext, useEffect, useState, useContext } from 'react';
import './style.css';
const MyContext = createContext(null);
export default function App() {
console.log('App');
const [myObj, setMyObj] = useState();
useEffect(() => {
Promise.resolve({ id: 42, text: 'some value from promise' }).then((res) => {
console.log('resolved: ', res);
setMyObj(res);
});
}, []);
return (
<MyContext.Provider value={myObj}>
{
myObj ? <div>
<h1>Hello StackBlitz!</h1>
<AnotherPage />
</div> : <div>Loading....</div>
}
</MyContext.Provider>
);
}
const AnotherPage = () => {
console.log('AnotherPage');
const obj = useContext(MyContext);
console.log('myObj', obj);// undefined/null at first page render...
return <div>Hello from another page</div>;
};
我正在尝试使用从 js promise 填充的 React 上下文。
我遇到的问题是,使用上下文的子组件(即 AnotherPage
)在上下文中的值可从根 [=13= 中调用的 useEffect
获得之前呈现] 页。因此,如果我尝试访问其中一个上下文属性,我将收到错误消息,因为它是 undefined/null
.
我如何 refactor/redesign 我的应用程序才能从子组件获得上下文?
这是我的应用程序的代码:
import React from 'react';
import { createContext, useEffect, useState, useContext } from 'react';
import './style.css';
const MyContext = createContext(null);
export default function App() {
console.log('App');
const [myObj, setMyObj] = useState();
useEffect(() => {
Promise.resolve({ id: 42, text: 'some value from promise' }).then((res) => {
console.log('resolved: ', res);
setMyObj(res);
});
}, []);
return (
<MyContext.Provider value={myObj}>
<div>
<h1>Hello StackBlitz!</h1>
<AnotherPage />
</div>
</MyContext.Provider>
);
}
const AnotherPage = () => {
console.log('AnotherPage');
const obj = useContext(MyContext);
console.log('myObj', obj);// undefined/null at first page render...
return <div>Hello from another page</div>;
};
这里是 Stackblitz:https://stackblitz.com/edit/react-xkxhxk?file=src/App.js
仅供参考,在我的真实应用程序中,从承诺中解析的实际对象比下面的 myObj
复杂得多。
您可以使用一些临时值而不是null来初始化上下文。
而不是做
const MyContext = createContext(null);
做
const MyContext = createContext({id: -1, text: 'yet to be initialized'});
因此您的子组件将使用这些值,直到 promise 更新上下文值。
我会这样解决:
const ASYNC_STATUS = {
Idle: 'idle',
Pending: 'pending',
Success: 'success',
Failed: 'failed',
};
export default function App() {
console.log('App');
const [myObj, setMyObj] = useState({
value: null,
status: ASYNC_STATUS.Idle,
});
useEffect(() => {
try {
setMyObj((prev) => ({
...prev,
status: ASYNC_STATUS.Pending,
}));
Promise.resolve({ id: 42, text: 'some value from promise' }).then(
(res) => {
console.log('resolved: ', res);
setMyObj({
value: res,
status: ASYNC_STATUS.Success,
});
}
);
} catch {
setMyObj((prev) => ({
...prev,
status: ASYNC_STATUS.Success,
}));
}
}, []);
return (
<MyContext.Provider value={myObj}>
<div>
<h1>Hello StackBlitz!</h1>
<AnotherPage />
</div>
</MyContext.Provider>
);
}
const AnotherPage = () => {
console.log('AnotherPage');
const { value, status } = useContext(MyContext);
console.log('myObj', value);
return (
<div>
{status === ASYNC_STATUS.Pending && <div>Loading...</div>}
{status === ASYNC_STATUS.Failed && <div>Failed!</div>}
{status === ASYNC_STATUS.Success && value && <div>here what you want to show</div>}
</div>
);
};
你无法逃避它,你必须在某处检查是否为空:
- 在每个消费者 (
useContext
) 用户中,最简单的方法是编写自定义挂钩并检查那里的可空性。 - 或者,在提供者内部,有条件地呈现子项
请注意,这不是“首次渲染”问题,因为这是一个承诺,您可以将其延迟足够长的时间,以便在首次渲染后更新。
export default function App() {
const [myObj, setMyObj] = useState();
useEffect(() => {
Promise.resolve({ id: 42, text: 'some value from promise' }).then((res) => {
setTimeout(() => {
setMyObj(res);
}, 2000);
});
}, []);
const children = useMemo(
() => (
<div>
<h1>Hello StackBlitz!</h1>
<AnotherPage />
</div>
),
[]
);
return (
<MyContext.Provider value={myObj}>{myObj && children}</MyContext.Provider>
);
}
我认为您可以在 promise 解决之前使用加载器。 这样子组件甚至不会在 promise 解析之前被渲染。
import React from 'react';
import { createContext, useEffect, useState, useContext } from 'react';
import './style.css';
const MyContext = createContext(null);
export default function App() {
console.log('App');
const [myObj, setMyObj] = useState();
useEffect(() => {
Promise.resolve({ id: 42, text: 'some value from promise' }).then((res) => {
console.log('resolved: ', res);
setMyObj(res);
});
}, []);
return (
<MyContext.Provider value={myObj}>
{
myObj ? <div>
<h1>Hello StackBlitz!</h1>
<AnotherPage />
</div> : <div>Loading....</div>
}
</MyContext.Provider>
);
}
const AnotherPage = () => {
console.log('AnotherPage');
const obj = useContext(MyContext);
console.log('myObj', obj);// undefined/null at first page render...
return <div>Hello from another page</div>;
};