This is the second part of the tutorial series on how to create a "Modern" web application or SPA using Django and React.js

In this part, we'll setup redux and react router in our note taking application. And later connect this frontend to an API backend.

The code for this repository is hosted on my github, v1k45/ponynote. You can checkout part-2 branch to see all the changes done till the end of this part.

What is react router?

React-router-dom is a library which is used for in-application routing in react applications. Using it you can mount components on urls of your choice.

We'll first create a router for our application with relevant routes. Since this is a basic note-taking application, just one component/route should suffice but we'll still create a few other components and routes for demonstration.

Setup react-router-dom

Start by installing react-router-dom:

$ cd ponynote/frontend
$ npm install --save react-router-dom

After this let us create a few components which we will be using in the application. To maintain a clean directory structure we'll create all components inside a components directory. We will create a PonyNote component for our main app and NotFound component for 404 pages.

The above code will make use of BrowserRouter, which means it will use the HTML5 history API to maintain the application routing. The Switch component is optional, but it is used for efficient routing. The Route components render the target component when the location of the application matches it's path. If no path is specified to the Route, all path matches return true, which is useful for 404 pages.

Now that our component is ready, we can update our components to show actual content.

After creating store, we need to wrap our react application's root component with react-redux's Provider component and pass store to it in order to use the redux store. The final App.js will look like this:

In the above code we "connect" our component using the connect high order function provided by react-redux. mapStateToProps is used to "map" the application state to the "props" of the component. Here, we are passing the notes array as a component prop with same name. mapDispatchToProps is "mapping" action dispatcher functions to component "props".

Inside the render function we have created a table and iterated all notes with placeholder edit and delete button. The above code should result in a webpage like this:

Working with redux states using actions

Now that we have a read-only implementation of our note-taking app which just displays notes. We will now create actions, reducer cases and UI element to modify the redux state.

In the above code we are handling different cases of action payload, namely ADD_NOTE, UPDATE_NOTE and UPDATE_NOTE. The individual cases do what their name suggest and return a modified copy of the state after the said action is done. Here we do not update the state directly, but we return a new state which will replace the current state of the notes reducer.

The cases in the reducers will only be invoked when an action is dispatched of corresponding type. In actions/notes.js declare the actions:

Each of the above functions returns an object with a type property which the reducer uses to determine how the state is to be updated. Besides type these payloads can have any property as values which can later be used inside the reducer function while modifying the state.

Update the actions/index.js file so that we can access all actions in one place:

import*asnotesfrom"./notes";export{notes}

Using Actions inside a component

After the actions are defined, we can use them inside the PonyNote component by declaring properties in mapDispatchToProps.

The above code will store the note text in the component state and save it when the form is submitted. onSubmit, the application will dispatch an action ADD_NOTE which will then add the note to redux state.

Similarly we can add an option to delete notes when the "delete" button is pressed. Replace the contents of tbody with the following:

The component state now keeps track whether we are creating or updating a note. The submitNote method changes behavior based on component state. We have also added a helper method to load and reset the form for updating notes.

Inside the form element, place a button to reset form after selecting notes to edit by mistake.

<buttononClick={this.resetForm}>Reset</button>

And update the edit button to load notes for editing:

<td><buttononClick={()=> this.selectForEdit(id)}>edit</button></td>

This will result in a simple note-taking web app with functionality to add, update and edit notes. It should look like this:

Add some css

Before moving any further, lets add some css so that our application doesn't hurt our eyes. For this we will use sakura.css, the same classless css library which is used for this blog!

After this, update the index.css file to import these downloaded css files:

@importurl("css/normalize.css");@importurl("css/sakura.css");

Much better!

Our webapp is working smoothly client-side but cannot store data permanently. In the next post we will create models and APIs in django to store and manipulate notes from database. This way we don't lose any notes.