Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds
Arrow up icon
GO TO TOP
React 18 Design Patterns and Best Practices

You're reading from   React 18 Design Patterns and Best Practices Design, build, and deploy production-ready web applications with React by leveraging industry-best practices

Arrow left icon
Product type Paperback
Published in Jul 2023
Publisher Packt
ISBN-13 9781803233109
Length 524 pages
Edition 4th Edition
Languages
Tools
Arrow right icon
Toc

Table of Contents (20) Chapters Close

Preface 1. Taking Your First Steps with React 2. Introducing TypeScript FREE CHAPTER 3. Cleaning Up Your Code 4. Exploring Popular Composition Patterns 5. Writing Code for the Browser 6. Making Your Components Look Beautiful 7. Anti-Patterns to Be Avoided 8. React Hooks 9. React Router 10. React 18 New Features 11. Managing Data 12. Server-Side Rendering 13. Understanding GraphQL with a Real Project 14. MonoRepo Architecture 15. Improving the Performance of Your Applications 16. Testing and Debugging 17. Deploying to Production 18. Other Books You May Enjoy
19. Index

Understanding the useReducer Hook

You probably have some experience using Redux (react-redux) with class components, and if that is the case, then you will understand how useReducer works. The concepts are basically the same: actions, reducers, dispatch, store, and state. Even if, in general, it seems very similar to react-redux, they have some differences. The main difference is that react-redux provides middleware and wrappers such as thunk, sagas, and many more besides, while useReducer just gives you a dispatch method that you can use to dispatch plain objects as actions. Also, useReducer does not have a store by default; instead, you can create one using useContext, but this is just reinventing the wheel.

Let’s create a basic application to understand how useReducer works. You can start by creating a new React app:

npx create-vite reducer --template react-ts

Then, as always, you can delete all files in your src folder except App.tsx and index.tsx to start a brand-new application.

We will create a basic Notes application where we can list, delete, create, or update our notes using useReducer. The first thing you need to do is import the Notes component, which we will create later, into your App component:

import Notes from './Notes'
function App() {
  return (
    <Notes />
  )
}
export default App

Now, in our Notes component, you first need to import useReducer and useState:

import { useReducer, useState, ChangeEvent } from 'react'

Then we need to define some TypeScript types that we need to use for our Note object, the Redux action, and the action types:

type Note = {
  id: number
  note: string
}
type Action = {
  type: string
  payload?: any
}
type ActionTypes = {
  ADD: 'ADD'
  UPDATE: 'UPDATE'
  DELETE: 'DELETE'
}
const actionType: ActionTypes = {
  ADD: 'ADD',
  DELETE: 'DELETE',
  UPDATE: 'UPDATE'
}

After this, we need to create initialNotes (also known as initialState) with some dummy notes:

const initialNotes: Note[] = [
  {
    id: 1,
    note: 'Note 1'
  },
  {
    id: 2,
    note: 'Note 2'
  }
]

If you remember how the reducers work, then this will seem very similar to how we handle the reducer using a switch statement, so as to perform basic operations such as ADD, DELETE, and UPDATE:

const reducer = (state: Note[], action: Action) => {
  switch (action.type) {
    case actionType.ADD:
        return [...state, action.payload]
    case actionType.DELETE:
        return state.filter(note => note.id !== action.payload)
   
    case actionType.UPDATE:
        const updatedNote = action.payload
        return state.map((n: Note) => n.id === updatedNote.id ? updatedNote : n)
   
    default:
          return state
  }
}

Finally, the component is very straightforward. Basically, you get the notes and the dispatch method from the useReducer Hook (similar to useState), and you need to pass the reducer function and initialNotes (initialState):

const Notes = () => {
  const [notes, dispatch] = useReducer(reducer, initialNotes)
  const [note, setNote] = useState<string>('')
  ...
}

Then, we have a handleSubmit function to create a new note when we write something in the input. Then, we press Enter:

const handleSubmit = (e: ChangeEvent<HTMLInputElement>) => {
  e.preventDefault()
  const newNote = {
    id: Date.now(),
    note
  }
  dispatch({ type: actionType.ADD, payload: newNote })
}

Finally, we render our Notes list with map, and we also create two buttons, one for delete and one for update, and then the input should be wrapped into a <form> tag:

return (
  <div>
    <h2>Notes</h2>
    <ul>
        {notes.map((n: Note) => (
            <li key={n.id}>
               {n.note} {' '}
          
            <button onClick={() => dispatch({ type: actionType.DELETE, payload: n.id })}>
           X
         </button>
         <button 
                 onClick={() => dispatch({ type: actionType.UPDATE, payload: {...n, note} })}
         >
            Update
         </button>
           </li>
        ))}
    </ul>
     
    <form onSubmit={handleSubmit}>
        <input 
            placeholder="New note"
            value={note}
            onChange={e => setNote(e.target.value)}
        />
    </form>
  </div>
)
export default Notes

If you run the application, you should see the following output:

Graphical user interface, text  Description automatically generated

Figure 8.22: React DevTools

As you can see in the React DevTools, the Reducer object contains the two notes that we have defined as our initial state.

Now, if you write something in the input and you press Enter, you should be able to create a new note:

Graphical user interface, text  Description automatically generated

Figure 8.23: Creating a new note

Then, if you want to delete a note, you just need to click on the X button. Let’s remove Note 2:

Graphical user interface, text, application  Description automatically generated

Figure 8.24: Deleting a note

Finally, you can write anything you want in the input, and if you click on the Update button, you will change the note value:

Graphical user interface, text, application  Description automatically generated

Figure 8.25: Updating a note

Nice, huh? As you can see, the useReducer Hook is pretty much the same as redux in terms of the dispatch method, actions, and reducers, but the main difference is that this is limited just to the context of your component and its child, so if you need a global store to be accessible from your entire application, then you should use react-redux instead.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image