Moving from react router 3.x to 4.x

2020-02-03 04:29发布

问题:

How can I move to using https://cdnjs.cloudflare.com/ajax/libs/react-router/4.0.0-2/react-router.min.js from using https://cdnjs.cloudflare.com/ajax/libs/react-router/3.0.1/ReactRouter.min.js?

Example using 3.x below.

HTML

<script src="https://unpkg.com/react@15.4.2/dist/react.min.js"></script>
<script src="https://unpkg.com/react-dom@15.4.2/dist/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-router/3.0.1/ReactRouter.min.js"></script>

JS

let { Router, IndexRoute, Redirect, Route, Link, browserHistory } = ReactRouter;

history.replaceState(0,0,'/');

const Main = () =>
  <Router history={browserHistory}>

    <Route path='/' component={App}>
      <IndexRoute component={Home}/>
      <Route path='map' component={Map}/>
      <Route path='settings' component={Settings}/>
            <Redirect from='foo' to='/' />
      <Route path='*' component={NotFound}/>
    </Route>

  </Router>

const App = props => 
  <div>
    <Navigation>
      <Link to='/map'>Map</Link>
      <Link to='/settings'>Settings</Link>
      <Link to='/foo'>Foo</Link>
    </Navigation>

    {props.children}

  </div>

const Navigation = props => <nav {...props} />
const Home = () => <h1>Home</h1>
const Map = () => <h1>Map</h1>
const Settings = () => <h1>Settings</h1>
const NotFound = (props) => <h1>404 - Not Found</h1>

ReactDOM.render(<Main />, document.body);

See it in action at: https://jsfiddle.net/developit/nvpr63eg/

If I move to any of the CDN hosted 4.x versions though it doesn't work (unreachable code after return statement).

回答1:

I was able to configure it with redux by changing a few things:

First, browserHistory is not defined on react-router anymore. One way to get it back is to use the package history.

You will need to install (redux optional):

npm i -S history react-router react-router-redux

Instead of trying to import browserHistory use:

import { createBrowserHistory } from 'history';

If you want to use redux then import this and add it to your combined reducers:

import { routerReducer as routing } from 'react-router-redux';

combineReducers({
  home, about,
  routing, // new
});

Next you create the history object

// redux
import { syncHistoryWithStore } from 'react-router-redux';
const history = syncHistoryWithStore(createBrowserHistory(), store);

// regular
const history = createBrowserHistory();

Then change your router to use the new history object

<Router history={ history } children={ Routes } />

Everything else should be the same.


If anyone runs into The prop history is marked as required in Router, but its value is undefined.

It is because browserHistory is no longer available in react-router, see post to use history package instead.


Related

  • Github ts-react-redux-startup: Project that uses it
  • Migrating to React Router 4 with Redux


回答2:

This is a supplement answer to the one Paul S posted above. Credit should still goto paul for posting it.

Reason m supplementing the answer is bcoz:-

  1. I got errors with BrowserHistory and Link.
  2. I had to include react-router-dom.
  3. was a typo (in Pauls original answer)

yarn steps:

yarn add react-router
yarn add react-router-dom

package.json:

 "react": "^15.6.1",
    "react-dom": "^15.6.1",
    "react-router": "^4.1.1",
    "react-router-dom": "^4.1.1"

index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import { Switch, Redirect, Route} from 'react-router';
import {BrowserRouter, Link} from 'react-router-dom';


//let { BrowserRouter, Switch, Redirect, Route, Link } = ReactRouter;

const Main = () =>
  <BrowserRouter>
    <App>
      <Switch>
        <Route exact path='/' component={Home}/>
        <Route path='/map' component={Map}/>
        <Route path='/settings' component={Settings}/>
        <Route path='/foo' component={Foo}/>
        <Route component={NotFound}/>
      </Switch>
    </App>
  </BrowserRouter>

const App = props =>
  <div>
    <Navigation>
      <Link to='/map'>Map</Link>
      <Link to='/settings'>Settings</Link>
      <Link to='/foo'>Foo</Link>
    </Navigation>

    {props.children}

  </div>

const Navigation = props => <nav {...props} />
const Home = () => <h1>Home</h1>
const Map = () => <h1>Map</h1>
const Settings = () => <h1>Settings</h1>
const Foo = () => <Redirect to='/' />
const NotFound = (props) => <h1>404 - Not Found</h1>

ReactDOM.render(<Main />, document.body);



回答3:

There should be an upgrade guide closer to full release. I adapted your code for the upcoming beta instead of the current alpha. The beta has not been released yet, so unfortunately there is no hosted version of React Router that you will be able to test this out with.

let { BrowserRouter, Switch, Redirect, Route, Link } = ReactRouter;

const Main = () =>
  <BrowserRouter>
    <App>
      <Switch>
        <Route exact path='/' component={Home}/>
        <Route path='/map' component={Map}/>
        <Route path='/settings' component={Settings}/>
        <Route path='/foo' component={Foo}/>
        <Route component={NotFound}/>
      </Switch>
    </App>
  </BrowserRouter>

const App = props => 
  <div>
    <Navigation>
      <Link to='/map'>Map</Link>
      <Link to='/settings'>Settings</Link>
      <Link to='/foo'>Foo</Link>
    </Navigation>

    {props.children}

  </div>

const Navigation = props => <nav {...props} />
const Home = () => <h1>Home</h1>
const Map = () => <h1>Map</h1>
const Settings = () => <h1>Settings</h1>
const Foo = () => <Redirect to='/' />
const NotFound = (props) => <h1>404 - Not Found</h1>

ReactDOM.render(<Main />, document.body);


回答4:

You certainly can, but you would need to rewrite some of your components and application logic because your code will not run as it is currently.

Have a read on the official documentation, here. There's also a good FAQ about these changes, here. Here's an excerpt:

Why the huge change?

tl;dr Declarative Composability.

We've never been this excited about React Router. Like you, we've learned a lot about React since we first picked it up. We built a Router the best we knew how along the way. What we've learned most is that we love React because of its declarative composability.

If you want to upgrade, there is an upgrade guide coming soon, according to an official source:

We believe very strongly in iterative migration, not rewrites, for applications. We are working on a migration guide, but the basic strategy is that both versions will be runnable in tandem (due to npm limitations, we'll publish the previous version separately so both can be installed).

You will be able to take routes one-by-one and migrate them to the new API. Additionally, the config addon mentioned above may help out here.


With that in mind, going back to your original issue: to make your App component run properly, it should now look something like:

const App = props => 
  <div>
    <Navigation>
      <Link to='/map'>Map</Link>
      <Link to='/settings'>Settings</Link>
      <Link to='/foo'>Foo</Link>
    </Navigation>
    {props.children}
  </div>

  <Match exactly pattern="/" component={Home} />
  <Match pattern="/map" component={Map} />
  <Match pattern="/settings" component={Settings} />
  <Match pattern="/topics" component={Topics} />

Hope this helps. Good luck.


edit: Apparently @PaulS has an example for a future version of React-Router v4. If the post is accurate, the above code will not work in the future; it will only work for the current version available. Keep an eye out on the linked pages above for info when RRv4 goes live.



回答5:

There are a couple things to note with the recent release of v4, concerning your jsfiddle. The first thing is that that the library as a whole has been separated: web, native, and core (use anywhere). Use react-router-dom for web only.

2: The way you nest routes is quite different. Instead of nesting routes in your parent component, which you actually cannot do, all you have to do is define your parent component (BrowserRouter in this case), and then you are free to define routes throughout your components. Your components define the hierarchy now. For example, you can organize your Main and App components like this:

const Main = () => (
  <BrowserRouter>
    <App />
  </BrowserRouter>
)

const App = props => (
  <div>
    <Navigation>
      <Link to='/'>Home</Link>
      <Link to='/map'>Map</Link>
      <Link to='/settings'>Settings</Link>
      <Link to='/foo'>Foo</Link>
    </Navigation>

    <Route exact path='/' component={Home}/>
    <Route path='/map' component={Map}/>
    <Route path='/settings' component={Settings}/>
    <Route path='/foo' render={() => (
        <Redirect to="/"/>
    )}/>
  </div>
)

Hope this helps. Please take a look at my fix for your jsfiddle below, and let me know if you have any questions.

https://jsfiddle.net/bagofcole/kL6m8pjk/12/

For more information check out: https://reacttraining.com/react-router/web/guides/quick-start