React & Redux | A Quick Side Project
Tags: showdev, react, redux, webdev
Published: 2021-06-04 19:08:18

## [Tracker](https://aj-rom.github.io/tracker)

As I am currently learning React and Redux I wanted to mess around with these new frameworks and get used to hosting them on GitHub for future projects.

This is a very basic application that showcases DOM manipulation, via React, and state management through Redux. What it allows us to do is add items to a list, we can increase the count on each item or decrease them, and remove them from the master list. 

You can check out the hosted application on github pages [here](https://aj-rom.github.io/tracker), or view the source code [here](https://github.com/aj-rom/tracker).

---

### The Development Process

As the development process goes, it is always a good idea to have something written down in what you expect your application to end up working like. I came up with the following:

```JSX
// Our ReactDOM.render method in index.js
<Provider store={store}>
  <App/>
</Provider>

// App Component
<React.Fragment>
   <Header/> // Where we should be able to add items to the list
   <Body items={this.props.items}/> // Pass our store of props
</React.Fragment>
// Connected via react-redux to pass the props from our main index.js
```

---

#### Setting Up Containers
These are simply for separating our components into a logical manner as well as increasing maintainability for future changes to components and the layout of our application.

##### Header

This is where we want our input form to be, so we are going to wrap this section in a div giving it a unique ID for styling/DOM manipulation.

Setting up our render method should like something like this:
```JSX
<div className='add-bar'>
    <AddBar />
</div>
```
##### Body

This container should be responsible for passing the props from our main application state down to our `<ItemList/>` component, and rendering this in a reasonable fashion.

Setting up our render our 
```JSX
<div id='item-list'>
   <ItemList items={items}/>
</div>
```
Now that we have our containers set up we can go ahead and begin building our our components.

---

#### Setting Up Components
As we declared above we need at least two components: `<AddBar/>` and `<ItemList/>`, and it would make sense for `<ItemList/>` to house many components such as `<Item/>`.

So we need to declare three components `<AddBar/>`, `<ItemList/>`, and `<Item/>`. Which all need to be connected to our main application state.

---

##### `<AddBar/>`

This component should be responsible for taking user input, and adding it to our main application states list of items. 
```JSX
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { addItem } from "../actions/item";
import '../style/form.css'
// Material UI Components & Style
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button'

class AddBar extends Component {

    constructor(props) {
        super(props);
        this.state = {
            item: '',
            count: 0
        }
    }

    handleChange = e => {
        if (this.state.item.length > 25 && e.target.value.length > 25) return
        this.setState({ item: e.target.value })
    }

    handleSubmit = e => {
        e.preventDefault()
        // Passed to props via connect method for dispatch actions/item.js
        this.props.addItem(this.state)
        this.setState({ item: '' })
    }

    render() {
        return (
            <form className="add-form" onSubmit={this.handleSubmit}>
                <TextField type='text' id='item' required={true}
                           autoComplete='off'
                           label='Item Name' value={this.state.item}
                           placeholder='Add Your Item Here' onChange={this.handleChange}
                />
                <Button type='Submit'>Submit</Button>
            </form>
        )
    }
}

// Pull main application state to props
// As well ass pull the addItem action to our props
export default connect(props => ({ item: props.item }), { addItem })(AddBar)
```

---

##### `<ItemList/>`
This component should be responsible for passing our state and breaking it down and rendering them into specific `<Item/>` components.

Our component should be declared something like:
```JSX

import React from 'react'
import Item from './item'

const ItemList = ({ items }) => {

    function renderItems() {
        return items.map((e, idx) => <Item key={idx} id={idx} item={e.item} count={e.count}/>)
    }

    return (
        <div className='item-list'>
             {renderItems()}
        </div>
    )
}

export default ItemList
```

Notice this doesn't have to use the *react-redux* connect method, because we are passing our main application state `items` as a prop to the `<Body/>` container.

This also allows us to render our individual items.

---

##### `<Item/>`
This component should be responsible for rendering specific items and their *count*, as well as handling buttons for incrementing and decreasing count and removal button for removing the item from our main application state and DOM.

Setting up our class would look something like:
```JSX
import React, { Component, lazy, Suspense } from 'react'
import { connect } from 'react-redux'
import '../style/item.css'
import { removeItem, increaseCount, decreaseCount } from '../actions/item'
import Card from '@material-ui/core/Card'
import Button from '@material-ui/core/Button'
import Typography from '@material-ui/core/Typography'
import ButtonGroup from '@material-ui/core/ButtonGroup'

class Item extends Component {

    handleRemove = e => {
        this.props.removeItem(this.props)
    }

    handleIncrease = e => {
        this.props.increaseCount(this.props)
    }

    handleDecrease = e => {
        this.props.decreaseCount(this.props)
    }

    render() {
        return (
             <Card className='item-card' id={this.props.id}>
                 <Typography variant='' className='item-title'>{this.props.item}</Typography>
                 <Typography variant='subtitle1' className='clicker'>{this.props.count} times</Typography>
                 <ButtonGroup className='action-row'>
                     <Button onClick={this.handleIncrease} className='item-button'>+</Button>
                     <Button onClick={this.handleDecrease} className='item-button'>-</Button>
                     <Button onClick={this.handleRemove} className='item-button'>Remove</Button>
                 </ButtonGroup>
             </Card>
        )
    }
}

export default connect(props => ({ ...props }),
    { removeItem, increaseCount, decreaseCount })(Item)
```

Notice that we are pulling in **three** actions: `removeItem`, `increaseCount`, and `decreaseCount`.

#### Setting Up Actions
Since this was a very simple application we only needed on action file and four total methods. All that should take an item as a parameter.

* `addItem`
```Javascript
export const addItem = (item) => {
    return { type: 'ADD_ITEM', item: item}
}
```
* `removeItem`
```Javascript
export const removeItem = (item) => {
    return { type : 'REMOVE_ITEM', item: item}
}
```
* `increaseCount`
```Javascript
export const increaseCount = (item) => {
    return { type: 'INCREASE_COUNT_OF_ITEM', item: item}
}
```
* `decreaseCount`
```Javascript
export const decreaseCount = (item) => {
    return { type: 'DECREASE_COUNT_OF_ITEM', item: item}
}
```

#### Setting Up a Reducer
In order for us to handle these actions we need a reducer function to manipulate the applications state.

This will usually be called `manage<ClassName>` so we will call our file `manageItems.js`. Our function should take two parameters, the applications state (list of items), and an action object.

Leaving us with a new file that looks like:
```Javascript

function manageItems(state = { items: [] }, action) {
    switch (action.type) {
        case 'ADD_ITEM':
            action.item.id = state.items.length
            return { ...state, items: [...state.items, action.item] }
        case 'REMOVE_ITEM':
            let items = [...state.items]
            items.splice(action.item.id, 1)
            return { ...state, items: items }
        case 'INCREASE_COUNT_OF_ITEM':
            let arr = [...state.items]
            const x = arr[action.item.id]
            x.count += 1
            return { ...state, items: arr }
        case 'DECREASE_COUNT_OF_ITEM':
            let dec = [...state.items]
            const y = dec[action.item.id]
            if (y.count === 0) return state

            y.count -= 1
            return { ...state, items: dec}
        default:
            return state;
    }
}

export default manageItems
```

This connects the functionality between our Item component and our main application state by allowing us to call actions to our reducer from the Item component buttons.

We set up a default parameter for our **initial** state, which is being called when we use `createStore` in our `index.js` file. This gets overridden with our current application state every subsequent call to this method.

---

#### Hosting to GitHub Pages

I wanted free hosting for this application, and as there are a couple of free options out there ([Netlify](https://www.netlify.com/), [Heroku](https://www.heroku.com), etc), my codebase was already hosted on a GitHub repository. 

Might as well just host it from the same location!

##### Installing `gh-pages`
There is a very useful npm package for achieving this extremely quickly, read more on it [here](https://www.npmjs.com/package/gh-pages).

All I had to do was run `npm install gh-pages`, add a 
```JSON
"homepage": "https://aj-rom.github.io/tracker"
``` key and value to my `package.json`. 

Next thing to add were the special scripts provided by `gh-pages`, to our `package.json` for us to be able to deploy this to github.

Our scripts section should now look like:
```JSON
  "scripts": {
    "predeploy": "npm run build",
    "deploy": "gh-pages -d build",
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
```

To deploy our application we run `npm run predeploy` which creates a `/build` directory with our production site. Navigate into the `/build` directory and open up `index.html` make sure everything is working the way you want and once you are satisfied go ahead and deploy it using `npm run deploy`.

This creates a new branch, `gh-pages`, and pushes only the contents of the `/build` folder to this branch. GitHub then automatically will detect that there is a branch that should be used for GitHub pages and your application will be up and running!

---

#### Optimizations via Lighthouse

Now that I got everything up and running I wanted to make sure everything was highly optimized and see how I could increase performance for any react-redux application in the future.

My initial page report for desktop was flawless, 100s across the board, but mobile was drastically lower performance wise.

This lead me to [code splitting](https://reactjs.org/docs/code-splitting.html) with `React.lazy` and `Suspense`. Which increased performance on mobile to be on par with the desktop report.

Feel free to run your own diagnostics check by navigating to the website [aj-rom.github.io/tracker](https://aj-rom.github.io). Open the chrome-developer console and navigate to the *Lighthouse* section. 

### Conclusion
I learned a lot while making this application when it came to separating concerns within a React-Redux application, and how to optimize component loading using code splitting.