notes1
Objective 1 - explain what Redux is and the problem it solves: WEB43 - 3.2
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?"
Overview
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 (Links to an external site.), 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 (Links to an external site.) 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:
The Store
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.
Application state is Immutable
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.
Pure functions change our state
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.
Follow Along
Watch the first 4 videos in this series (Links to an external site.) 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?
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 2 - create a Redux Store and connect it to a React application: WEB43 - 3.2
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 (Links to an external site.). Some more documentation and information lives here (Links to an external site.). The packages React and Redux are entirely separate, as quoted in the Redux documentation.
Overview
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 (Links to an external site.). Some more documentation and information lives here (Links to an external site.). 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.
Follow Along
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.
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.
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.
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.
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
.
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:
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 3 - use the connect function to connect React components to the Redux store: WEB43 - 3.2
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.
Overview
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.
Follow Along
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:
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:
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.
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.
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
.
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!!!`
Challenge
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.
Objective 4 - write Actions and Action Creators to describe state changes: WEB43 - 3.2
In the world of Redux there's a whole new set of programmer jargon. The first we'll talk about here is actions.
Overview
In the world of Redux there's a whole new set of programmer jargon. The first we'll talk about here is actions
.
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:
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:
Action creators
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:
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.
Action types
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:
Now let's try all of this in code!
Follow Along
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 (Links to an external site.))
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';
)
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.
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.
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.
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.
Here's what our codesandbox (Links to an external site.) should look like now.
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.
Last updated