React-router v4 Page Refresh Not Working

2019-02-24 09:56发布

问题:

I am probably missing history or something but when I refresh a page on a sub route such as /login or any other routes I get:

403 Forbidden

Code: AccessDenied Message: Access Denied RequestId: 075CAA73BDC6F1B9 HostId: O1n36xVCoeu/aLaSMrtCkAFZruWk5ZcO4RIrznEhvUxdu8lFEhL0XcKw2L4W4J7VYYet+HDv8tc=

Keep in mind the routes work fine as long as I don't refresh.
So lets say I go to / (the homepage). On it, I have a hyperlink that maps to the path /login in my react-router routes. I click the hyperlink, it works fine, renders the /login control in react based on my react-router routes. It's only when I refresh any page I'm on that I get the error above.

Also when I sync my bucket I'm putting public read on it so not sure why I'm getting this permission error:

aws s3 sync --acl public-read build s3://${bkt}

Here's an example of some routes and a component:

      <Provider store={store}>
        <Layout>
          <Switch>
            <Route component={Home} exact path='/' />
            <ProtectedRoute component={
DashboardContainer} path='/dashboard' />
            <Route component={LoginContainer} path='/login' />
            <Route component={NotFound} path='*' />
          </Switch>
        </Layout>
      </Provider>

LoginContainer

    import { connect } from 'react-redux'
    import React, { Component } from 'react'
    const _ = require('lodash')

...

    import Login from '../components/auth/Login/Login'

    class LoginContainer extends Component {
      constructor(props) {
        super(props)

        this.handleEmailInput = this.handleEmailInput.bind(this)
        ... rest of the handlers
      }


      async handleLoginPressed(e) {
        e.preventDefault()
        this.validateForm()

        await this.props.authenticate(this.state.email, this.state.password)
        if(this.props.isAuthenticated) {
          this.props.history.push('/dashboard')
          return
        }

      ... more code

      render(){
        return(
          <div>
            <Login
              login={this.handleLoginPressed}
            />
          </div>
          )
      }
    }

    const mapStateToProps = state => ({
      token: state.auth.token
    })

    export const mapDispatchToProps = {
      authenticate: AuthAsyncActions.authenticate
    }

    export { Login }
    export default connect(mapStateToProps, mapDispatchToProps)(LoginContainer)

Now I've left out a bunch of repetitive code and this shows basically the only thing in here I have for routing is if they successfully log in, they're redirected to /dashboard. My problem isn't that, redirect works fine. It's whenever I refresh on any page I'm on, it doesn't re-route again.

回答1:

This is because your server tries to go to that url directly. You have two options:

  1. Use the hash router provided by react-router v4. Routes now will look like this: http://example.com/#/somePage

  2. If you don't like to have a # on the url you have to change the server behaviour. This configuration changes for diferents servers. In development mode if you are using webpack-dev-server just enable historyApiFallback. In production mode search for how to redirect 403/404 responses using the server you will use.



回答2:

There's a difference between client side and server side routing. This has been covered in a squillion posts and other places. Basically, if your client side react app redirects them to a certain route, that is NOT the same as them manually entering that route in the browser (which contacts the server and tries to get that SPECIFIC route).

So if you have a route in the front end, say:

<Route component={Potato} exact path='/potato' />

but in your back end you don't have something which handles requests to said path, then manually going to that URL or refreshing when on that URL will cause your app to poop itself.

Make sure you have a catch all route in your back end, something like this:

app.get('*', (request, response) => {
    response.sendFile(path.resolve(__dirname, 'index.html'))
});

essentially, a catch all route that just returns your index.html

Also be sure to look up client side vs server side routing, cos that will cause issues with page refreshing and manually navigating to certain URL's with React Router



回答3:

When deploy my create-react app to heroku, I ran into similar issue, the app works fine until refresh on a subroute. The way I fixed it was adding this file in my root directory. Not sure if this will help you on aws.

static.json

{
  "root": "build/",
  "clean_urls": false,
  "routes": {
    "/**": "index.html"
  }
}