Allan Pope | Jun 26, 2016
After hearing a lot about React & Redux recently I decided to play around with it and try to build something to see what it’s like. This article covers the things I’ve learnt so far and will give you the knowledge and foundations to build a basic application.
I’ll assume that you have some JavaScript knowledge and I’ll link to resources that I’ve found useful while learning. This will give you the chance to dive into topics further. After reading this you will have a clearer picture on what React and Redux is and how an application would fit together.
Your application will have a store which will hold the state for your application. One of the principles of Redux is that your application will only have one store. The store is where you store the state for the whole application. You can think of the state as all the data/content for you app. In your component you will display data/content from the store.
Here’s a quote from the Redux doc’s that tells you how it works:
The whole state of your app is stored in an object tree inside a single store.
The only way to change the state tree is to emit an action, an object describing what happened.
To specify how the actions transform the state tree, you write pure reducers.
That’s it!
One awesome feature Redux has is it’s time travelling dev tools. It let’s you jump forward and back to view your applications state at any one time. I’m not going to go into how to set it up in this article, but I recommend looking into it more.
There are three parts to Redux: Actions, Reducers and the Store. Here’s how each part works:
Redux is immutable, meaning you should always return a new version of the state, instead of modifying the state.
Let’s go through a scenario and get an idea of how the redux event flow works. In the scenario, the electronic list will be our React component, you will fire an Action, Bill will play the role of Actions and Lucy will play the role of a Reducer.
There’s a big event happening at your work and you want to invite your friend Sue. To do this, you will need to get her added to the attendee list. You ask around and find out that there is an electronic list. The list is always updated to the latest attendee information.
You walk up Bill and say I want to add an attendee called Sue to the events attendee list.
Bill only knows how to do get certain things actioned. He sees your request and walks over to where Lucy sits and says please add an attendee called Sue to the events attendee list.
Like Bill, Lucy only knows how to do certain things. When she gets asked to do things she doesn’t know how to do, she ignores them. In this case she has heard Bill say add an attendee called Sue and she processes it. Lucy has been put in charge of all the event information, she knows how to update it. Lucy gets the attendee list and adds Sue to it. The event information is then dispatched out to the rest of the company and will send an update to anything subscribed to it, eg: the electronic list.
You look at the electronic list, see that it’s been updated and that Sue is now on the list.
We are going to build an app which will take a list of attendee names and print each name on a ‘hello my name is’ badge. Each badge will be coloured in the attendees favourite colour. Once we get that working we will add the functionality to add and remove people from the list. Note that I’ll be using some ES6 in these examples.
JavaScript Libraries we will be using:
Since we are focusing on JavaScript, we will use Foundation to take care of the styling for the app. There are some new ideas on how to style React components, one being CSS Modules which you can learn about here if you’re interested.
We need a way to access functionality from libraries we are using. In the example below, we are storing the render method from ReactDom in a constant called render.
const { render } = ReactDOM;Render will take our React Component and render it as a DOM element. We will render our component to the HTML element with an id of app.
render, react component, DOM element to render it into
render(<App />, document.getElementById('app'));We create a React component called App. All components have a render function and return some HTML. You can only render one HTML element from a component.
This would be invalid and would throw an error.
class App extends React.Component {
render() {
return (
<h1>Attendees</h1>
<p>Some text</p>
<img src="http://placehold.it/300x20" />
)
}
}Instead wrapping it inside of a div would make it valid.
class App extends React.Component {
render() {
return (
<div>
<h1>Attendees</h1>
<p>Some text</p>
<img src="http://placehold.it/300x20" />
</div>
)
}
}Going forward our newly created App component will act as our root component. We will put any other components we create inside it.
See the Pen 1/4 Building your first React Redux App by Allan Pope (@allanpope) on CodePen.
Lets setup up Redux and pass the application some data. We will need to setup Actions and Reducers, as well as the Store for the app.
We will setup our react components to call an action when an event happens. We’ll send any data we want to the Actions. The action will then send out a message to the Reducers, saying here’s what I want to do and here’s the data I’ve been given.
We currently don’t need any Actions in our app so we’ll leave it for the time being as a empty object.
const actions = {};The Reducer has access to the applications state. It will accept two parameters, one is the state and the other is the action. Since we have no actions setup, all we need to do is return the state, as it will never change. Once we start adding Actions we will need to come back here and add functionality so it returns a new modified state.
function reducer(state = [], action) {
return state;
}As part of the setup, we will create a new component called AppContainer that will wrap around our App component. We’ll use functionality from the React Redux in this part of the setup. This section here may be worth rereading once you’ve learned how the rest of the application works.
mapStateToProps function will subscribe the AppContainer
component to any changes that happen in the store. Any time a change
happens in the store, this function is called and the new stores state
will be merged into the AppContainer component props.
mapDispatchToProps function will take your Actions and
merge them into the AppContainer’s component props, so we can call
Actions from components.
const AppContainer = connect(
function mapStateToProps(state) {
return {
attendees: state
};
},
function mapDispatchToProps(dispatch) {
return bindActionCreators(actions, dispatch);
}
)(App);The bottom line in the example above is where we tell the
AppContainer component to wrap around the (App);
component.
createStore function will create a Redux store for the
application. There should only be one of these and it will contain the
state. We will pass our Reducers into it and the default state, which
will be the attendee list.
// createStore(reducer, defaultState);
const store = createStore(reducer, attendeeList);As part of using React Redux bindings, we need to wrap our
AppContainer component in a Provider tag. The Provider tag wraps our
whole application. The AppContainer has access to the store through the
connect() function.
render(
<Provider store={store}>
<AppContainer />
</Provider>,
document.getElementById('app')
);When you write your react component, it will know nothing. It will just render the HTML that you wrote inside it’s render function. You can pass your component data through props.
We will access the attendee list data using
this.props.attendees, which we created in MapStateToProps.
We will loop over it and render out each of the attendees names.
When we have dynamic content we will need to give items a unique
identifier so React can work out what’s what. We can do this by adding a
key attribute to the list item key={index}. If you forget
this, then you’ll get a error in your console saying ‘Each child in an
array should have a unique “key” prop.’
Below you can see these steps working together to render the Attendee list.
See the Pen 2/4 Building your first React Redux App by Allan Pope (@allanpope) on CodePen.
The next step is to display each attendees name in a ‘hello my name is’ badge. One way to do this would be to add the badge HTML to our App component. This would work but we want to have code that does different things separated. Instead lets create a new badge component, which will render the badge HTML.
class Badge extends React.Component {
render() {
var style = {backgroundColor: this.props.attendee.color};
return (
<div className="hello-badge" style={style}>
<p className="hello-badge__title"><span className="hello-badge__hello">Hello</span><br /> my name is</p>
<p className="hello-badge__name">{this.props.attendee.name}</p>
</div>
)
}
}You’ll notice that we use ClassName="" instead of
class="" this is because we are using JSX syntax. You can
learn more about that here.
You’ll also see that we are using the attendees favourite colour provided in the data to give each attendee their own coloured badge.
To use our new badge component we will need add it to our App
component. Since our badge component knows nothing about the attendees
we will need to pass in our attendee through props. We can access it
inside the component using this.props.attendee
(this.props.somethingWePassedIn).
<ul className="attendees">
{this.props.attendees.map((attendee, index) =>
<li className="attendees__attendee" key={index}>
<Badge attendee={attendee} />
</li>
)}
</ul> See the Pen 3/7 Building your first React Redux App by Allan Pope (@allanpope) on CodePen.
We will now refactor our app component slightly. Initially when we created it, it’s responsibility was to be a wrapper for the application. It now has the responsibility of how to render out an attendee list. Lets create a new component called attendees, which will render the list. We will move our list HTML inside the new component and pass it the attendee list.
Our new App component is now simpler.
class App extends React.Component {
render() {
return (
<div>
<h1>Attendees</h1>
<hr/>
<Attendees attendees={this.props.attendees} />
</div>
)
}
}
class Attendees extends React.Component {
render() {
return (
<ul className="attendees">
{this.props.attendees.map((attendee, index) =>
<li className="attendees__attendee" key={index}>
<Badge attendee={attendee} />
</li>
)}
</ul>
)
}
}See the Pen 4/7 Building your first React Redux App by Allan Pope (@allanpope) on CodePen.
We’ve got our badges displaying nicely and outputting our attendees. Now we want to add the functionality so that we can add more attendees.
We need to create a form which will take the attendees name and favourite colour. Then we’ll need to send the data off somewhere so we can store it in our stores state. Lets create a AddAttendee component.
The AddAttendee component HTML will make up a form with some basic Foundation classes for styling.
class AddAttendee extends React.Component {
render() {
return (
<div className="row">
<div className="medium-6 medium-offset-3 columns">
<form ref="addAttendee">
<label for="name">Name</label>
<input id="name" type="text" ref="name" placeholder="John Doe" />
<label for="color">Favourite color</label>
<input id="color" type="text" ref="color" placeholder="#2e2e2e" />
<button type="submit" className="button">Add attendee</button>
</form>
</div>
</div>
)
}
}As you can see, it’s mainly just a basic HTML form. One thing to note
is that we have added ref="" attributes to the inputs and
form. This is because we need a way to access those elements when the
form submits.
Lets add an onSubmit event handler to our form and bind it with the forms context.
<form ref="addAttendee" onSubmit={this.handleSubmit.bind(this)}>
When the form submits it will now call handleSubmit().
Using the ref’s we can grab the values from our two inputs.
handleSubmit(e) {
// Stop page refreshing
e.preventDefault();
// Store reference to our form references
let refs = this.refs;
// Users name
let name = refs.name.value;
// Users favourite colour
let color = refs.color.value
// Trigger action
this.props.addAttendee(name, color);
// Reset form so our inputs are empty again
refs.addAttendee.reset();
}Note the line this.props.addAttendee(name, color);. This
is where we a calling an action. The Actions name could be anything. We
haven’t created this action yet so we’ll need to do that now.
When we setup Redux earlier we created a empty Actions object
const actions = {}. Our onSubmit handler is calling
addAttendee() so lets add it. We can add as many Actions as
we like here.
const actions = {
addAttendee: (name, color) => {
return {
// String for Reducer to pick up
type: 'ADD_ATTENDEE',
// Randomly generated id
id: uuid.v4(),
// Name and colour we sent through from the form
name,
color
}
}
};What’s important here is the type: 'ADD_ATTENDEE. This
is just a string value. It can be called whatever you want it to be. It
should just describe what it’s doing. The Reducers will be listening for
Actions. Lets create a Reducer that will store our new attendee’s name
and favourite colour when the addAttendee action has been called.
Our Reducer is going to be a switch statement. Here we have access to
the applications state and the action being sent to it. The switch
statement is passed the action.type which was this
type: 'ADD_ATTENDEE' line that we put in the Actions. If
the action.type does not match anything we just return the default
state.
Inside of the case 'ADD_ATTENDEE': we return a new
array. We store our new attendee’s details in a object. We also do
..state which concatenates our old state into our new
array. It’s important in Redux that data is immutable, meaning you
should always create and return a new version of the state, instead of
modifying the exisiting state.
function reducer(state = [], action) {
switch (action.type) {
case 'ADD_ATTENDEE':
// Return a new array with old state and added attendee.
return [{
name: action.name,
color: action.color
},
...state
];
default:
return state;
}
};Go ahead and enter your name and favourite colour into demo and see it pop up as a badge.
See the Pen 5/10 Building your first React Redux App by Allan Pope (@allanpope) on CodePen.
We now have a fully functioning React Redux Application. Let’s extend this further by adding the functionality to remove someone.
Lets create a RemoveAttendee component. We attach a
handleOnClick() event to the button. When the function runs
it will call the action removeAttendee(index) with the
index of the attendee we want to remove.
class RemoveAttendee extends React.Component {
handleOnClick() {
let index = this.props.index;
this.props.removeAttendee(index);
}
render() {
return (
<button className="alert button tiny" onClick={this.handleOnClick.bind(this)}> × Remove attendee</button>
)
}
}We will add the RemoveAttendee component to the Attendee’s component list. So when every attendee badge is rendered out, it will also render a remove button. We’ll also pass through the index of the attendee and the removeAttendee action.
<ul className="attendees">
{this.props.attendees.map((attendee, index) =>
<li className="attendees__attendee" key={index}>
<Badge attendee={attendee} /> <RemoveAttendee removeAttendee={this.props.removeAttendee} index={index} />
</li>
)}
</ul> The Actions setup will be very similar to the addAttendee action.
We’ll add the removeAttendee() function with the correct
type and the index data to pass along.
removeAttendee: (index) => {
return {
type: 'REMOVE_ATTENDEE',
index
}
}We will set up another case statement in the Reducer called ‘REMOVE_ATTENDEE’. We are returning a new version of the state, excluding the one we wanted to delete from the previous state.
case 'REMOVE_ATTENDEE':
return [
// In the array grab the state from beginning to index of one to delete
...state.slice(0, action.index),
// Grab state from the one after one we want to delete
...state.slice(action.index + 1)
];Go ahead try removing someone from the list.
See the Pen 6/10 Building your first React Redux App by Allan Pope (@allanpope) on CodePen.
That was a lot, if you made it this far, well done. If I’ve missed something or got something wrong, please let me know, I’m still learning how React & Redux works. Using React and Redux to build a simple application like this would be overkill, but hopefully this gave you foundation skills to go ahead and start build something more complex.