React Hooks with React Router v4 - 我如何重定向到另一个路由?
Posted
技术标签:
【中文标题】React Hooks with React Router v4 - 我如何重定向到另一个路由?【英文标题】:React Hooks with React Router v4 - how do I redirect to another route? 【发布时间】:2019-07-01 22:43:51 【问题描述】:我有一个简单的反应钩子应用程序 - 待办事项列表 - 带有反应路由器 v4
在待办事项列表中,当点击待办事项时,我需要:
-
在上下文中调度当前的待办事项
重定向到另一条路线(从 /todos 到 /todos/:id)
在之前的基于 React 类的实现中,我可以使用 this.context.history.push 重定向到另一个路由。
我将如何使用 React Hooks 结合 React Router v4 来处理这个问题(在下面的代码中,请参阅我在函数 editRow() 中的注释)?
代码如下:
=====index.js=====
import React from 'react';
import ReactDOM from 'react-dom';
import BrowserRouter from "react-router-dom"
import App from './App';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>, document.getElementById('root'));
=====main.js=====
import React from 'react'
import Switch, Route from 'react-router-dom'
import TodosList from './todoslist'
import TodosEdit from './todosedit'
const Main = () => (
<main>
<Switch>
<Route exact path="/todos" component=TodosList/>
<Route exact path="/todos/:id" component=TodosEdit />
</Switch>
</main>
)
export default Main
=====app.js=====
import React, useContext, useReducer from 'react';
import Main from './main'
import TodosContext from './context'
import todosReducer from './reducer'
const App = () =>
const initialState = useContext(TodosContext);
const [state, dispatch] = useReducer(todosReducer, initialState);
return (
<div>
<TodosContext.Provider value=state, dispatch>
<Main/>
</TodosContext.Provider>
</div>
)
export default App;
=====TodosContext.js=====
import React from 'react'
const TodosContext = React.createContext(
todos: [
id:1, text:'Get Grocery', complete:false,
id:2, text:'Excercise', complete:false,
id:3, text:'Drink Water', complete:true,
],
currentTodo:
)
export default TodosContext
=====reducer.js=====
import React from 'react'
export default function reducer(state, action)
switch(action.type)
case "GET_TODOS":
return
...state,
todos: action.payload
case "SET_CURRENT_TODO":
return
...state,
currentTodo: action.payload
default:
return state
=====Todos.js=====
import React, useState, useContext, useEffect from 'react';
import TodosContext from './context'
function Todos()
const [todo, setTodo] = useState("")
const state, dispatch = useContext(TodosContext)
useEffect(()=>
if(state.currentTodo.text)
setTodo(state.currentTodo.text)
else
setTodo("")
dispatch(
type: "GET_TODOS",
payload: state.todos
)
, [state.currentTodo.id])
const editRow = event =>
let destUrlEdit = `/todos/$event.id`
let obj =
obj.id = event.id
obj.text = event.text
dispatch(type:"SET_CURRENT_TODO", payload: obj)
//after dispatch I would like to redirect to another route to do the actual edit
//destUrlEdit
return(
<div>
<h1>List of ToDos</h1>
<h4>title</h4>
<ul>
state.todos.map(todo => (
<li key=todo.id>todo.text
<button onClick=()=>
editRow(todo)>
</button>
</li>
))
</ul>
</div>
)
export default Todos;
【问题讨论】:
【参考方案1】:您的问题与使用 react-router-v4 而不是钩子以编程方式导航有关,
在 react-router-v4 中,如果 Todos 组件被渲染为子组件或 Route 或从渲染表单 Route 的祖先并将路由器 props 传递给它,您将从 props 获取历史记录。但是它没有接收到Router props,你可以使用react-router
中的withRouter
HOC来获取router props并调用props.history.push(destUrlEdit)
import React, useState, useContext, useEffect from 'react';
import TodosContext from './context'
import withRouter from 'react-router-dom';
function Todos(props)
const [todo, setTodo] = useState("")
const state, dispatch = useContext(TodosContext)
useEffect(()=>
if(state.currentTodo.text)
setTodo(state.currentTodo.text)
else
setTodo("")
dispatch(
type: "GET_TODOS",
payload: state.todos
)
, [state.currentTodo.id])
const editRow = event =>
let destUrlEdit = `/todos/$event.id`
let obj =
obj.id = event.id
obj.text = event.text
dispatch(type:"SET_CURRENT_TODO", payload: obj)
//after dispatch I would like to redirect to another route to do the actual edit
//destUrlEdit
props.history.push(destUrlEdit);
return(
<div>
<h1>List of ToDos</h1>
<h4>title</h4>
<ul>
state.todos.map(todo => (
<li key=todo.id>todo.text
<button onClick=()=>
editRow(todo)>
</button>
</li>
))
</ul>
</div>
)
export default withRouter(Todos);
【讨论】:
感谢您的及时回复!!这为我指明了探索在 React 类组件与 React 功能组件中访问 React Router 道具的正确方向。要添加到您的答案中的一件事是,因为使用 react-redux 和 connected-react-router...
import useDispatch from 'react-redux';
import push from 'connected-react-router';
export default () =>
const dispatch = useDispatch();
return (
<Button onClick=() => dispatch(push('/login'))>
Login
</Button>
);
;
【讨论】:
我收到了错误Uncaught Could not find router reducer in state tree, it must be mounted under "router"
,尽管在我开始使用钩子之前一切都已正确设置。使用组件类的旧方法有效。一旦我切换到这种方法,我就会得到那个错误。【参考方案3】:
它实际上比其他答案简单得多,React Router v5.1 提供了一个useHistory
钩子。
import React from 'react'
import useHistory from 'react-router-dom'
const MyComponent = () =>
const history = useHistory()
const handleButtonClick = (event) =>
history.push(event.target.value)
return (
<button
type="button"
value="/my/path"
onClick=handleButtonClick
>
Navigate Me!
</button>
)
【讨论】:
我认为实际上是 v5.1 引入了这个功能:reacttraining.com/blog/react-router-v5-1 是的,你是对的!我已经更新了答案中的版本。 history.push('/') 仍然会将用户发送到页面的旧迭代,但是,您仍然需要将其放在 useEffect 中并为 useEffect 提供一个变量检查更改,或通过 props.history.push('/') 向您的历史记录提供道具,其中道具被传递到功能组件中。以上是关于React Hooks with React Router v4 - 我如何重定向到另一个路由?的主要内容,如果未能解决你的问题,请参考以下文章
[React] Refactor a Class Component with React hooks to a Function
[React Testing] Test your Custom Hook Module with react-hooks-testing-library
如何在 React with Typescript 中使用和指定通用 Hooks?
React Typescript with hooks:最大更新深度超出错误
[React + GraphQL] Use useLazyQuery to manually execute a query with Apollo React Hooks