使用 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) {
}
}
我正在使用 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) {
}
}