No Brainer Authentication in Django & React with Redux — Part 2

Manan
The Startup
Published in
6 min readDec 2, 2020
https://i.morioh.com/39b881aa45.png

We’ll pick right off where we left in the last part, where we were done with our Django backend and we’ll start with using React in this one.

There’s a few ways to start a React project — I personally like create-react-app with the Typescript template. That’s what we’ll do. We’ll create the frontend in the root directory and name the app ui. You can name it frontend or whatever you fancy.

$ npx create-react-app ui --template typescript

We need to install a few more dependencies. The first two are for redux. The next three are for redux enchancers and a router. The last few are the type declarations to work with Typescript.

$ npm i --save redux react-redux 
$ npm i --save react-router-dom redux-logger redux-persist
$ npm i --save @types/redux-logger @types/react-redux @types/react-router-dom

Delete all the files in the src/ directory except index.tsx, App.tsx, and react-app-env.d.ts. Create a directory in src/ called store. All our redux logic — actions, reducers, types, hooks, etc, will live here.

$ cd src/
$ mkdir -p store/auth/ && cd store/auth
$ touch types.ts reducers.ts actions.ts hooks.ts

Let’s start with our types.

Let’s walk through it step by step. We create two string constants that would act as identifiers for the type of action we’ll dispatch.

Next, we define the interface for our authentication state. This is the shape of our object that will store the token, the username, and a boolean signifying whether the user is logged in or not.

Next, we define a bunch of interfaces for the actions that we can dispatch. The LoginPayload interface is just describes data we’ll be supposed to send the reducer to authenticate a user. All actions also need to contain a unique type to help the reducer distinguish them from each other.

Finally, we have a union of all the actions and export it as a generic AuthActionType.

Let’s look at the reducers.ts file —

Again, if you’ve ever used Redux, this should be home. If not, let’s walk through it again. authReducer is a function that is used to perform reads and writes to the auth state — based on the action that is. And that’s all we do.

Inside the switch statement, we compare the type of the action to the constants we had defined earlier and then mutate the state accordingly.

Important point to remember — never mutate the state inside the reducer directly. Always return an entirely new updated state. Don’t ask me why, I don’t quite understand it myself but the reasons provided online seem to be good enough.

Finally, all we have to do is create our actions. In the actions.ts file, enter the following —

Pretty self explanatory. We simply create functions that take in the required data and return the appropriate action object for the reducer to accept.

One last thing and this is completely optional but highly recommended, write hooks for easier access to commonly read state properties. In hooks.ts, enter the following —

And we’re done with our auth store! Of course, we still need to configure a root reducer for the entire app but then we’ll be on our way.

Change directory back into the src/store/ folder and create two files — index.ts and reducer.ts. We’ll create our root reducer inside the reducer file and export our final redux store in index.ts.

I would explain the code but most of it is boilerplace nonesense. If you remember the dependencies I made you installed in the beginning, these were it. I like redux-logger quite a bit and find it useful for debugging.

We’re now completely done with our redux setup and can now move on to creating routes and other fun stuff.

Go back in your src/ directory and create two more folders —

$ cd src/ && mkdir pages components
$ touch routes.tsx

The pages/ will store all the components that will be used as routes. components/ will store all the reusable Components that will be used in pages. The routes file will, of course, hold all the routes.

PrivateRoute is a useful reusable component that protects pages that require authentication. It calls our trusty useAuthenticated hook to verify whether someone is logged in or not. The useAuthenticated hook in turn relies on the useSelector hook provided by react-redux that can “select” properties from our state.

We still haven’t written most of the components that we import in routes.tsx so let’s do that now.

Let’s begin with the most complicated one, the Login component. It’s actually simpler than you think -

We have a couple of imports. React for… well, our React component and JSX. Redirect and useDispatch are interesting. Redirect will be used to… well, redirect the user to the home page if they’re already logged in. We don’t wanna show the login page to an authenticated user.

useDispatch is a hook provided by react-redux that returns a function used to dispatch actions to the store reducers. Next, we import two things — Credentials, which seems to be a type, and getToken, which seems to be a function from an api/ directory. That’s right, we’re gonna be principled and create yet another directory api/ inside src/ to store all our fetching methods.

Inside api/, create a file auth.ts that will store all authentication related methods.

Once again, this just makes a POST request to the endpoint we set up in the first part and recieves the token. If invalid credentials were provided, we simply throw an error saying so.

Back to Login.tsx. We set up a couple of state variables namely error and credentials along with their set methods (setError & setCredentials). error will be used for displaying any errors we might face when we make a request to the token endpoint and credentials will be used to store the username and password from the input fields we describe in the JSX.

We have an asynchronous handleSubmit function that will be called whenever we submit our HTML form is submitted. We use the getToken function defined in api/auth.ts and dispatch the token along with the username stored in our credentials state variable using the logIn action defined way back in store/auth/actions.ts. If there are any errors, we simply update the error state variable and it’s rendered on the document.

The rest is pretty easy to understand. Go through it yourself. That was for our Login page. Next we have the Home and Account page.

Once again, simple.

You may be wondering why we don’t check whether the user is authenticated inside the Account component. That’s because of the ProtectedRoute guard we put in our routes earlier — it makes sure that the Account page is only available if the current redux state has the isAuthenticated property to true. Thus, adding an extra read for checking the auth state would be redundant inside the Account component.

The final step is to have a Navigation reusable component and then all that’s remained it to wrap the entire app up in a Provider.

And that’s it! We’ve written all our components, our pages, our routes, our store that includes, actions, reducers, types, and custom hooks! We’ve done so much!

All that is left is to configure the Provider and connect it to our base App component. Inside src/App.tsx, enter the following —

Once again, pretty much all normal except the PersistGate wrapper. All it does it wait for the store to be rehydrated and persist before rendering the components so that we don’t have to manually check whether the store is the latest version or not.

If you’ve followed everything so far, you should have a working React website with a Django backend!

--

--

Manan
The Startup

Computer Science and Mathematics enthusiast. I dabble in Philosophy.