14. Fetching Data on Route Change

Video Link

To start, we'll remove the fetchTodos test API call from our index.js entry point because we want to fetch the todos from inside our VisibleTodoList component.

We'll start by importing fetchTodos into VisibleTodoList.js

import { fetchTodos } from '../api';

The VisibleTodoList component is generated by the connect and withRouter calls that each generate an intermediate component that injects props.

A good place to call our fetchTodos API would be inside the componentDidMount() lifecycle hook. However, we can't override the life cycle hooks of generated components! This means we have to create a new React component.

Creating a New React Component

Inside of VisibleTodoList.js, we'll import React and the Component base class from React. We will then declare a React component class called VisibleTodoList that extends the base component class.

import React, { Component } from 'react';
// other imports...

class VisibleTodoList extends Component {
  render() {
    return <TodoList {...this.props} />;
  }
}
.
.
.

We still want to render the presentational TodoList component exactly as before. The only purpose of adding this new class is to add the lifecycle hooks. Any props will be passed down to the TodoList.

Now that VisibleTodoList is defined as a class above, we can't declare another constant with the same name, so we reassign the VisibleTodoList binding to point to the wrapped component. We'll also change the connect() call to wrap our new class instead.

VisibleTodoList = withRouter(connect(
  mapStateToProps,
  { onTodoClick: toggleTodo }
)(VisibleTodoList));

export default VisibleTodoList;

The component generated by the connect() call will render the VisibleTodoList class we defined. The result of the connect and withRouter wrapping calls is the final VisibleTodoList component that we export from the file.

Adding Lifecycle Hooks

When the component mounts, we want to fetch the todos for the current filter.

It will be convenient to have the filter directly available as a prop, so we change mapStateToProps to calculate the filter from params like it used to do, but we will also pass it as one of the properties of the return object. So now we'll get both the todos and the filter itself inside of the VisibleTodoList component.

Updating mapStateToProps

const mapStateToProps = (state, { params }) => {
  const filter = params.filter || 'all';
  return {
    todos: getVisibleTodos(state, filter),
    filter,
  };
};

Going back to the lifecycle method, we can use this.props.filter inside componentDidMount. When the todos are fetched, fetchTodos returns a Promise. We can use the then method to access the resolved todos, and log the current filter and the todos we just received from the fake backend.

Implementing componentDidMount

class VisibleTodoList extends Component {
  componentDidMount() {
    fetchTodos(this.props.filter).then(todos =>
      console.log(this.props.filter, todos)
    );
  }

With our current implementation, running the app will show the all filter being printed, and the corresponding todos.

However, nothing will happen when filters are changed, because componentDidMount only runs once. To fix this, we need to add a second lifecycle hook called componentDidUpdate.

Implementing componentDidUpdate

// inside the `VisibleTodoList` below `componentDidMount()`
componentDidUpdate(prevProps) {
   if (this.props.filter !== prevProps.filter) {
     fetchTodos(this.props.filter).then(todos =>
       console.log(this.props.filter, todos)
     );
   }
 }

componentDidUpdate receives the previous props as an argument. We then compare the current and the previous values of the filter. If the current filter is not the same as the previous filter, we call fetchTodos() for the current filter.

Recap at 3:36 in video

results matching ""

    No results matching ""