英文:
Is there a way to use react-redux dispatch inside the loader function of react-router-dom?
问题
我正在使用 react-router-dom@6.8.1
与 redux-toolkit
。我遇到了一个典型的情况,我需要在后端调用中获取数据(这是在RRD提供的加载器函数内部发生的)。
获取数据后,我需要在加载器函数内部更新Redux状态。(这样,我就可以避免在我的函数组件内部使用另一个useEffect
)。以下是一个代码片段,帮助你更好地理解。
const router = createBrowserRouter([
{
path: "/",
errorElement: <Error />,
children: [
{
index: true,
element: <Home />,
loader: async () => {
try {
const response = await axios({
method: "get",
url: "这里是某个URL",
});
// 状态更新应该在这里使用dispatch发生。 **********
return response;
} catch (e: any) {
throw json(
{ message: "在获取数据时发生错误" },
{ status: e.status }
);
}
},
},
],
},
]);
由于我们不能在React函数组件或自定义钩子之外使用useDispatch
,我想知道是否有实现这个的方法。
如有需要,我乐意提供更多细节,比如package.json等。
英文:
I am using react-router-dom@6.8.1
along with redux-toolkit
. I have a typical scenario where I'm making a backend call to fetch data (this is happening inside a loader function offered by RRD).
Once I fetch the data, I need to update the redux state inside the loader function. (This way, I can avoid another useEffect
inside my function component). Here is a code snippet to give you a better understanding.
const router = createBrowserRouter([
{
path: "/",
errorElement: <Error />,
children: [
{
index: true,
element: <Home />,
loader: async () => {
try {
const response = await axios({
method: "get",
url: "some URL here",
});
// State update should happen here using dispatch. **********
return response;
} catch (e: any) {
throw json(
{ message: "Error occured while fetching data" },
{ status: e.status }
);
}
},
},
],
},
]);
Since we cannot use useDispatch
outside of the React function components or custom hooks, I am wondering if there is any way to achieve this.
I am glad to provide any more details like package.json etc, upon requirement.
答案1
得分: 8
如果您不想渲染一个可以使用 useDispatch
hook 并将 dispatch
作为参数传递的父/包装组件,您可以直接导入已实例化的 store
对象并分发动作到 store。
示例:
import { json } from 'react-router-dom';
import { store } from '../path/to/store';
export const homeLoader = async () => {
try {
const response = await axios({
method: "get",
url: "some URL here",
});
// 在此处使用 dispatch 进行状态更新。
store.dispatch(stateUpdateAction(/* 负载 */));
return response;
} catch (e: any) {
throw json(
{ message: "在获取数据时发生错误" },
{ status: e.status }
);
}
}
const router = createBrowserRouter([
{
path: "/",
errorElement: <Error />,
children: [
{
index: true,
element: <Home />,
loader: homeLoader,
},
],
},
]);
英文:
If you don't want to render a parent/wrapper component that can use the useDispatch
hook and pass dispatch
down as an argument you can instead import the instantiated store
object and dispatch actions directly to the store.
Example:
import { json } from 'react-router-dom';
import { store } from '../path/to/store';
export const homeLoader async () => {
try {
const response = await axios({
method: "get",
url: "some URL here",
});
// State update should happen here using dispatch.
store.dispatch(stateUpdateAction(/* payload */));
return response;
} catch (e: any) {
throw json(
{ message: "Error occured while fetching data" },
{ status: e.status }
);
}
}
const router = createBrowserRouter([
{
path: "/",
errorElement: <Error />,
children: [
{
index: true,
element: <Home />,
loader: homeLoader,
},
],
},
]);
答案2
得分: 7
你可以通过将路由的渲染包装在一个组件内来实现这一点。例如,可以在index.js
中这样做:
function Index() {
const dispatch = useDispatch();
const router = createBrowserRouter([
{
path: "/",
errorElement: <Error />,
children: [
{
index: true,
element: <Home />,
loader: async () => {
try {
const response = await axios({
method: "get",
url: "some URL here",
});
dispatch({ type: "Something" });
return response;
} catch (e) {
throw json({ message: "Error occurred while fetching data" }, { status: e.status });
}
},
},
],
},
]);
return <RouterProvider router={router} />;
}
// 截止到 React 18
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Provider store={store}>
<Index />
</Provider>
);
如果你想将dispatch
作为参数传递给不同文件中的加载器,你可以使用一个返回React Router Dom所需加载器的函数,例如:
const loaderGetter = (dispatch) => async () => {
try {
const response = await axios({
method: "get",
url: "some URL here",
});
dispatch({ type: "Something" });
return response;
} catch (e) {
throw json({ message: "Error occurred while fetching data" }, { status: e.status });
}
};
然后在Index
函数中使用它:
function Index() {
const dispatch = useDispatch();
const router = createBrowserRouter([
{
path: "/",
errorElement: <Error />,
children: [
{
index: true,
element: <Home />,
loader: loaderGetter(dispatch),
},
],
},
]);
return <RouterProvider router={router} />;
}
// 截止到 React 18
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Provider store={store}>
<Index />
</Provider>
);
英文:
You can do that by wrapping the rendering of your routes inside a component. For example, something like so in index.js
:
function Index() {
const dispatch = useDispatch();
const router = createBrowserRouter([
{
path: "/",
errorElement: <Error />,
children: [
{
index: true,
element: <Home />,
loader: async () => {
try {
const response = await axios({
method: "get",
url: "some URL here",
});
dispatch({ type: "Something" });
return response;
} catch (e: any) {
throw json({ message: "Error occured while fetching data" }, { status: e.status });
}
},
},
],
},
]);
return <RouterProvider router={router} />;
}
// As of React 18
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Provider store={store}>
<Index />
</Provider>
);
If you want to pass dispatch
as a parameter to a loader in a different file, you could use a function that returns the loader that React Router Dom expects, like so, for example:
const loadereGetter = (dispatch) => async () => {
try {
const response = await axios({
method: "get",
url: "some URL here",
});
dispatch({ type: "Something" });
return response;
} catch (e: any) {
throw json({ message: "Error occured while fetching data" }, { status: e.status });
}
};
function Index() {
const dispatch = useDispatch();
const router = createBrowserRouter([
{
path: "/",
errorElement: <Error />,
children: [
{
index: true,
element: <Home />,
loader: loaderGetter(dispatch),
},
],
},
]);
return <RouterProvider router={router} />;
}
// As of React 18
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Provider store={store}>
<Index />
</Provider>
);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论