reactjs - Context 的 Provider 默认值函数被调用,而不是 Provider 内部的函数
问题描述
我遇到了一个问题,即调用 Context 的默认值,而不是 Provider 中的函数。
由于 Typescript,它需要一个定义,undefined
并不是一个理想的解决方案。
interface ContextProps {
error: ApiError | null;
errorUI: React.ReactElement | null;
handleError: (value: ApiError | null) => void;
}
export const ErrorContext = React.createContext<ContextProps>({
error: null,
handleError: (_err: ApiError | null) => { console.log('hi'); }, // <<-- this function gets triggered, not the one inside the Provider
});
const ErrorProvider: FC = ({ children }) => {
const [error, setError] = useState<ApiError | null>(null);
const handleError = (err: ApiError) => setError(err);
return (
<ErrorContext.Provider value={{ error, handleError }}>
{children}
</ErrorContext.Provider>
);
}
// HOOK:
const useDisplayError = () => {
const { handleError } = useContext(ErrorContext);
return useCallback(
(error) => {
handleError(error);
},
[handleError]
);
};
以下是应用程序的布局方式以供使用:
ReactDOM.render(
<Router history={history}>
<AppProviders>
<App />
</AppProviders>
</Router>,
document.getElementById('root'));
const AppProviders: FC = ({ children }) => {
return (
<AuthProvider>
<ThemeProvider theme={theme}>
<GlobalStyle />
<LoadingProvider>
<ErrorProvider>{children}</ErrorProvider>
</LoadingProvider>
</ThemeProvider>
</AuthProvider>
);
};
const App: FC = () => {
const { token } = useAuth();
return token ? <Authenticated /> : <Unauthenticated />;
};
const Unauthenticated: FC = () => {
return <Login />;
};
// 组件消费 Hook
const Login: FC = () => {
const { login } = useAuth();
const displayError = useDisplayError();
const handleSubmit = (event: MouseEvent) => {
event.preventDefault();
setStatus(Status.LOADING);
try {
login({ email, password });
} catch (e) {
displayError(e); // hook gets called here
}
};
return <div />
};
钩子中的函数handleError
被钩子本身的消费组件正确调用。但是,被调用的函数从Provider 本身内部的非 handleError
函数映射回函数的版本。createContext
因此,console.log
会调用而不是从 Provider 内部更新状态所需的操作。为什么是这样?
解决方案
如果 Context 不起作用,请检查您的提供者的顺序!
此命令有效:
const AppProviders: FC = ({ children }) => {
return (
<ThemeProvider theme={theme}>
<GlobalStyle />
<LoadingProvider>
<ErrorProvider>
<AuthProvider>{children}</AuthProvider>
</ErrorProvider>
</LoadingProvider>
</ThemeProvider>
);
};