Pushing Users to Sign In if Not Authenticated in React
All right welcome back. So in this video we need to somehow get access to this history object so we can push the user back if they're authenticated.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

Now we can do this pretty easily by creating our own browser history and using router instead of browser router. So let's now get over to bootstrap.js and let's get rid of this browser router and turn it into router. So replace it with router and make sure it replaces down here as well. And we need to import it in place of browser router up here at the top from react router Dom.

bootstrap.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose } from 'redux';
import { Router, Route, Switch } from 'react-router-dom';
import reduxThunk from 'redux-thunk';
import reducers from './reducers';

const createStoreWithMiddleware = applyMiddleware(reduxThunk)(compose((window.devToolsExtension ? window.devToolsExtension() : f => f)(createStore)));

// import 'bootstrap/dist/css/bootstrap.css';
import './style/main.scss';

import Layout from './components/layout';

import Signup from './components/auth/signup';
import Signin from './components/auth/signin';

import history from './history';
import requireAuth from './components/requireAuth';

class Dashboard extends Component {
  render() {
    return (
        <div>
        hey there
    </div>
    )
  }
}

function main() {
  ReactDOM.render(
    <Provider store={createStoreWithMiddleware(reducers)}>
      <Router history={history}>
        <Switch>
          <Layout>
            <Route path='/' exact component={Signin}/>
            <Route path='/signin' component={Signin}/>
            <Route path='/signup' component={Signup}/>
          </Layout>
        </Switch>
      </Router>
    </Provider>
    , document.querySelector('.app-wrapper'));
}

document.addEventListener('DOMContentLoaded', main);

And now it's going to give us an error because we no longer have that history object anywhere in our application.

large

So we can't use it anywhere, so basically we need to provide a prop to router called history and we don't really have a history object at this point. So we need to create one, let's do that by creating a new file in our source directory, and let's call it history.js.

And what we need to do in here is basically create our own browser history. So let's say import create browser history from history and let's say export default create browser history and pass in an empty object. Okay, cool that's all we have to do there.

history.js

import { createBrowserHistory } from 'history';

export default createBrowserHistory({});

Now we just need to require it in our bootstrap.js pass it into our router and then you can see that since we have it in our history.js we can access it in any other file which is really convenient. So let's go out of history.js and into bootstrap.js and let's import it.

So let's say import history from './history'. Now what we need to do is simply pass it into our history prop here on our router and we have access to our history again. So basically at this point nothing has really changed. Except for we still have this error because we spelt history wrong, so lets go into history.js and spell this right.

And that should work again, let's make sure we fix it here and we should be good to go. So at this point nothing has changed in our application, it's as if we didn't change anything. We have access to our history and all of our components that we're using it in, except for now we have a file that we can use anywhere in our application.

So this is really helpful in our require auth composed component because we can just import it and use it instead of having to somehow get it into our props. So let's say import history from ../history. Okay so you need to go up a directory and snatch it from history.js.

Now what we can do is just say history.push and then the root route(/) all right sweet.

requireAuth.js

import React, { Component } from 'react';
import { connect } from 'react-redux';

import history from '../history';

So now what we need to do is just finish writing this component and it's pretty simple. We just need to put in some logic into our component willUpdate. So if the browser receives some new data and it re-renders we want to basically push them back if they're not authenticated still.

So we can do this pretty easily by instead of saying props saying next props in this. Now if you go to the browser into this article we pulled up in the last video componentDidMakesSense.

If we scroll down or search componentWillUpdate, you'll see it belongs to the updating section that he put it in up here and basically it says react invokes this method immediately before rendering when new props or state are being received.

So there's not much use for componentWillUpdate and should probably be avoided but that's if you're doing anything with get requests. And in this case we are strictly dealing with our authenticated piece of state. So in here we just want to say if not nextProps.authenticated then push them back. Okay cool, that should finish the functionality of this component here.

requireAuth.js

import React, { Component } from 'react';
import { connect } from 'react-redux';

import history from '../history';

export default function(ComposedComponent) {
    class Authentication extends Component {
        componentWillMount() {
            if(!this.props.authenticated) {
                history.push('/');
            }
        }
        componentWillUpdate(nextProps) {
            if(!nextProps.authenticated) {
                history.push('/');
            }
        }
        render() {
            return <ComposedComponent/>
        }
    }

    function mapStateToProps(state) {
        const { authenticated } = state.auth;
        return { authenticated } 
    }

    return connect(mapStateToProps)(Authentication)
}

Let's go and test it out by actually creating a route for this route and walking it if they're not authenticated. Because right now in our bootstraps.js we actually don't have a route. So what I want to do is just copy this and put dashboard here and then let's just put something in here like let's write a component right here. OK, let's say class dashboard is equal to class dashboard extends Component and let's just put a render function in here and say div and let's say hey there.

Now what we can do is just use this in here instead of sign up so it can be unique. So dashboard it's pretty quick I'll say Hey there we're there and if we're not authenticated it's going to push it back to sign in.

bootstrap.js

class Dashboard extends Component {
    render() {
        <div>
            hey there
        </div>
    }
}


function main() {
  ReactDOM.render(
    <Provider store={createStoreWithMiddleware(reducers)}>
      <Router history={history}>
        <Switch>
          <Layout>
            <Route path='/' exact component={Signin}/>
            <Route path='/signin' component={Signin}/>
            <Route path='/signup' component={Signup}/>

            <Route path='/dashboard' component={requireAuth(Dashboard)}/>
          </Layout>
        </Switch>
      </Router>
    </Provider>
    , document.querySelector('.app-wrapper'));
}

And let's make sure we're importing component, so let's say import React and component up at the top here on line one and let's try it out.

All right, so right now it says dashboard did not return anything from render.

large

so let's make sure we're returning this div and say return and that's good. So it should say hey there. Let's check it out, it says hey there at the top.

large

Now what we need to do is basically protect it as you can see we're not authenticated so we don't want to see this unless we're authenticated. So let's do this let's go into our code and let's use this function we just created requireAuth and pass it in, pass in our dashboard component, and give it its functionality.

So let's go down here and in place of dashboard let's just say requireAuth and make sure you import it from components requireAuth so import requireAuth from './components/requireAuth'; alright it's a function that takes in a component dashboard. So we're going to extend its functionality with the requireAuth functionality.

Okay at this point it basically should be hiding it should say when authentication pushes back here. So if you manually try to go to slash dashboard it's just going to push it back because it's checking or state and saying hey you're not authenticated, go back to the root route.

Now let me just show you how this is specifically working by going into requireAuth and putting something asdf here on both of these okay so just thats going to push us to that route instead of the home one. So we could push them to another page that says something like Hey you're not authenticated.

So kind of a cool piece of functionality that we were easily able to implement. Let's change it back to the route route then sign in and let's test it out and see if it works when we actually sign into one of our accounts. So choose an account that you've created in your database and let's hit log in and you'll see we're on the dashboard that says hey there and we're not booted back because authenticated is set to true.

Okay sweet. So that's how we implement that piece of functionality. That's how you write a composed component and that should set us up pretty good and we now have basically the entire sign in/sign out and authenticated piece of functionality done in our application.

So good job in doing that in the next set of videos what we're going to do is basically implement or start building the newsletter feature. We're going to start building out this newsletter requests bar up here.

So let's commit our code

terminal

git status
git add .
git commit -m "finished require auth and created browser history manually"

And I wouldn't recommend pushing to heroku because nothing's going to work because it's going to try and see our local host url on the sign in and there's obviously not a server running there. So that's not going to work, so I wouldn't recommend pushing Heroku at this point.

Alright, I'll see you in the next video.

Resources

Source code