- Implement reducer and action in the Todo app.
- Connecting redux state and dispatching actions in a container component.
- Implement a React lifecycle method.
- Dispatch actions for creating initial todo items in the lifecycle method
componentDidMount.
At the moment we have a list of todos in our component state in App.jsx. The goal now is that Redux will own all state and operations that mutate that state in the application, primarily the list of todos.
✏️ The easiest solution for now is to just cut the list from our component state in App.jsx and paste it in as the default state in todosReducer.
Now the state exists in Redux but we have yet to connect that state to our React components.
✏️ Create a new file TodoListContainer.jsx.
Let's write it out step by step.
- ✏️ Import React and the magic glue from Redux to connect the state with the component. We also need our Component that knows how to render out the todo list:
import React from "react";
import { connect } from "react-redux";
import TodoList from "./TodoList";- ✏️ Next, we create the Container-component:
const TodoListContainer = props => <TodoList {...props} />;- ✏️ Now for the Redux magic to select what React props we want to map to what Redux state. Remember, the
TodoListexpects antodoItemsprop of type array that contains instances ofTodoclass instances.:
const mapStateToProps = state => ({
todoItems: state.todos
});- ✏️ Next, we connect the TodoListContainer and the mapStateToProps together. Note that we just pass
nullasmapDispatchToPropsbecause we don't have any functions to connect yet. You can leave the parameter unset/undefined if you want.
export default connect(
mapStateToProps,
null
)(TodoListContainer);- ✏️ Finally, we have to use the TodoListContainer component in our App instead of the TodoList component. Open App.jsx and import and use TodoListContainer instead of TodoList. Since the TodoListContainer component now takes care of loading the list of todoItems, this prop can be removed. Using this component should be as straight-forward as
<TodoListContainer />.
✏️ Make sure the list of todos still renders in the browser.
✏️ Keep Redux dev tools open in Chrome. Use the State and Tree modes and verify that the todos node in the state inspector now has the list of todos.
Now that we have moved initial state to Redux, the next goal is to implement the whole Redux chain: Dispatching an createTodo action that is received and handled in the reducer and then added to the list and displayed in the GUI.
Remember this one-way data flow:
The first step, so we can slowly get familiar with Redux, is to dispatch one createTodo action for each initial todo we have hard-coded into the reducer so far.
✏️ Create a new file todoActions.js.
✏️ Create the createTodo action:
export const createTodo = description => ({
type: "CREATE_TODO",
description
});✏️ Open todosReducer.js and add a new case that handles the CREATE_TODO action type.
✏️ Implement the case for CREATE_TODO. Make the new state be the existing list of todos plus the new one contained in action.description. You can use the code examples in exercise 4 if you're stuck (but please try first).
❗ Remember to avoid modifying the existing state/list of todos. You want to create a new list containing copies of the items plus the new todo item.
The last thing is that we must dispatch actions from an React component when it initially mounts in order to get the todos created. To do that, we need to make use of React's lifecycle methods.
React components have various lifecycle methods (see this diagram for a visual representation). From the React docs:
Each component has several “lifecycle methods” that you can override to run code at particular times in the process. In the list below, commonly used lifecycle methods are marked as bold. The rest of them exist for relatively rare use cases.
These methods are called in the following order when an instance of a component is being created and inserted into the DOM:
constructor()static getDerivedStateFromProps()render()componentDidMount()An update can be caused by changes to props or state. These methods are called in the following order when a component is being re-rendered:
static getDerivedStateFromProps()shouldComponentUpdate()render()getSnapshotBeforeUpdate()componentDidUpdate()This method is called when a component is being removed from the DOM:
componentWillUnmount()
The most used lifecycle method is componentDidMount. Dispatching actions from this method is the recommended way to fetch initial data and do whatever else is needed to get the component up and running.
Remember that we want stuff related to how things work in Container-components and stuff related to how things looks in plain, dumb, Component-components. That means we want to implement componentDidMount in TodoListContainer.
Lifecyle methods can only be implemented in React class components and not in pure components (because a pure component is just a plain function which cannot have additional functions.
💡 However, never versions of React have a feature called Hooks. Hooks will enable you to use React's state and lifecycle features in components without writing a class. This feature is beyond the scope of this workshop, but may be covered in the future.
✏️ Refactor TodoListContainer to be a React class component instead of a pure component:
import React, { Component } from "react";
import TodoList from "./TodoList";
class TodoListContainer extends Component {
componentDidMount() {}
render() {
return <TodoList {...this.props} />;
}
}But how do we get the createTodo action we made earlier into componentDidMount so we can dispatch it? By using mapDispatchToProps.
✏️ Import the createTodo action from todoActions.js and connect it using mapDispatchToProps.
import { createTodo } from './todoActions';
/* ... */
const mapDispatchToProps = dispatch => ({
createTodoItem: description => dispatch(createTodo(description));
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(TodoListContainer);Now we'll get this function available as the prop named createTodoItem and we can simply call this function with a description to create new todos.
Full TodoListContainer at this point. Please write it out yourself and don't copy & paste:
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import TodoList from "./TodoList";
import { createTodo } from "./todoActions";
import Todo from "./Todo";
class TodoListContainer extends Component {
componentDidMount() {
this.props.createTodoItem("Wake up");
this.props.createTodoItem("Do the dishes");
this.props.createTodoItem("Fold clothes");
this.props.createTodoItem("Browse Reddit");
}
render() {
return <TodoList todoItems={this.props.todoItems} />;
}
}
TodoListContainer.propTypes = {
todoItems: PropTypes.arrayOf(PropTypes.instanceOf(Todo)).isRequired,
createTodoItem: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
todoItems: state.todos
});
const mapDispatchToProps = dispatch => ({
createTodoItem: description => dispatch(createTodo(description))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(TodoListContainer);You should now see the same list of todo items render in the browser with no errors. It may not look like much progress in the GUI, but who cares about that, right?! As we all know, the important stuff is what happens in the dev tools 😆
❗ Remember to remove the default todo items in
todosReducerand make the default state be an empty array again.
✏️ Open Redux dev tools and inspect the left-hand panel. It should now contain 4 CREATE_TODO actions (or however many actions you dispatched).
✏️ As you select each action (while in the Diff mode), notice how the main panel shows how the state changed when that action was received and reduced in the store. This gets more interesting when we start editing individual properties in exisiting objects and other more complicated operations.
✏️ Switch to Action mode and see that you can inspect the action that was dispatched in detail.
✏️ Click actions in the left-hand panel and note the small Jump|Skip buttons to the right in each list entry. Click Jump and see how the GUI now shows how the app looks while in the state up until that point.
Pretty powerful debugging tools and extremely useful for understanding how state changes impacts your application 💪 😍
✏️ Play around with Redux dev tools and the different modes and inspectors. See what it can do and show you.
Speaking of dev tools, now that our app has grown a bit, let's give React dev tools another try.
✏️ Open React dev tools in Chrome. Expand the Component Nodes in the main window a bit. Note how you can see each component as it appears in code, with correct props as they appear in code.
✏️ Find the TodoList in the tree and select it. Inspect the right-hand panel and note that you can inspect the props it receives in detail. This is very useful when you just want to inspect all props the component actually receives at runtime, and what values they are.

