使用react router,我可以使用Link元素来创建由react route本地处理的链接。

我看到它在内部调用this.context.transitionTo(…)。

我想做导航。不是来自链接,而是来自下拉选择(例如)。我如何在代码中执行此操作?this.context是什么?

我看到了导航混合,但我可以在没有混合的情况下这样做吗?


当前回答

在我的回答中,有三种不同的方法可以通过编程重定向到路由。已经介绍了一些解决方案,但下面的解决方案仅针对具有附加演示应用程序的功能组件。

使用以下版本:

反应: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程序化重定向示例

其他回答

仅使用浏览器api,独立于任何库

const navigate = (to) => {
    window.history.pushState({}, ",", to);
  };

这不会导致整页刷新。您仍然可以使用浏览器后退按钮

在react router的上下文中,已经有大量的答案可以正确、准确地回答这个问题,但我想花点时间指出一些我在其他答案中没有提到的问题。

这是一种stackoverflow的比喻,建议用与原始问题不同的方式来做一些事情,但在这种情况下,由于问题的年龄,我觉得在当前的时间里,问题的表述会有所不同。

我建议放弃react router,只需通过window.location.href=[yoururlhere]推送到本地浏览器位置,然后通过正则表达式(或确切的)匹配window.location.pathname和您的预期路径来检查路径。

更简单的答案通常是最好的答案,在这种情况下,React Router的用例由本地位置对象API提供更好的服务。

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>

布局/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;

根据JoséAntonio Postigo和Ben Wheeler先前的回答:

新奇之处?用TypeScript编写并使用修饰符或静态属性/字段

import * as React from "react";
import Component = React.Component;
import { withRouter } from "react-router";

export interface INavigatorProps {
    router?: ReactRouter.History.History;
}

/**
 * Note: goes great with mobx
 * @inject("something") @withRouter @observer
 */
@withRouter
export class Navigator extends Component<INavigatorProps, {}>{
    navigate: (to: string) => void;
    constructor(props: INavigatorProps) {
        super(props);
        let self = this;
        this.navigate = (to) => self.props.router.push(to);
    }
    render() {
        return (
            <ul>
                <li onClick={() => this.navigate("/home")}>
                    Home
                </li>
                <li onClick={() => this.navigate("/about")}>
                    About
                </li>
            </ul>
        )
    }
}

/**
 * Non decorated
 */
export class Navigator2 extends Component<INavigatorProps, {}> {

    static contextTypes = {
        router: React.PropTypes.object.isRequired,
    };

    navigate: (to: string) => void;
    constructor(props: INavigatorProps, context: any) {
        super(props, context);
        let s = this;
        this.navigate = (to) =>
            s.context.router.push(to);
    }
    render() {
        return (
            <ul>
                <li onClick={() => this.navigate("/home")}>
                    Home
                </li>
                <li onClick={() => this.navigate("/about")}>
                    About
                </li>
            </ul>
        )
    }
}

无论今天安装了什么npm。

“react router”:“^3.0.0”和“@types/react router”:“^2.0.41”