Skip to main content
- Class Components
- The React Lifecycle
- Composing and Sharing Non-Visual Behavior
- Testing Web Applications
- Class Components
- The React Lifecycle
- Composing and Sharing Non-Visual Behavior
- Testing Web Applications
React is a very sophisticated UI - Component Library that can be used together with a few other libraries to build out large scale Single Page Applications. But as our Application’s scale in size so too will the data that we are concerned with within our applications. And you can only start to imagine how difficult it might be manage all of that data inside of React components. Redux is a library that can help solve this problem by providing a single source of truth for our data within our application. As your team grows, and as your application scales, Redux can help offset the pains that can be brought on as a result of complexity.
At the end of this module, you should be able to:
- explain what Redux is and the problem it solves
- create a Redux Store and connect it to a React application
- use the connect() function to "connect" React components to the Redux store
- write Actions and Action Creators to describe state changes
- write Reducers to respond to actions and update state
Be on time. It’s a mark of respect not to keep others waiting.
Review each preclass resource before class.
-
Try watching this video on www.youtube.com, or enable JavaScript if it is disabled in your browser.
-
Try watching this video on www.youtube.com, or enable JavaScript if it is disabled in your browser.
-
Try watching this video on www.youtube.com, or enable JavaScript if it is disabled in your browser.
-
Try watching this video on www.youtube.com, or enable JavaScript if it is disabled in your browser.
-
Try watching this video on www.youtube.com, or enable JavaScript if it is disabled in your browser.
-
Try watching this video on www.youtube.com, or enable JavaScript if it is disabled in your browser.
-
Presentational and Container Components
A well thought out description of how we can think about our components in react.
Managing application state and keeping it in sync across all layers of our application is very important.
Many bugs are caused by applications going into an inconsistent state.
Redux is a predictable state management library for JavaScript applications and is the most popular State container for React applications. By now, we have discovered that building out applications using React requires a lot of forethought to give an application scalability. Specifically, we have noticed some complications around the area of state-management. You may have asked yourself a question like “Which of my components should have state vs. which of my components should just be a way to present some DOM elements?”
Luckily you’re not alone in this dilemma. In fact, the Facebook team that built React in the first place noticed that managing state could become a nightmare at scale were they only to use component state. So, they built a pattern, and said, “everyone here at Facebook is going to write code after this pattern.” This way, they could eliminate many of the problems that unwieldy state-full components could surface. That pattern was called Flux link to docs, and it’s primary use case was to add some stringency to the React ecosystem because by itself, React is very unopinionated in how one should be designing their application and managing state.
Flux was great, but developers had a hard time with implementation because the pattern presented a few other problems. Because of this, (and around the same time that React was becoming so popular) Dan Abramov built out a ‘Time-traveling’ approach to debug an application. This method eventually became known as Redux. Dan wanted to be able to go back in time to see when/where the state had changed in his application, and to do that, he ended up creating one of the most popular state-management libraries known to React Developers today.
Redux is a small, light-weight state container for use when building JavaScript applications. Remember, Redux has nothing to do with React other than the fact that many developers use them together. The core concepts/principles of Redux are 3 fold:
Everything that changes within your application is represented by a single JavaScript Object known as the store. The store contains our state for our application.
When the application state changes, we clone the state object, modify the clone, and replace the original state with the new copy. We never mutate the original object, and we never write to our store object.
Given the same input, a pure function returns the same output every time. All functions (reducers) in Redux must be pure functions. Meaning they take in some state and a description of what changes took place and return a copy of our state.
Redux is pretty simple at its core, the complications with Redux arise when we try and implement it within a React application. Usually, these issues are because there is some new syntax, and it’s just a matter of time spent learning to sort them out.
Watch the first 4 videos in this series and try to wrap your head around how this would be useful in a React application. Think of your projects that you’ve built in React up until this point. What types of problems would Redux have solved in those projects if any?
Write your thoughts on why Redux exists and at least one reason to use it as well as one reason to not use it.
- Introduction to Redux
Article on Smashing Magazine that introduces the basics of Redux - A Cartoon Intro to Redux
Article that explains the basics of Redux in an entertaining manner
When using Redux, the state for the whole application is contained inside the Store. This objective will guide you through the process of what it takes to wire up a React application to use Redux.
In this section, we’ll learn how to create the Redux Store and how to use a library called react-redux to connect our React application to the Store. Because Redux is a standalone library, (meaning it can be used on its own or with another library/framework for state-management and data flow) we have to use a second helper package that will enable us to string together Redux within a React application. That package is called React-Redux. Some more documentation and information lives here. The packages React and Redux are entirely separate, as quoted in the Redux documentation.
From the very beginning, we need to stress that Redux has no relation to React. You can write Redux apps with React, Angular, Ember, jQuery, or vanilla JavaScript.
The first step we’re going to take to enable Redux within a React application is to install it. This process assumes you’ve used Create React App to boilerplate out a React application.
npm install react-redux redux
Now that we have redux and react-redux installed, let’s learn how to set it up within our application. We will use the createStore function from redux, so let’s import that first.
import { createStore } from 'redux';
createStore will take in a single reducer that represents the state (data) of our application globally. We need to create a store variable, and use createStore to create the Redux store.
const store = createStore(reducer);
You’ll notice that we passed a reducer into createStore, but we don’t have a reducer yet. We’ll learn a lot more about reducers soon. For now, let’s create a function called reducer that returns an object representing our state.
function reducer() {
return {
title: 'Hello world! I\'m in the Redux store!',
}
}
const store = createStore(reducer);
Now that we have a store, we want to make our application aware of it. The way this works is that react-redux gives us a <Provider></Provider> component that wraps around our entire application. We will pass our newly created store to that component as a prop.
Within our Root Component (usually Index.js), go ahead and import Provider from react-redux.
import { Provider } from 'react-redux';
Then, all we need to do is wrap our <App/> with the <Provider> component and pass a store prop set equal to the store we created. This will look like this:
<Provider store={store}>
<App/>
</Provider>
Let’s go ahead and take a peek at our application using the React Dev Tools now that we’ve wrapped it up in a Provider Component.
Now, take the time to think about where and when you’ve done this before? Is there a package that we’ve used this same way? If so, which package is it? It is important to note that a large number of packages that we use in React are implemented this way.
Write down a few thoughts on what you see, where you’ve seen similar patterns, etc, and send that to your PM.
Now that we have created a Redux store, we have to connect our components to that store in order to access the data they will need. We will use the connect function, and mapStateToProps to accomplish this.
Now that we have built a store to manage our state, we need to connect our components to that store. We can do so using the connect function, within the components themselves. We can also build a helper function within the component files to tell the connect function what pieces of state we want to access. This function is usually named mapStateToProps, and it will map pieces of our Redux state to the props of our component. Let’s try it out.
Using the app you created earlier that has the redux store wired up, change the object you initially returned out of the reducer function to look like this:
{
user: {
name: 'Dustin'
},
movies: [
'Star Wars',
'Lord of the Rings',
'Harry Potter'
],
todoList: [
{ task: 'Learn Redux', id: 0, completed: false }
],
moviesToWatch: 13
}
Now create a component called MovieList. Next, we’ll take a look at the syntax we use to connect our React component to Redux, then we’ll talk about it. To start, import the connect function into your component:
import { connect } from 'react-redux';
Next, we use the connect function, where we export the component at the bottom of the file. We invoke connect twice (function currying). First with two arguments - a function and an object. Second with just the component we are trying to connect. For now, we’ll pass null and {} into the first invocation.
// export default MovieList; Not this way if we are connecting this component!
export default connect(null, {})(MovieList)
Now MovieList is connected to the store. Let’s write our mapStateToProps function now, to tell connect which pieces of our state we want to bring in to this component. This function takes in state as a parameter, then returns an object where the properties can be passed to props, and the values are retrieved from the store for our component.
For a MovieList component, we probably only want to know about the movies array and the moviesToWatch number, maybe the user object. We’ll not worry about the todoList, since our component doesn’t need to know about that part of our state. Let’s bring those three pieces of our state into the component.
const mapStateToProps = state => {
return {
movies: state.movies,
moviesToWatch: state.moviesToWatch,
user: state.user
}
}
Let’s pass this in as the first argument to the first connect invocation. Notice that state is being passed into this function. Under the hood, connect passes our entire state tree to mapStateToProps. That means that within that function, we have access to all our state via the state argument. But, the component only receives the pieces of state that we turn out of mapStateToProps.
export default connect(mapStateToProps, {})(MovieList)
Now, if you look at the props in the React tools, you will see that all three pieces of our state have been passed to our component through the connect function! As a side note, other props we’ve passed to this component the traditional way are still going to be available.
By the way, did you notice that we are using a function that takes in a component, extends its functionality, and returns a component? connect is a HOC!!!`
Create a new application. Add the redux and react-redux packages. Create a redux store with some test data (have fun with this part!). Build a component and connect that component to the store using connect and a mapStateToProps function. Render the connected data from your connected component.
The Redux store is read only. The only way to modify application state when using Redux is by dispatching actions.
We can use action creators and the react-redux library in order to dispatch those actions.
In the world of Redux there’s a whole new set of programmer jargon. The first we’ll talk about here is actions.
Actions in Redux are packets of information that contain an action type and associated data.
In code, an action is simply an object with up to two properties - a type property and an optional payload property. Each action MUST have a type property. The type property is a string that explains what interaction just happened. By convention, we use all caps and underscores for types - ie 'LOGIN_USER or TOGGLE_TODO. The payload property is data that goes along with that interaction.
Actions are “dispatched” to our reducer - aka, passed into the reducer function as an argument. When our reducer recieves an action, it will update the state according to the type and payload on the action.
Let’s say we have a toggle handler function that switches a boolean field called show, which is set on our state in our Redux store. An action for such an event would look like this:
{ type: "TOGGLE_SHOW", show: !state.show };
This allows us to keep things as simple as possible when responding to events and interactions!
Importantly in Redux, reducers are the only place we can update our state. Actions tell our reducers “how” to update the state, and perhaps with what data it should be updated, but only a reducer can actually update the state. More to come on reducers later..
From what we’ve learned so far, we can start to see the flow of data in a React/Redux application:
Store sets the state ->
Event or user interaction happens ->
An action desctibing the event and possible changes is dispatched to reducer ->
The reducer handles the action and replaces the store accordingly.
Actions should not be confused with action creators (though admittedly, it’s very easy to confuse them). An action creator is a function that creates an action. Or in other words, an action creator is a function that returns an action object.
Action creators are a middle step between events/interactions and the dispatch process. They make it possible to write reusable functions that can create actions on the fly, rather than us hard coding actions into our components. With action creators in mind, here is an updated look at our data flow:
Store sets the state ->
Event or user interaction happens ->
An action creator is called and dispatches an action ->
Actions tell us about the changes from the event ->
Reducers handle those actions and replace the store accordingly.
This flow is one of the reasons that Redux is so powerful. The two major principles here are Functional Programming and Immutability. Dispatched actions are the trigger for reducers, and reducers are pure functions that never produce any side-effects. Everything you do in Redux is functional.
The final term we want to cover here is action types. We’ve talked about, and even demonstrated the type property of an action. We want to change that up right now, ever-so-slightly… Instead of passing a string to action.type we create a variable with the name of the string, and assign it the string we would have passed to an action. Then we give action.type the variable as it’s value.
We do this because we deal with strings like we deal with types. Strings are used in multiple places like you’ll see in reducers very soon, misspellings occur, and are very hard to debug. If we misspell our action type in our reducer, our state won’t be updated correctly, and we’ll be left wondering what went wrong.
Instead, we’ll create an action type, and import it wherever we need it. That way, with linters IntelliSense in our code editor, we can spot errors a lot quicker. This is what action types looks like:
export const TOGGLE_SHOW = 'TOGGLE_SHOW';
// then in our action:
{ type: TOGGLE_SHOW, payload: !state.show }
Now let’s try all of this in code!
Let’s build our first action creator. We’ll assume that we have a Redux store connected to our app, and a component connected to the store. On the state object, we have a title property brought into the component via the mapStateToProps function. Our component has an input field and an “update title” button. When we type into the input and click the button, we want to update our state object with our new title. (You can start with this codesandbox)
First, we need to create a new folder in the src folder called actions. Inside that folder, create an actions.js file. Inside that file, create a variable called UPDATE_TITLE with the value 'UPDATE_TITLE'. The variable is an action type. Next, let’s create a function called updateTitle that takes in a new title. This will be our action creator, and it will simply return an action with the type UPDATE_TITLE and a payload of the new title we passed into it. Don’t forget to export both the action type and the action creator function. (Note that these will be named exports, so they will be imported with curly brace sytnax - import { namedExport } from './place';)
export const UPDATE_TITLE = 'UPDATE_TITLE';
export function updateTitle(newTitle) {
return {
type: UPDATE_TITLE,
payload: newTitle
}
}
See how easy that was? It sounded pretty scary up above, but in practice, this is all very concise and intuitive. Now we have an action creator that can dispatch our action to the reducer and send the reducer the new title. Let’s import our action creator into our component, and talk about how to use it there.
When we use action creators in our connected components, we first import the action creator. Then, we pass the action creator into the connect function. Action creators are passed to the object that is the second argument in the first connect invocation.
export default connect(mapStateToProps, { actionCreator: actionCreator })(Component);
Then, just like the state pieces that we brought into our component via the mapStateToProps function, we have access to our action creator in props. This step is important because it is the connect function that works in the background to actually dispatch our actions to the reducer. We can’t just import an action creator and use it in our component. It must go through connect and be used from the props object.
Let’s see how we would do this in the app we have been building. Go to the Title component, import updateTitle, and pass it into the connect function.
import React from 'react';
import { connect } from 'react-redux';
import { updateTitle } from '../actions/actions';
...
export default connect(
mapStateToProps,
{ updateTitle } // same as { updateTitle: updateTitle }
)(Title);
Now when the “Update title” button is pushed, invoke a function on the class that invokes this.props.updateTitle that gets this.state.newTitleText passed into it.
updateTitle = (e) => {
e.preventDefault();
this.props.updateTitle(this.state.newTitleText)
}
...
<button onClick={this.updateTitle}>Update title</button>
Yes, names will be the same all over the place with this stuff. Just note that props.updateTitle is the action creator.
To make sure it is working, let’s add a console.log in the action creator, and log out the newTitle that is passed into it. Since we don’t have a reducer to handle this action yet, we won’t see the state or title updated yet. So this is the best way we can make sure it’s working.
export function updateTitle(newTitle) {
console.log(newTitle);
return {
type: UPDATE_TITLE,
payload: newTitle
}
}
Here’s what our codesandbox should look like now.
In the application you created earlier, look at the state you are displaying. Find something easy, like a string or a number, to update. (Add a string to your state if you only have arrays or objects. This will keep things simple today while we learn this brand new state management flow). Build an action creator, pass it in to your component, and call that action creator (from props) after some interaction like a button click. Make sure you have a console.log in the action creator so you will know if it’s working.
Reducers calculate the new version of state based on the current state and a given Action. The best way to think about Reducers is as the make-up of our state. So when we create a reducer it can be described as the portion of state that it represents. Reducers are pure functions and they are the glue that bind together our Actions and our Store.
When an action is dispatched, it flows through every reducer in a script. Redux was built on this fundamental principle, among others.
Reducers are pure functions, meaning they don’t produce any side-effects. A reducer follows the key principles that come from the Array.reduce() function, in that they behave similarly to the callback that you would pass to reduce. They aren’t the same thing, but they behave and do similar things, so that is an appropriate comparison to keep in mind.
Reducers take in two arguments, the current state from the Redux store, and the action object, sent via action creator functions. Remember that an action gives us a packet of information as an object with a type and payload field that we can use. The type tells the reducer what to do, and the payload tells the reducer what to update on state.
Reducers will NEVER update state directly, they only return a new object Remember this as you craft your reducers - that they are not only functional and pure, but they lend themselves to the pattern of immutability well.
Inside reducers, we use switch statements to look at the action type, then return the updated state. switch statements are like long if / else if / else if / ... / else statement. If you haven’t encountered a switch statement yet, read this before continuing.
Back in our title app, let’s create another folder inside src called reducers, and inside that, a reducer.js file. To start, we will create an initialState object that looks a lot like the one returned in the reducer function in index.js. (You can start here if you need to).
const initialState = {
title: 'Title from Redux store'
};
Then we’ll create the reducer function. Remember, it takes two arguments - state (with the initial state as a default - watch the syntax there), and action. Then it will contain a switch statement that looks at action.type and return state as the default.
function reducer(state = initialState, action) {
switch(action.type) {
default:
return state;
}
}
export default reducer;
Next, we need to build a case for our action. The dispatched action is the UPDATE_TITLE action. So, we’ll want to import the action type from the actions.js file first. It is a named export, so you need to use brackets to import it.
import { UPDATE_TITLE } from '../actions/actions';
Then, in the case for UPDATE_TITLE, we will return a new state object. We do this be returning an object with state spread into it, and updating the title property with action.payload.
function reducer(state = initialState, action) {
switch(action.type) {
case UPDATE_TITLE:
return {
...state,
title: action.payload
}
default:
return state;
}
}
export default reducer;
Take a minute to study that syntax. Here are a couple of important details:
- We are returning a brand new object. We do NOT update the state object and return that
- We are using the
spread operatorto “spread” in our old state - We then update the one piece of data we want to update -
title
This syntax is the convention when building reducers. This is something that takes some good repetition to get used to!
Now that we have built our reducer, we need to import it into the index.js file and pass it into the createStore function in place of the fake one we built earlier. The convention for the import is to call it rootReducer inside index.js.
import rootReducer from './reducers/reducer';
const store = createStore(rootReducer);
Now we have come full circle! Go ahead and test out your app to make sure it is working. Great job creating your first ever React/Redux application! Here is a working codesandbox with everything we have implemented for this app.
In your app that you were working on earlier, create a reducer that will handle state changes for whatever action you are dispatching from your action creator. Pass the reducer into your createStore function and test out your app.
A short exercise highlighting the properties of arrays and objects, in regard to passing values by reference.
-
In this project you are given the skeleton/structure/styling of a React app. There is even some state built in for you. Your job will be to implement React-Redux as the state management system for this application.
You can use class recordings to help you master the material.
To demonstrate mastery of this module, you need to complete and pass a code review on each of the following:
-
Objective challenge:
Write your thoughts on why Redux exists and at least one reason to use it as well as one reason to not use it.
-
Objective challenge:
Let’s go ahead and take a peek at our application using the React Dev Tools now that we’ve wrapped it up in a Provider Component.
Now, take the time to think about where and when you’ve done this before? Is there a package that we’ve used this same way? If so, which package is it? It is important to note that a large number of packages that we use in React are implemented this way.
Write down a few thoughts on what you see, where you’ve seen similar patterns, etc, and send that to your PM.
-
Objective challenge:
Create a new application. Add the
reduxandreact-reduxpackages. Create a redux store with some test data (have fun with this part!). Build a component and connect that component to the store usingconnectand amapStateToPropsfunction. Render the connected data from your connected component. -
Objective challenge:
In the application you created earlier, look at the state you are displaying. Find something easy, like a string or a number, to update. (Add a string to your state if you only have arrays or objects. This will keep things simple today while we learn this brand new state management flow). Build an action creator, pass it in to your component, and call that action creator (from props) after some interaction like a button click. Make sure you have a console.log in the action creator so you will know if it’s working.
-
Objective challenge:
In your app that you were working on earlier, create a reducer that will handle state changes for whatever action you are dispatching from your action creator. Pass the reducer into your
createStorefunction and test out your app. -
Guided Project: Immutability Exercise
-
Project: Car Sales
-
Code Challenge: LinkedListCycle
-
Code Challenge: BalancedBrackets