Now that we have defined our reducers, we can start migrating the App component. Let's migrate it now:
- Edit src/App.js and adjust the import statement to import useReducer, useEffect, and useMemo from React:
import React, { useReducer, useEffect, useMemo } from 'react'
- Import the appReducer function from src/reducers.js:
import appReducer from './reducers'
- Remove the class component code. We are going to define a function component instead now.
- First, we define the function, which is not going to accept any props:
export default function App () {
- Now, we define a Reducer Hook using the appReducer function:
const [ state, dispatch ] = useReducer(appReducer, { todos: [], filter: 'all' })
- Next, we define an Effect Hook, which is going to fetch todos via the API function, and then a FETCH_TODOS action will be dispatched:
useEffect(() => {
fetchAPITodos().then((todos) =>
dispatch({ type: 'FETCH_TODOS', todos })
)
}, [])
- Then, we implement the filter mechanism using a Memo Hook, in order to optimize performance and avoid re-computing the filtered todos list when nothing changes:
const filteredTodos = useMemo(() => {
const { filter, todos } = state
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
}
}, [ state ])
- Now, we define various functions that are going to dispatch actions and change the state:
function addTodo (title) {
dispatch({ type: 'ADD_TODO', title })
}
function toggleTodo (id) {
dispatch({ type: 'TOGGLE_TODO', id })
}
function removeTodo (id) {
dispatch({ type: 'REMOVE_TODO', id })
}
function filterTodos (filter) {
dispatch({ type: 'FILTER_TODOS', filter })
}
- Finally, we return and render all the components that are needed for our ToDo app:
return (
<StateContext.Provider value={filteredTodos}>
<div style={{ width: 400 }}>
<Header />
<AddTodo addTodo={addTodo} />
<hr />
<TodoList toggleTodo={toggleTodo} removeTodo={removeTodo} />
<hr />
<TodoFilter filter={state.filter} filterTodos={filterTodos} />
</div>
</StateContext.Provider>
)
}
As we can see, using a reducer to handle complex state changes makes our code much more concise and easier to maintain. Our app is now fully migrated to Hooks!