有没有办法在react-router-dom的loader函数中使用react-redux的dispatch?

huangapple go评论57阅读模式
英文:

Is there a way to use react-redux dispatch inside the loader function of react-router-dom?

问题

我正在使用 react-router-dom@6.8.1redux-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: &quot;/&quot;,
    errorElement: &lt;Error /&gt;,
    children: [
      {
        index: true,
        element: &lt;Home /&gt;,
        loader: async () =&gt; {
          try {
            const response = await axios({
              method: &quot;get&quot;,
              url: &quot;some URL here&quot;,
            });
            // State update should happen here using dispatch. **********
            return response;
          } catch (e: any) {
            throw json(
              { message: &quot;Error occured while fetching data&quot; },
              { 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 &#39;react-router-dom&#39;;
import { store } from &#39;../path/to/store&#39;;

export const homeLoader async () =&gt; {
  try {
    const response = await axios({
      method: &quot;get&quot;,
      url: &quot;some URL here&quot;,
    });

    // State update should happen here using dispatch.
    store.dispatch(stateUpdateAction(/* payload */));

    return response;
  } catch (e: any) {
    throw json(
      { message: &quot;Error occured while fetching data&quot; },
      { status: e.status }
    );
  }
}
const router = createBrowserRouter([
  {
    path: &quot;/&quot;,
    errorElement: &lt;Error /&gt;,
    children: [
      {
        index: true,
        element: &lt;Home /&gt;,
        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: &quot;/&quot;,
      errorElement: &lt;Error /&gt;,
      children: [
        {
          index: true,
          element: &lt;Home /&gt;,
          loader: async () =&gt; {
            try {
              const response = await axios({
                method: &quot;get&quot;,
                url: &quot;some URL here&quot;,
              });
              dispatch({ type: &quot;Something&quot; });
              return response;
            } catch (e: any) {
              throw json({ message: &quot;Error occured while fetching data&quot; }, { status: e.status });
            }
          },
        },
      ],
    },
  ]);
  return &lt;RouterProvider router={router} /&gt;;
}

// As of React 18
const root = ReactDOM.createRoot(document.getElementById(&quot;root&quot;));
root.render(
  &lt;Provider store={store}&gt;
    &lt;Index /&gt;
  &lt;/Provider&gt;
);

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) =&gt; async () =&gt; {
  try {
    const response = await axios({
      method: &quot;get&quot;,
      url: &quot;some URL here&quot;,
    });
    dispatch({ type: &quot;Something&quot; });
    return response;
  } catch (e: any) {
    throw json({ message: &quot;Error occured while fetching data&quot; }, { status: e.status });
  }
};
function Index() {
  const dispatch = useDispatch();
  const router = createBrowserRouter([
    {
      path: &quot;/&quot;,
      errorElement: &lt;Error /&gt;,
      children: [
        {
          index: true,
          element: &lt;Home /&gt;,
          loader: loaderGetter(dispatch),
        },
      ],
    },
  ]);
  return &lt;RouterProvider router={router} /&gt;;
}

// As of React 18
const root = ReactDOM.createRoot(document.getElementById(&quot;root&quot;));
root.render(
  &lt;Provider store={store}&gt;
    &lt;Index /&gt;
  &lt;/Provider&gt;
);

huangapple
  • 本文由 发表于 2023年2月8日 16:27:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/75383036.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定