1

Im trying to implement a withRouter in React, so I have imported it like this

import { BrowserRouter, Route, Switch , withRouter} from "react-router-dom";

And then in the last line I have written the following

export default withRouter(App);

But im receiving an error

You should not use <withRouter(App) /> outside a <Router>

Im trying to implement it in my app.js , which looks like the following

   class App extends React.Component {

    constructor(props) {
        super(props);

        console.log("PROPS APPJS")
        console.log(props)

        //checks if user is autheticated within the system in order to manage routes
        this.state = {
            authenticationChecked: false,
            isAuthenticated: false 
        }  

    }

    componentDidMount() {
        //calls the auth service to decide the auth state value
        isAuthenticated().then((result) => {
            if (result === true) {
                this.setState({ isAuthenticated: true, authenticationChecked: true})
            } else {
                this.setState({ isAuthenticated: false, authenticationChecked: true})
            }
        });
    }

    login = (email, password) => {
        var thiscomponent  = this;

        axios({
            method: 'post',
            url: 'http://localhost:3003/login',
            data: qs.stringify({ email, password }),
            headers: {
                'content-type': 'application/x-www-form-urlencoded;charset=utf-8'
            }
        }).then(res => {
            console.log("set cookie")
            //the accestoken is set as a cookie in order to check routes
            Cookies.set('accesstoken', res.data.accesstoken);
            console.log("those are the props")
            console.log(this.props);
            this.setState({ isAuthenticated: true }, () => {
               thiscomponent.props.history.push('/'); 
            })

        }).catch(err => {
            console.log(err)
        })  
    }


    render() {
        if (!this.state.authenticationChecked) return null;

        return (

                <BrowserRouter>
                    <Switch>

                        <Route exact path="/login" render={(props) => <LoginPage login={this.login} {...props} />} />
                        <PrivateRoute authed={this.state.isAuthenticated} exact path="/register" render={(props) => <RegisterPage />} />
                        <PrivateRoute authed={this.state.isAuthenticated} exact path="/" render={(props) => <NewLandingPage  {...props}  />} />
                        <PrivateRoute authed={this.state.isAuthenticated} path="/page1" render={(props) => <Page1 />} />
                        <PrivateRoute authed={this.state.isAuthenticated} path="/page2" render={(props) => <Page2 />} />

                    </Switch>
                </BrowserRouter>
        )
    }
}
export default withRouter(App);

What can be the issue there? I actually have a browserrouter there, isnt it supposed to be a router too?

mouchin777
  • 686
  • 8
  • 26
  • what are you trying to use `withRouter` for, though? Because reading through https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/withRouter.md it doesn't sound like it's meant for what your code suggests you think it's going to do for your. – Mike 'Pomax' Kamermans Jan 23 '20 at 08:28
  • @Mike'Pomax'Kamermans in my login function, ```var thiscomponent = this``` evaluates to undefined, so im unable to use ```thiscomponent.props.history.push()``` as per my understanding as I was suggested there https://stackoverflow.com/questions/59859747/react-not-redirecting-after-first-login-but-redirects-after-forcefully-redirect , I should use withrouter to have a populated history – mouchin777 Jan 23 '20 at 08:30
  • Sure, but that's a different problem: turn that into a normal class function instead of a static property if you want `this` to mean something. (`login(email, password) { ... }` instead of an arrow function) – Mike 'Pomax' Kamermans Jan 23 '20 at 08:32
  • 2
    this doesn't evaluate to undefined but props.history does as your history is not available in the props of that class unless you are inside of BrowserRouter. this in the arrow function refers to the parent this. Which very well exists. Have a look here: https://github.com/ReactTraining/react-router/issues/4059 -> Asaf Aviv is correct. – lehm.ro Jan 23 '20 at 08:53

2 Answers2

3

You have BrowserRouter inside App but when you use withRouter the parent that renders the child with withRouter needs to have the BrowserRouter.

So remove BrowserRouter from App and render App inside BrowserRouter

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>, 
  container
)

Also if you use methods as arrow functions you don't need to store the this context as it will always point out to the class instance

Ninjakannon
  • 3,283
  • 5
  • 42
  • 65
Asaf Aviv
  • 8,367
  • 1
  • 15
  • 33
0

The problem most likely comes from the authentication check you are carrying out. Regardless of the outcome of it, you are wrapping the component in withRouter() so if authenticationChecked is false withRouter() has no access to any Routes, hence your error.

Your solution might be to create a component for the login that then wraps your BrowserRouter etc., in a hierarchy something like this:

return (
  <LoginProvider>
    <MyBrowserRouter>
      <Router>
    </MyBrowserRouter>
  </LoginProvider>
);

Then you could wrap MyBrowserRouter in withRouter() but make the render of LoginProvider take children based on your authentication check.

rrd
  • 5,018
  • 3
  • 22
  • 32