Define Business Logic in a React App With Actions and Reducers
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
Lesson Transcript
[00:00 - 00:35] Define the business logic. In this lesson we'll define the actions and reducers necessary to create new cards and columns. We will provide the redu cers dispatch method through the react context and we'll use it on our add new item component. We will use immerges to simplify updating the state. Immerges will allow us to mutate the state instead of creating the new instance. Every time that we want to update the data. Install use immer. Yarn add use immer. I'm going to be using version 051 at 051. This library is written in TypeScript so we don't need to install an additional types package.
[00:36 - 00:50] Create actions. We'll begin by adding two actions. Add task and add list. To do this we'll have to define the action type alias. Create a new file as our C state actions TS and define a new type there.
[00:51 - 03:06] Export type action equals type add list payload string or type add task payload with text string and list ID also string. Here we've defined a type alias called action and then we passed two different types separated by vertical lines to it. This means that the action type now can resolve to one of the two forms that we've passed. So it works like the logical or each action here has an associated payload field. The add list contains the list title inside of the payload of type string and the add task contains the task text and the list ID to reference the list where the task belongs to. What we did here is we defined a union type, a union of two other types. Alternatively we could also define the types for this union using the interface syntax. So we would have an interface add list action and an interface add task action which we would combine into a union type called action. It will also work I just prefer regular types. This technique here is called a discriminated union and it will allow us to know what type of payload does the action have depending on the type field. So for example for type at least we will know that payload is a string and for the type add task the payload is going to be an object with field text and the field list ID. Each action has a type property. This property is our discriminant. It means that TypeScript can look at this property and tell what the other fields of this type will be. So in our case what will be the type of the payload here is how it would work. So if the action type is at least then if we call return type of action payload we will receive string. Otherwise if the type is add task we will receive this object type with text string and list ID also string. This will be useful when we'll define our reducers. Alright we have the action type now let's define the action creators. Still inside the state actions.ts defined and expert two functions.
[03:07 - 05:11] First one add task should receive text string and list ID also a string. This is an action creator so it returns an action and we can use round brackets to just return an object from this function. It's going to have type add task and payload of type object with fields text and list ID. The second action creator is as you can guess for the add list action expert const add list equals a function with text string as an argument it also returns an action and here the type will be add list and payload is going to be text. Define the upstate reducer create a new file as her c state up state reducer.ts it will contain our reducer function import the action type from the action module import action from actions. Move the upstate type definitions from the upstate context to this upstate reducer file. So we go into upstate context and then we take the type definitions starting from task and up until the upstate remove it from here and add to the upstate reducer. Don't forget to export them. Expert task expert list and expert upstate go back import upstate from the upstate reducer import the list type and that task. Go back to the upstate reducer file below the types define an expert the upstate reducer itself expert const upstate reducer equals a function that receives a draft of type up state and an action of type action. The redu cer should return upstate or void add a switch statement switch action type and for now we'll keep it empty.
[05:12 - 11:36] Adding lists each newly created list will have a unique ID that will use to find it in the state we'll use an ID to generate them install this library yarn add nano ID I'm going to be using version 3 122 after it's installed go to upstate reducer and import nano ID from nano ID. Now go to the upstate reducer function and add a case block case add list here we push a new list to the draft draft lists push ID nano ID text action payload tasks empty array and then we call break here we can call push directly and mutate the lists array because we are using emir otherwise we would have to create a new array add all the items from the old one plus the new one that we're creating adding tasks adding tasks is a bit more complex because they need to be added to the specific lists tasks array we'll need a helper function to find the items by their indices create a new file inside of the SRC Utils folder call it array utils.ts we're going to define a function that will accept any object that has a field ID of type string so we'll define it as a generic function first define a new type item type item equals a new type with field ID of type string in our function we'll use a type variable that will extend the type item define the function export const find item index by ID here we specify the type constraint that means that we constrain our generic property to have the fields that are defined on the item type in this case the ID field so t item that's our type variable extends item and here it means that it must have the same fields as the item type now let's define the arguments items should have t item array type and ID should be a string inside of this function we will return items find index and here we pass a function that receives an item of type t item and compares the item ID with the ID from the arguments let's verify that the type constraint will work we'll define the items without ID it's going to be an array with one element here it's only going to have text test and now we'll call our function find item index by ID items without ID and then we pass test ID all right we have an error let's see what's wrong and it says that the argument of type text string is not assign able to parameter of type item the property ID is missing and that's exactly what we want we want to constrain the type of items that we can pass to this function only to the types that have the ID property all right let's remove that return to as our c state upstate reducer import our helper function import find item index by ID from utils array utils now define the add task handler below the add list add a new case statement case add task here we get the text and list ID from the action payload const text list ID equals action payload we get the target list index using the helper function const target list index equals find item index by ID we pass in the draft lists and the list ID now we can add a new task to the target list draft lists we get the one by the target list index we get the task array and we push a new item there it will have ID generated by an nid and the text field from the action payload add a break statement provide dispatch through the context open as our c state upstate context tss and add the impers we'll want the dispatch type from react upstate reducer from the upstate reducer module the action type and they use im re reducer hook from use imre add dispatch function to the upstate context props definition dispatch should have type dispatch and we pass the action type to it update the upstate provider we get the state and dispatch using the use imre reducer hook const state and dispatch equals use im re reducer here we pass the upstate reducer and the up data now we used to get the lists directly from the hard coded update now we get them from the state we also should update the value that we pass through the provider and we add the dispatch there dispatching actions go to as our c up to tss and import the add list action creator from the as our c state actions tss import add list from state actions update the up component now we get the dispatch method from the use upstate dispatch and then update the on add method on the add new item component here we pass a function that receives text and we call dispatch with the add list action and we pass the text to it now go to column tss import add task import add task from state actions get dispatch from the use upstate and update the on add prop we pass in a function with text we call dispatch with add task and we pass text and id here now let's launch the app and see if it works yarn start let's recreating a new list test list and then a new card inside of it test task Everything works. Congratulations.
This lesson preview is part of the Fullstack React with TypeScript Masterclass course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Fullstack React with TypeScript Masterclass, plus 70+ \newline books, guides and courses with the \newline Pro subscription.
![Thumbnail for the \newline course Fullstack React with TypeScript Masterclass](/_next/image?url=https%3A%2F%2Fs3.amazonaws.com%2Fassets.fullstack.io%2Fn%2F20220425171607321_CoverImage_FSReactWTypeScript_Masterclas.png&w=3840&q=75)