使用react router,我可以使用Link元素来创建由react route本地处理的链接。
我看到它在内部调用this.context.transitionTo(…)。
我想做导航。不是来自链接,而是来自下拉选择(例如)。我如何在代码中执行此操作?this.context是什么?
我看到了导航混合,但我可以在没有混合的情况下这样做吗?
使用react router,我可以使用Link元素来创建由react route本地处理的链接。
我看到它在内部调用this.context.transitionTo(…)。
我想做导航。不是来自链接,而是来自下拉选择(例如)。我如何在代码中执行此操作?this.context是什么?
我看到了导航混合,但我可以在没有混合的情况下这样做吗?
当前回答
上面已经提到,我们可以使用useNavigate()在上一次更新React Router V6(不包括useHistory)中导航
import { useNavigate } from 'react-router-dom';
const myComponent = () => {
const navigate = useNavigate();
navigate('my_url');
...
}
但我在这里找不到的是调用导航出React组件,就像将redux saga函数中的页面导航到另一个页面。以防万一,如果你有同样的问题,这是我发现的。
在根组件中(我称之为<App/>)
import { useNavigate } from 'react-router-dom';
import useBus from 'use-bus';
const App = () => {
const navigate = useNavigate();
useBus('@@ui/navigate', (action) => navigate(action.payload.url), []);
...
}
脱离React组件(在我的例子中是redux saga函数)
import { dispatch } from 'use-bus';
dispatch({ type: '@@ui/navigate', payload: { url: '/404' } });
希望有帮助!
其他回答
React Router 4.x答案
在我这一方面,我希望有一个单独的历史对象,我甚至可以携带外部组件。我喜欢按需导入一个history.js文件,并对其进行操作。
您只需将BrowserRouter更改为Router,并指定历史属性。这对你来说没有任何改变,除了你有自己的历史对象,你可以随心所欲地操纵它。
您需要安装历史记录,即react router使用的库。
示例用法,ES6表示法:
history.js
import createBrowserHistory from 'history/createBrowserHistory'
export default createBrowserHistory()
基本组件.js
import React, { Component } from 'react';
import history from './history';
class BasicComponent extends Component {
goToIndex(e){
e.preventDefault();
history.push('/');
}
render(){
return <a href="#" onClick={this.goToIndex}>Previous</a>;
}
}
如果您必须从实际从Route组件渲染的组件进行导航,还可以从props访问历史,如下所示:
基本组件.js
import React, { Component } from 'react';
class BasicComponent extends Component {
navigate(e){
e.preventDefault();
this.props.history.push('/url');
}
render(){
return <a href="#" onClick={this.navigate}>Previous</a>;
}
}
试试React Hook Router,“反应路由器的现代替代品”:
import { useRoutes, usePath, A} from "hookrouter";
要回答OP关于通过选择框链接的问题,您可以这样做:
navigate('/about');
更新的答案
我认为React Hook Router是一个很好的入门套件,帮助我学习了路由,但我后来更新了React Router,了解了它的历史和查询参数处理。
import { useLocation, useHistory } from 'react-router-dom';
const Component = (props) => {
const history = useHistory();
// Programmatically navigate
history.push(newUrlString);
}
您可以将要导航到的位置推到历史位置。
布局/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路由器v6+答案
TL;DR:您可以使用新的useNavigate钩子。
import { useNavigate } from "react-router-dom";
function Component() {
let navigate = useNavigate();
// Somewhere in your code, e.g. inside a handler:
navigate("/posts");
}
useNavigate钩子返回一个可用于编程导航的函数。
反应路由器文档中的示例
import { useNavigate } from "react-router-dom";
function SignupForm() {
let navigate = useNavigate();
async function handleSubmit(event) {
event.preventDefault();
await submitForm(event.target);
navigate("../success", { replace: true });
// replace: true will replace the current entry in
// the history stack instead of adding a new one.
}
return <form onSubmit={handleSubmit}>{/* ... */}</form>;
}
React Router 5.1.0+应答(使用钩子和React>16.8)
您可以使用Functional Components上的useHistory挂钩,并以编程方式导航:
import { useHistory } from "react-router-dom";
function HomeButton() {
let history = useHistory();
// use history.push('/some/path') here
};
React Router 4.0.0+答案
在4.0及以上版本中,将历史记录用作组件的道具。
class Example extends React.Component {
// use `this.props.history.push('/some/path')` here
};
注意:如果<Route>未呈现组件,则此.props.history不存在。您应该使用<Route path=“…”component={YourComponent}/>在YourComponent中具有this.props.history
React路由器3.0.0+答案
在3.0及以上版本中,将路由器用作组件的道具。
class Example extends React.Component {
// use `this.props.router.push('/some/path')` here
};
React路由器2.4.0+答案
在2.4及以上版本中,使用更高阶的组件将路由器作为组件的道具。
import { withRouter } from 'react-router';
class Example extends React.Component {
// use `this.props.router.push('/some/path')` here
};
// Export the decorated class
var DecoratedExample = withRouter(Example);
// PropTypes
Example.propTypes = {
router: React.PropTypes.shape({
push: React.PropTypes.func.isRequired
}).isRequired
};
React Router 2.0.0+答案
此版本与1.x向后兼容,因此无需升级指南。仅仅通过这些例子就足够了。
也就是说,如果您希望切换到新模式,路由器内有一个browserHistory模块,您可以使用
从“react router”导入{browserHistory}
现在,您可以访问浏览器历史记录,因此可以执行推送、替换等操作。例如:
browserHistory.push('/some/path')
进一步阅读:历史和航行
React Router 1.x.x答案
我不会详细介绍升级。您可以在《升级指南》中阅读相关内容
这个问题的主要变化是从导航混合到历史。现在它使用浏览器历史记录API来更改路由,因此我们将从现在开始使用pushState()。
下面是使用Mixin的示例:
var Example = React.createClass({
mixins: [ History ],
navigateToHelpPage () {
this.history.pushState(null, `/help`);
}
})
请注意,此历史记录来自rackt/History项目。不是来自React Router本身。
如果您出于某种原因(可能是因为ES6类)不想使用Mixin,那么您可以从this.props.history中访问从路由器获得的历史记录。它将仅对路由器渲染的组件可用。因此,如果您想在任何子组件中使用它,则需要通过props将其作为属性传递。
您可以在他们的1.0.x文档中阅读有关新版本的更多信息
下面是一个关于在组件外部导航的帮助页面
它建议获取一个引用history=createHistory(),并对其调用replaceState。
React路由器0.13.x答案
我也遇到了同样的问题,只能通过带有react路由器的Navigation mixin找到解决方案。
我是这样做的
import React from 'react';
import {Navigation} from 'react-router';
let Authentication = React.createClass({
mixins: [Navigation],
handleClick(e) {
e.preventDefault();
this.transitionTo('/');
},
render(){
return (<div onClick={this.handleClick}>Click me!</div>);
}
});
我能够在不需要访问.context的情况下调用transitionTo()
或者你可以试试ES6高级课程
import React from 'react';
export default class Authentication extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
e.preventDefault();
this.context.router.transitionTo('/');
}
render(){
return (<div onClick={this.handleClick}>Click me!</div>);
}
}
Authentication.contextTypes = {
router: React.PropTypes.func.isRequired
};
React路由器Redux注意:如果您使用的是Redux,还有一个项目叫做React Router Redux为您提供redux绑定ReactRouter,使用与React Redux会
React Router Redux有一些可用的方法,允许从内部动作创建者进行简单的导航。对于在React Native中拥有现有架构的人来说,这些模式尤其有用,并且他们希望在React Web中以最小的样板开销使用相同的模式。
探索以下方法:
推(位置)替换(位置)go(数字)goBack()goForward()
以下是Redux Thunk的用法示例:
./actioncreators.js
import { goBack } from 'react-router-redux'
export const onBackPress = () => (dispatch) => dispatch(goBack())
./viewcomponent.js
<button
disabled={submitting}
className="cancel_button"
onClick={(e) => {
e.preventDefault()
this.props.onBackPress()
}}
>
CANCEL
</button>
在我的回答中,有三种不同的方法可以通过编程重定向到路由。已经介绍了一些解决方案,但下面的解决方案仅针对具有附加演示应用程序的功能组件。
使用以下版本:
反应:16.13.1反应时间:16.13.1反应路由器:5.2.0反应路由器dom:5.2.0字体:3.7.2
配置:
因此,首先解决方案是使用HashRouter,配置如下:
<HashRouter>
// ... buttons for redirect
<Switch>
<Route exact path="/(|home)" children={Home} />
<Route exact path="/usehistory" children={UseHistoryResult} />
<Route exact path="/withrouter" children={WithRouterResult} />
<Route exact path="/redirectpush" children={RedirectPushResult} />
<Route children={Home} />
</Switch>
</HashRouter>
从有关<HashRouter>的文档中:
一个<Router>,它使用URL的哈希部分(即window.location.hash)来保持UI与URL同步。
解决:
使用<Redirect>使用useState推送:
在功能组件(我的存储库中的RedirectPushAction组件)中使用,我们可以使用useState来处理重定向。棘手的是,一旦发生重定向,我们需要将重定向状态设置回false。通过使用带有0延迟的setTimeOut,我们等待React提交重定向到DOM,然后返回按钮,以便下次使用。
请在下面找到我的示例:
const [redirect, setRedirect] = useState(false);
const handleRedirect = useCallback(() => {
let render = null;
if (redirect) {
render = <Redirect to="/redirectpush" push={true} />
// In order wait until committing to the DOM
// and get back the button for clicking next time
setTimeout(() => setRedirect(false), 0);
}
return render;
}, [redirect]);
return <>
{handleRedirect()}
<button onClick={() => setRedirect(true)}>
Redirect push
</button>
</>
从<Redirect>文档:
渲染<重定向>将导航到新位置。新位置将覆盖历史堆栈中的当前位置,就像服务器端重定向(HTTP 3xx)那样。
使用useHistory钩子:
在我的解决方案中,有一个名为UseHistoryAction的组件,它表示以下内容:
let history = useHistory();
return <button onClick={() => { history.push('/usehistory') }}>
useHistory redirect
</button>
useHistory钩子允许我们访问历史对象,它帮助我们以编程方式导航或更改路线。
使用withRouter,从道具获取历史记录:
创建了一个名为WithRouterAction的组件,显示如下:
const WithRouterAction = (props:any) => {
const { history } = props;
return <button onClick={() => { history.push('/withrouter') }}>
withRouter redirect
</button>
}
export default withRouter(WithRouterAction);
阅读路由器文档:
您可以通过withRouter高阶组件访问历史对象的财产和最接近的<Route>匹配项。无论何时渲染,withRouter都会将更新的匹配、位置和历史属性传递给包装组件。
演示:
为了更好地表示,我用这些示例构建了一个GitHub存储库,请在下面找到它:
React Router程序化重定向示例