使用 React Router v6 从 Saga 重定向到路由

Redirect to Route from Saga using React Router v6

我正在使用 redux saga 和 react router v6,我想重定向到我的一个 sagas 的路由,有没有办法做到这一点?

有多种选择


1 - 发送导航方法作为调度的 redux 操作的一部分

// component
const navigate = useNavigate()
dispatch({type: FOO, navigate})

// saga
yield takeEvery(FOO, function*(action) {
  action.navigate('/foo')
})

优点:

  • 您正在使用 react-router 团队推荐的 navigate 方法
  • API不太可能改变

缺点

  • 您只能在收到此类操作的特定 sagas 中访问导航方法
  • 您的操作中有不可序列化的数据

2 - 另一种选择是以某种方式存储 navigate 方法。例如。您可以创建一个虚拟反应组件,它将通过 useNavigate 挂钩获取导航方法,然后将其存储在某个“全局”变量中。请参阅此 SO 答案以获取可能的解决方案:

这解决了之前解决方案的缺点,但仍然存在一些问题:

  • 在访问导航方法之前,您需要至少渲染一次 React 树
  • 您通过引入虚拟组件
  • 为您的视图层增加了non-view复杂性

3 - 还有另一种解决方案,类似于我们在 react-router5 中使用的解决方案,它处理了先前解决方案中的问题。就是使用history对象。它没有记录,因为它不稳定,但是有一个 HistoryRouter 实现作为 react-router-dom 包的一部分。参见 https://github.com/remix-run/react-router/releases/tag/v6.1.1

import {unstable_HistoryRouter as HistoryRouter} from 'react-router-dom'
import { createBrowserHistory } from "history";
const history = createBrowserHistory()

// saga setup
sagaMiddleware.runSaga(rootSaga, history);

// react
<HistoryRouter history={history} />

此解决方案的问题是它不稳定,因为它可能与某些 React 18 功能有关。就我个人而言,我更喜欢它,因为它解决了所有其他问题,并且一旦 React 18 实际发布并且我们知道它们是什么,我们就可以处理它。

我的解决方案

// "HistoryRouter" implementation
import * as React from 'react'
import type {BrowserHistory} from 'history'
import {Router} from 'react-router-dom'

export interface HistoryRouterProps {
    history: BrowserHistory
    basename?: string
    children?: React.ReactNode
}

export function HistoryRouter({
    basename,
    children,
    history,
}: HistoryRouterProps) {
let [state, setState] = React.useState({
    action: history.action,
    location: history.location,
})

React.useLayoutEffect(() => history.listen(setState), [history])

return (
    <Router
         basename={basename}
         children={children}
         location={state.location}
         navigationType={state.action}
         navigator={history}
   />
)
}


// Use
import {createBrowserHistory} from 'history'
export const history = createBrowserHistory()

ReactDOM.render(
   <HistoryRouter history={history}>
       <Routes>
           <Route path="login" element={<LoginComponent />} />
           <Route path="register" element={<RegisterComponent />} />
           <Route path="*" element={<HomeComponent />} />
      </Routes>
   </HistoryRouter>
, root)

// call history push in file saga
function* fetchLoginSaga(action: FetchLoginRequest) {
    try {
         yield call(history.push, "/home")
    } catch (e: any) {
    
    }
}