使用react router,我可以使用Link元素来创建由react route本地处理的链接。
我看到它在内部调用this.context.transitionTo(…)。
我想做导航。不是来自链接,而是来自下拉选择(例如)。我如何在代码中执行此操作?this.context是什么?
我看到了导航混合,但我可以在没有混合的情况下这样做吗?
使用react router,我可以使用Link元素来创建由react route本地处理的链接。
我看到它在内部调用this.context.transitionTo(…)。
我想做导航。不是来自链接,而是来自下拉选择(例如)。我如何在代码中执行此操作?this.context是什么?
我看到了导航混合,但我可以在没有混合的情况下这样做吗?
当前回答
更新:2022:使用useNavigate的React Router v6.6.1
useHistory()钩子现已弃用。如果您使用的是React Router 6,编程导航的正确方法如下:
import { useNavigate } from "react-router-dom";
function HomeButton() {
const navigate = useNavigate();
function handleClick() {
navigate("/home");
}
return (
<button type="button" onClick={handleClick}>
Go home
</button>
);
}
带挂钩的React Router v5.1.0
如果您使用的是React>16.8.0和功能组件,则React Router>5.1.0中有一个新的useHistory钩子。
import { useHistory } from "react-router-dom";
function HomeButton() {
const history = useHistory();
function handleClick() {
history.push("/home");
}
return (
<button type="button" onClick={handleClick}>
Go home
</button>
);
}
反应路由器v4
使用React Router的v4,有三种方法可以用于组件内的编程路由。
使用withRouter高阶组件。使用合成并渲染<Route>使用上下文。
React Router主要是历史库的包装器。历史记录处理与浏览器窗口的交互。历史记录为您提供浏览器和哈希历史记录。它还提供了一个内存历史,对于没有全局历史的环境非常有用。这在使用Node进行移动应用程序开发(react native)和单元测试时特别有用。
历史记录实例有两种导航方法:推送和替换。如果您将历史记录视为一个访问位置数组,push将向数组中添加一个新位置,replace将用新位置替换数组中的当前位置。通常,您在导航时需要使用push方法。
在React Router的早期版本中,您必须创建自己的历史实例,但在v4中,<BrowserRouter>、<HashRouter>和<MemoryRouter>组件将为您创建浏览器、哈希和内存实例。React Router使与路由器关联的历史实例的财产和方法通过路由器对象下的上下文可用。
1.使用withRouter高阶组件
withRouter高阶组件将注入历史对象作为组件的属性。这允许您访问push和replace方法,而不必处理上下文。
import { withRouter } from 'react-router-dom'
// this also works with react-router-native
const Button = withRouter(({ history }) => (
<button
type='button'
onClick={() => { history.push('/new-location') }}
>
Click Me!
</button>
))
2.使用合成并渲染<Route>
<Route>组件不仅仅用于匹配位置。您可以渲染无路径路线,它将始终与当前位置匹配。<Route>组件传递与withRouter相同的属性,因此您可以通过历史属性访问历史方法。
import { Route } from 'react-router-dom'
const Button = () => (
<Route render={({ history}) => (
<button
type='button'
onClick={() => { history.push('/new-location') }}
>
Click Me!
</button>
)} />
)
3.使用上下文*
但你可能不应该
最后一个选项是只有当您觉得使用React的上下文模型时才应该使用的选项(React的context API在v16中是稳定的)。
const Button = (props, context) => (
<button
type='button'
onClick={() => {
// context.history.push === history.push
context.history.push('/new-location')
}}
>
Click Me!
</button>
)
// you need to specify the context type so that it
// is available within the component
Button.contextTypes = {
history: React.PropTypes.shape({
push: React.PropTypes.func.isRequired
})
}
1和2是实现的最简单的选择,因此对于大多数用例来说,它们是最好的选择。
其他回答
布局/BaseLayout.jsx
import { Outlet } from "react-router-dom";
import Navbar from "../components/Navbar";
const BaseLayout = () => {
return(
<div>
<Navbar/>
<Outlet/>
</div>
)
}
export default BaseLayout
路由器/index.jsx
import { createBrowserRouter} from "react-router-dom";
import BaseLayout from "../layouts/BaseLayout";
import HomePage from "../views/HomePage";
import Menu from "../components/Menu"
import Detail from "../components/Detail";
const router = createBrowserRouter([
{
element: <BaseLayout/>,
children:[
{
path: "/",
element: <Menu />,
},
{
path: '/:id',
element: <Detail/>
}
]
},
])
export default router
存储/actionType.js
export const FETCH_DATA_FOODS = "food/setFood"
export const FETCH_DATA_FOODS_DETAILS = "food/setDetailFood"
存储/还原器/还原器.js
import { FETCH_DATA_FOODS, FETCH_DATA_FOODS_DETAILS } from "../actionType";
const initialState = {
foods:[],
detailFood:{}
};
const foodReducer = (state = initialState, action) => {
switch(action.type){
case FETCH_DATA_FOODS:
return{
...state,
foods: action.payload
}
case FETCH_DATA_FOODS_DETAILS:
return{
...state,
detailFood: action.payload
}
default:
return state
}
}
export default foodReducer
存储/actionCreator
import { FETCH_DATA_FOODS, FETCH_DATA_FOODS_DETAILS } from "./actionType";
// import { FETCH_DATA_FOODS } from "../actionType";
export const actionFoodSetFoods = (payload) => {
return {
type: FETCH_DATA_FOODS,
payload,
};
};
export const actionDetailSetDetailFood = (payload) => {
return {
type: FETCH_DATA_FOODS_DETAILS,
payload,
};
};
export const fetchDataFoods = () => {
return (dispatch, getState) => {
fetch("https://maxxkafe.foxhub.space/users")
.then((response) => {
if (!response.ok) {
throw new Error("notOk");
}
return response.json();
})
.then((data) => {
// dispatcher({
// type: "food/setFood",
// payload: data
// })
dispatch(actionFoodSetFoods(data));
});
};
};
export const fetchDetailDataFood = (id) => {
return (dispatch, getState) => {
console.log(id);
fetch(`https://maxxkafe.foxhub.space/users/${id}`)
.then((response) => {
if (!response.ok) {
throw new Error("gaOkNich");
}
console.log(response, ",,,,,,,,");
return response.json();
})
.then((data) => {
dispatch(actionDetailSetDetailFood(data));
});
};
};
stores/index.js
import { legacy_createStore as createStore, combineReducers, applyMiddleware } from 'redux'
import foodReducer from './reducers/foodReducer'
import thunk from "redux-thunk"
const rootReducer = combineReducers({
foods: foodReducer
});
const store = createStore(rootReducer, applyMiddleware(thunk));
export default store
应用程序.js
import { RouterProvider } from "react-router-dom";
import router from "./routers";
import { Provider } from "react-redux";
import store from "./stores";
const App = () => {
return (
<Provider store={store}>
<RouterProvider router={router} />
</Provider>
);
};
export default App;
组件/类别.jsx
import { useEffect, useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { Link } from "react-router-dom";
import { fetchDataCategories } from "../stores/actionCreate";
import RowCategory from "../views/rowTableCategory";
const Category = () => {
// const [categories, setCategories] = useState([])
const { categories } = useSelector((state) => state.categories);
const dispatcher = useDispatch();
useEffect(() => {
// fetch("http://localhost:3003/categories")
// .then((response) => {
// if(!response.ok){
// throw new Error ("gaOkNich")
// }
// return response.json()
// })
// .then((data) => {
// setCategories(data)
// })
dispatcher(fetchDataCategories());
}, []);
return (
<section className="mt-12">
<div className="flex mr-20 mb-4">
<Link to={'/add-Form'} className="flex ml-auto text-white bg-red-500 border-0 py-2 px-6 focus:outline-none hover:bg-red-600 rounded">
Create Category
</Link>
</div>
<div className="overflow-hidden rounded-lg border border-gray-200 shadow-md m-5">
<table className="w-full border-collapse bg-white text-left text-sm text-gray-500">
<thead className="bg-gray-50">
<tr>
<th scope="col" className="px-6 py-4 font-medium text-gray-900">
Name
</th>
<th scope="col" className="px-6 py-4 font-medium text-gray-900">
Created At
</th>
<th scope="col" className="px-6 py-4 font-medium text-gray-900">
Updated At
</th>
<th
scope="col"
className="px-6 py-4 font-medium text-gray-900"
></th>
</tr>
</thead>
<tbody className="divide-y divide-gray-100 border-t border-gray-100">
{categories.map((el) => {
return <RowCategory key={el.id} el={el} />;
})}
</tbody>
</table>
</div>
</section>
);
};
export default Category;
组件/login.jsx
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { useDispatch } from "react-redux";
import { fetchDataFoods, login } from "../stores/actionCreate";
const Login = () => {
const input = {
email: "",
password: "",
};
const [values, setValues] = useState(input);
// const [password, setPassword] = useState('')
// const {admin} = useSelector((state) => state.admin)
const dispatcher = useDispatch();
const movePage = useNavigate();
const handleChange = (event) => {
const { name, value } = event.target;
setValues({
...values,
[name]: value,
});
console.log(value);
};
const handleLogin = async (event) => {
event.preventDefault();
try {
await dispatcher(login(values));
await dispatcher(fetchDataFoods());
movePage("/home");
} catch (error) {
console.log(error);
}
};
return (
<section className="font-mono bg-white-400 mt-[10rem]">
<div className="container mx-auto">
<div className="flex justify-center px-6 my-12">
<div className="w-full xl:w-3/4 lg:w-11/12 flex justify-center">
<div className="w-full lg:w-7/12 bg-white p-5 rounded-lg lg:rounded-l-none">
<h3 className="pt-4 text-2xl text-center">Login Your Account!</h3>
<form
className="px-8 pt-6 pb-8 mb-4 bg-white rounded"
onSubmit={handleLogin}
>
<div className="mb-4">
<label
className="block mb-2 text-sm font-bold text-gray-700"
htmlFor="email"
>
Email
</label>
<input
className="w-full px-3 py-2 mb-3 text-sm leading-tight text-gray-700 border rounded shadow appearance-none focus:outline-none focus:shadow-outline"
id="email"
type="email"
name="email"
placeholder="Email"
// onChange={(event) => setValues({email: event.target.value})}
onChange={handleChange}
value={values.email.email}
/>
</div>
<div className="mb-4 md:flex md:justify-between">
<div className="mb-4 md:mr-2 md:mb-0">
<label
className="block mb-2 text-sm font-bold text-gray-700"
htmlFor="password"
>
Password
</label>
<input
className="w-full px-3 py-2 mb-3 text-sm leading-tight text-gray-700 border rounded shadow appearance-none focus:outline-none focus:shadow-outline"
id="password"
type="password"
name="password"
placeholder="Password"
onChange={handleChange}
value={values.password}
// onChange={(event) => setValues({password: event.target.value})}
/>
</div>
</div>
<div className="mb-4 md:flex md:justify-between"></div>
<div className="mb-6 text-center">
<button
className="w-full px-4 py-2 font-bold text-white bg-blue-500 rounded-full hover:bg-blue-700 focus:outline-none focus:shadow-outline"
type="submit"
>
Login
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</section>
);
};
export default Login;
在React Router v4中实现这一点时面临问题的人。
这里有一个从redux操作导航到React应用程序的工作解决方案。
文件history.js
import createHistory from 'history/createBrowserHistory'
export default createHistory()
文件App.js/Route.jsx
import { Router, Route } from 'react-router-dom'
import history from './history'
...
<Router history={history}>
<Route path="/test" component={Test}/>
</Router>
文件*另一个_File.js或redux文件
import history from './history'
history.push('/test') // This should change the URL and rerender Test component
感谢GitHub上的评论:ReactTraining问题评论
对于已经使用React Router v6的用户,可以使用React Router提供的useNavigate钩子来完成。
使用此钩子进行导航非常简单:
import { generatePath, useNavigate } from 'react-router';
navigate(-1); // navigates back
navigate('/my/path'); // navigates to a specific path
navigate(generatePath('my/path/:id', { id: 1 })); // navigates to a dynamic path, generatePath is very useful for url replacements
要将withRouter与基于类的组件一起使用,请尝试以下操作。不要忘记将导出语句更改为与Router一起使用:
从“react router dom”导入{withRouter}
class YourClass extends React.Component {
yourFunction = () => {
doSomeAsyncAction(() =>
this.props.history.push('/other_location')
)
}
render() {
return (
<div>
<Form onSubmit={ this.yourFunction } />
</div>
)
}
}
export default withRouter(YourClass);
在react router的上下文中,已经有大量的答案可以正确、准确地回答这个问题,但我想花点时间指出一些我在其他答案中没有提到的问题。
这是一种stackoverflow的比喻,建议用与原始问题不同的方式来做一些事情,但在这种情况下,由于问题的年龄,我觉得在当前的时间里,问题的表述会有所不同。
我建议放弃react router,只需通过window.location.href=[yoururlhere]推送到本地浏览器位置,然后通过正则表达式(或确切的)匹配window.location.pathname和您的预期路径来检查路径。
更简单的答案通常是最好的答案,在这种情况下,React Router的用例由本地位置对象API提供更好的服务。