Making the App component dynamic

We are now going to make the App component dynamic by adding functionality to fetch, add, toggle, filter, and remove todo items. Furthermore, we are going to define a StateContext provider.

Let's start making the App component dynamic:

  1. In src/App.js, import the StateContext, after the other import statements:
import StateContext from './StateContext'
  1. Then, import the fetchAPITodos and generateID functions from the src/api.js file:
import { fetchAPITodos, generateID } from './api'
  1. Next, we are going to modify our App class code, implementing a constructor, which will set the initial state:
export default class App extends React.Component {
constructor (props) {
  1. In this constructor, we need to first call super, to make sure that the parent class (React.Component) constructor gets called, and the component gets initialized properly:
        super(props)
  1. Now, we can set the initial state by setting this.state. Initially, there will be no todo items, and the filter value will be set to 'all'
        this.state = { todos: [], filteredTodos: [], filter: 'all' }
}
  1. Then, we define the componentDidMount life cycle method, which is going to fetch todo items when the component first renders:
    componentDidMount () {
this.fetchTodos()
}
  1. Now, we are going to define the actual fetchTodos method, which in our case, is simply going to set the state, because we are not going to connect this simple app to a backend. We are also going to call this.filterTodos() in order to update the filteredTodos array after fetching todos:
    fetchTodos () {
fetchAPITodos().then((todos) => {
this.setState({ todos })
this.filterTodos()
})
}
  1. Next, we define the addTodo method, which creates a new item, and adds it to the state array, similar to what we did in our blog app using Hooks:
    addTodo (title) {
const { todos } = this.state

const newTodo = { id: generateID(), title, completed: false }

this.setState({ todos: [ newTodo, ...todos ] })
this.filterTodos()
}
  1. Then, we define the toggleTodo method, which uses the map function to find and modify a certain todo item:
    toggleTodo (id) {
const { todos } = this.state

const newTodos = todos.map(t => {
if (t.id === id) {
return { ...t, completed: !t.completed }
}
return t
}, [])

this.setState({ todos: newTodos })
this.filterTodos()
}
  1. Now, we define the removeTodo method, which uses the filter function to find and remove a certain todo item:
    removeTodo (id) {
const { todos } = this.state

const newTodos = todos.filter(t => {
if (t.id === id) {
return false
}
return true
})

this.setState({ todos: newTodos })
this.filterTodos()
}
  1. Then, we define a method to apply a certain filter to our todo items:
    applyFilter (todos, filter) {
switch (filter) {
case 'active':
return todos.filter(t => t.completed === false)

case 'completed':
return todos.filter(t => t.completed === true)

default:
case 'all':
return todos
}
}
  1. Now, we can define the filterTodos method, which is going to call the applyFilter method, and update the filteredTodos array and the filter value:
    filterTodos (filterArg) {
this.setState(({ todos, filter }) => ({
filter: filterArg || filter,
filteredTodos: this.applyFilter(todos, filterArg || filter)
}))
}
We are using filterTodos in order to re-filter todos after adding/removing items, as well as changing the filter. To allow both functionalities to work correctly, we need to check whether the filter argument, filterArg, was passed. If not, we fall back to the current filter argument from the state.
  1. Then, we adjust the render method in order to use state to provide a value for the StateContext, and we pass certain methods to the components:
    render () {
const { filter, filteredTodos } = this.state

return (
<StateContext.Provider value={filteredTodos}>
<div style={{ width: 400 }}>
<Header />
<AddTodo addTodo={this.addTodo} />
<hr />
<TodoList toggleTodo={this.toggleTodo} removeTodo={this.removeTodo} />
<hr />
<TodoFilter filter={filter} filterTodos={this.filterTodos} />
</div>
</StateContext.Provider>
)
}
  1. Finally, we need to re-bind this to the class, so that we can pass the methods to our components without the this context changing. Adjust the constructor as follows:
            constructor () {
super(props)

this.state = { todos: [], filteredTodos: [], filter:
'all' }

this.fetchTodos = this.fetchTodos.bind(this)
this.addTodo = this.addTodo.bind(this)
this.toggleTodo = this.toggleTodo.bind(this)
this.removeTodo = this.removeTodo.bind(this)
this.filterTodos = this.filterTodos.bind(this)
}

Now, our App component can dynamically fetch, add, toggle, remove, and filter todo items. As we can see, when we use class components, we need to re-bind the this context of the handler functions to the class.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.227.194