As an alternate to useState, you could also use the useReducer hook that provides state and a dispatch method for triggering actions. In this lesson, we’ll centralize logic that is spread across a web application and centralize it using the useReducer hook.
Instructor: [00:00] Let's kick up a dev server for our React todo app. It has basic functionality of adding, removing, and marking off todos as well as having an about dialog. Let's look at the current code and talk about another way to solve it. The component is a functional component already using hooks, use state, use effect, use ref, custom hooks, etc.
[00:23] However, if we search for the update todos updater, you'll find it littered all over the code base. If you've heard of Redux, you may have also heard of a reducer. React provides a use reducer hook that I'd like to convert this code to.
[00:39] Where we're using our local storage hook, let's replace the update todos updater with dispatch and replace use local storage with use reducer, which takes a function and an initial value that we'll set to an empty array.
[00:56] The reducer's parameters are state and action. Inside the reducer, you will respond to the action and return what the state should be. We'll switch on the action.type and let's make up some types. We'll have an add todo. For now, let's just return the same state and we'll come back to that later.
[01:14] We'll have a delete todo, a toggle todo, and finally a default in case nothing matches and return the current state. Our handle new submit function will take the todo ID increment code and the update todos bit and put it in our copy paste buffer and replace it with dispatch with a type of add todo and a text of new todo.
[01:41] Then we could come up to our reducer under add todo and paste the code that we had below and return an array that spreads the current state and replaces new todo with action.text.
[01:55] As for the delete, we'll dispatch an action with type delete todo and ID. Then copy the inside of the update todos and paste it inside the reducer. Return state that filters out the item to be deleted. ID will need to be the ID of our action.
[02:16] Next let's tackle toggling. In our handle complete toggle, we'll dispatch an action with type toggle todo along with its ID. As we did with delete, we'll copy the inside of the update todo's updater and delete the rest. Then come up and paste the contents in our reducer.
[02:35] Here, we'll return and change prev todos to be state, which maps over the items, modifying the item to be toggled. Again, we need to change the ID to the action's ID. If you scroll to the top, you'll notice that we aren't using the local storage hook anymore. We'll come back to that, but for now that should be OK.
[02:58] We should be able to kick up our dev server again and give it a try. No todos come up because it's not communicating with local storage anymore, but it is working other than that. We can add, check off, and delete, but it'd be nice to have that local storage piece still working, so let's work on that.
[03:15] For this piece we'll create a custom hook called use todos, which is a valid hook name because it starts with use. Anyway, we'll grab all the contents from the use ref all the way down to the end of the use reducer and move it into our custom use todos hook.
[03:32] At the bottom, we'll return todos and dispatch an array. Then we'll come down and destructure todos and dispatch from our custom use todos hook. Things should still work just the same. We didn't really change anything but move them around. Let's make sure.
[03:50] Sure enough. It works as it did before. Now let's beef it up a little and make it support local storage. We'll change the name to use todos with local storage and give it a default value and support an initial value that gets its data from local storage.
[04:08] I'll create a value from storage variable and parse the JSON from local storage with a hard coded todos key. Also, if there's no item, we'll stringify the default value. In addition to reading from storage, we'll reduce over the array and find the maximum ID value and assign that to the todo ID ref. Finally, we need to return the todos array from local storage.
[04:35] Now we can use the initial value function as the default to use reducer. However, unlike use state it doesn't support passing a function as an option. Instead, we could leverage the use memo hook. The second parameter indicates when the memorized version should change. In our case, we want it to always be the same, so passing an empty array conveys that message.
[05:02] In order to use memo, we'll need to import it from React. We'll need to use an effect in order to save our todos back to local storage. Here we'll pass todos to the input array so the effect will only run when the todos change. Here we'll set the local storage key to the todos stringified.
[05:26] Then we'll use our custom hook below, passing an empty array as our default. Now we can test our app. Sure enough, the t dos automatically show up and it seems to work as expected.