diff --git a/Writerside/images/zustand.png b/Writerside/images/zustand.png new file mode 100644 index 0000000..fc3015e Binary files /dev/null and b/Writerside/images/zustand.png differ diff --git a/Writerside/pdfSourceR.html b/Writerside/pdfSourceR.html index 9400cf7..eab8b97 100644 --- a/Writerside/pdfSourceR.html +++ b/Writerside/pdfSourceR.html @@ -273,7 +273,7 @@ .topic { page-break-before: always; } -

Table of contents

3
9
10
14
21
23
26
28
30
34
36
40
44
46
52
60
66
73
76
84
91
95
98
106
113
121
131
139
147
154
160
168
175
189
196
198
206
214
222
229
235
244
253
269
272
276

Course Outline: React with TypeScript Documentation

Here is the course outline for a React.js and TypeScript documentation written in Markdown (.md) format:

1. Introduction to React

  • Overview of React

  • Benefits of using React

  • Introduction to TypeScript in React

  • Setting up the Development Environment

    • Installing Node.js and npm

    • Setting up a React project with Create React App (CRA) or Vite

    • Configuring TypeScript in a React Project

2. Core Concepts

  • JSX & Props

    • What is JSX?

    • Embedding JavaScript in JSX

    • Passing Props to Components

    • Using Props for Reusable Components

  • Components in React

    • Functional vs. Class Components

    • Creating Components with TypeScript

    • Default and Named Exports

    • Rendering Components

3. State Management

  • useState Hook

    • Introduction to useState

    • Managing State in Functional Components

    • Example: Managing Form Inputs

  • useReducer Hook

    • Introduction to useReducer

    • Complex State Management

    • Example: Building a Counter with useReducer

4. Advanced React Concepts

  • Handling Events

    • Event Handling in React

    • Passing Parameters to Event Handlers

    • Common Event Handlers (e.g., onClick, onChange, onSubmit)

  • Conditional Rendering

    • Using Ternary Operators for Conditional Rendering

    • Best Practices for Conditional Rendering

  • Rendering Lists

    • Mapping Data to Components

    • Understanding Keys in React

    • Handling Dynamic Lists

5. Hooks in React

  • Introduction to Hooks

    • What are Hooks?

    • Rules of Hooks

  • useEffect Hook

    • Synchronizing with External Systems

    • Fetching Data with useEffect

    • Dependency Arrays

  • useRef Hook

    • Managing DOM References

    • Persisting Values Across Renders

    • Example: Counting Renders

6. Custom Hooks

  • Introduction to Custom Hooks

    • When and Why to Create Custom Hooks

    • Naming Conventions

  • Creating a Custom Hook

    • Example: useLocalStorage Hook

    • Example: useFetch Hook

7. Performance Optimization

  • Memoization in React

    • What is Memoization?

    • useMemo Hook

    • useCallback Hook

    • React.memo for Component Optimization

  • Avoiding Prop Drilling

    • Issues with Prop Drilling

    • Solutions: Context API, Redux, Component Composition

8. Advanced State Management with Redux Toolkit

  • Introduction to Redux

    • Centralized State Management

    • Benefits of Redux

  • Setting Up Redux Toolkit

    • Installing Redux Toolkit

    • Creating a Redux Store

    • Using createSlice and configureStore

  • Handling Asynchronous Actions

    • Using createAsyncThunk for Async Operations

    • Integrating with Redux Slices

  • RTK Query for Data Fetching

    • Introduction to RTK Query

    • Queries and Mutations in RTK Query

    • Handling Caching and Error States

9. Routing with React Router

  • Introduction to React Router

    • Setting Up React Router

    • Basic Routing Concepts

    • Nested Routes and URL Parameters

  • React Router DOM V6

    • New Features in React Router V6

    • Route Protection with Private Routes

    • Handling 404 Pages

10. Form Management with React Hook Form & Yup

  • Introduction to React Hook Form

    • Why Use React Hook Form?

    • Setting Up React Hook Form

    • Handling Form Validation with Yup

  • Advanced Form Handling

    • Dynamic Forms

    • Handling Form Submissions

11. Deploying React Applications

  • Preparing for Deployment

    • Optimizing Your React App for Production

    • Managing Environment Variables

  • Deployment Strategies

    • Deploying to Vercel, Netlify, and Azure

    • Continuous Integration/Continuous Deployment (CI/CD) with GitHub Actions

12. Best Practices and Common Pitfalls

  • Code Structuring

    • Organizing Files and Folders

    • Naming Conventions and Best Practices

  • Avoiding Common Mistakes

    • Managing State and Props Effectively

    • Ensuring Performance Optimization

    • Handling Errors Gracefully

13. Conclusion

  • Recap of Key Concepts

  • Further Learning Resources

  • Next Steps in React and TypeScript Mastery

1. Intro to React

React TypeScript
React TypeScript

1.1 Overview of React

React Quickstart

Before you start, you should have a basic understanding of:

  1. [x] What is HTML

  2. [x] What is CSS

  3. [x] What is DOM

  4. [x] What is ES6

  5. [x] What is Node.js

  6. [x] What is npm

What is React?

react
react
  • React, sometimes referred to as a frontend JavaScript framework, is a JavaScript library created by Facebook.

  • React is a tool for building UI components.

  • React has solidified its position as the go-to JavaScript front-end framework in the current tech landscape. It’s fascinating to see how it’s seamlessly woven itself into the development practices of well-established corporations and budding startups alike.

What is React used for?

  1. Web development :

    • This is where React got its start and where you’ll find it used most often. React is component-based. An example of a component could be a form or even just a form field or button on a website. In React, you build up complete applications using components like these by nesting them.

    • Components in React can manage their own state and communicate that state to child components. By “state,” we mean the data that populates the web application.

  2. Mobile app development :

    • React Native is a JavaScript framework that uses React. With React Native, developers can apply web-based React principles to creating mobile apps for Android and iOS. Here, React is used to connect the mobile user interface of the application to the phone’s operating system.

  3. Desktop app development :

    • Developers can also use React with Electron, another JavaScript library, to create cross-platform desktop apps. Some apps you may know about that are built with Electron include Visual Studio Code, Slack, Skype, Discord, WhatsApp, and WordPress Desktop.

React.JS History

React.js, a popular JavaScript library for building user interfaces, particularly for single-page applications, has a fascinating history that reflects its evolution and growing adoption in the web development community. Here's a brief history of React.js in bullet points:

  • 2011: React.js created by Facebook's Jordan Walke for internal use.

  • 2013: Open-sourced at JSConf US; introduced virtual DOM.

  • 2014: Facebook introduced Flux, influencing state management in React.

  • 2015: React Native launched, expanding React to mobile apps.

  • 2015: React v0.14 split core into react and react-dom.

  • 2016: React v15 brought performance improvements and prop-types.

  • 2017: React Fiber (v16) restructured core for better responsiveness.

  • 2018: Hooks introduced in v16.8, transforming component design.

  • 2019: Experimental Suspense and Concurrent Mode introduced.

  • 2020: React v17 focused on easier upgrades.

  • 2022: React 18 brought full Concurrent Mode and enhanced UI responsiveness.

  • 2023: React 19 is the latest major release of the React JavaScript library, bringing a range of new features and improvements aimed at enhancing both developer experience and application performance. Some of the key updates include:

    1. React Compiler: A significant new feature, the React Compiler automates many performance optimizations, like memoization, which were previously handled manually using hooks like useMemo and useCallback. This simplifies the code and makes React apps faster and more efficient.

    2. Actions and Form Handling: React 19 introduces a new way to handle form submissions and state changes using "Actions." This feature simplifies managing asynchronous operations, making it easier to handle loading states, errors, and successful form submissions.

    3. New Hooks: Several new hooks have been introduced, such as useOptimistic, which allows for optimistic UI updates (i.e., updating the UI immediately while awaiting server confirmation), and use, which simplifies asynchronous operations within components. Additionally, the useFormStatus and useActionState hooks make managing form state more intuitive.

    4. Server Components: React 19 enhances server-side rendering by allowing server components, similar to features in frameworks like Next.js. This can lead to faster page loads and improved SEO.

    5. Improved Metadata Management: Managing document metadata like titles and meta tags is now easier and more integrated into React components, eliminating the need for third-party libraries like react-helmet.

    6. Background Asset Loading: React 19 introduces background loading of assets (like images and scripts), which helps improve page load times and overall user experience by preloading resources in the background as users navigate through the app.

1.2 Thinking in React

"Thinking in React" is a concept that describes the process of designing and building user interfaces with React.js. It emphasizes breaking down the UI into components, managing data flow, and structuring the application in a way that aligns with React's component-based architecture. Here’s a concise breakdown:

1. Break Down the UI into Components

  1. Start with a Mockup: Look at your UI and identify the different parts that can be broken down into components.

  • Imagine that you already have a JSON API and a mockup from a designer.

    • The JSON API returns some data that looks like this:

      [
      +        

      Table of contents

      3
      9
      10
      14
      21
      23
      26
      28
      30
      34
      36
      40
      44
      46
      52
      60
      66
      73
      76
      84
      91
      95
      98
      106
      113
      121
      131
      139
      147
      154
      160
      168
      175
      189
      196
      198
      206
      214
      222
      229
      235
      244
      253
      269
      272
      276
      290
      292
      296
      305

      Course Outline: React with TypeScript Documentation

      Here is the course outline for a React.js and TypeScript documentation written in Markdown (.md) format:

      1. Introduction to React

      • Overview of React

      • Benefits of using React

      • Introduction to TypeScript in React

      • Setting up the Development Environment

        • Installing Node.js and npm

        • Setting up a React project with Create React App (CRA) or Vite

        • Configuring TypeScript in a React Project

      2. Core Concepts

      • JSX & Props

        • What is JSX?

        • Embedding JavaScript in JSX

        • Passing Props to Components

        • Using Props for Reusable Components

      • Components in React

        • Functional vs. Class Components

        • Creating Components with TypeScript

        • Default and Named Exports

        • Rendering Components

      3. State Management

      • useState Hook

        • Introduction to useState

        • Managing State in Functional Components

        • Example: Managing Form Inputs

      • useReducer Hook

        • Introduction to useReducer

        • Complex State Management

        • Example: Building a Counter with useReducer

      4. Advanced React Concepts

      • Handling Events

        • Event Handling in React

        • Passing Parameters to Event Handlers

        • Common Event Handlers (e.g., onClick, onChange, onSubmit)

      • Conditional Rendering

        • Using Ternary Operators for Conditional Rendering

        • Best Practices for Conditional Rendering

      • Rendering Lists

        • Mapping Data to Components

        • Understanding Keys in React

        • Handling Dynamic Lists

      5. Hooks in React

      • Introduction to Hooks

        • What are Hooks?

        • Rules of Hooks

      • useEffect Hook

        • Synchronizing with External Systems

        • Fetching Data with useEffect

        • Dependency Arrays

      • useRef Hook

        • Managing DOM References

        • Persisting Values Across Renders

        • Example: Counting Renders

      6. Custom Hooks

      • Introduction to Custom Hooks

        • When and Why to Create Custom Hooks

        • Naming Conventions

      • Creating a Custom Hook

        • Example: useLocalStorage Hook

        • Example: useFetch Hook

      7. Performance Optimization

      • Memoization in React

        • What is Memoization?

        • useMemo Hook

        • useCallback Hook

        • React.memo for Component Optimization

      • Avoiding Prop Drilling

        • Issues with Prop Drilling

        • Solutions: Context API, Redux, Component Composition

      8. Advanced State Management with Redux Toolkit

      • Introduction to Redux

        • Centralized State Management

        • Benefits of Redux

      • Setting Up Redux Toolkit

        • Installing Redux Toolkit

        • Creating a Redux Store

        • Using createSlice and configureStore

      • Handling Asynchronous Actions

        • Using createAsyncThunk for Async Operations

        • Integrating with Redux Slices

      • RTK Query for Data Fetching

        • Introduction to RTK Query

        • Queries and Mutations in RTK Query

        • Handling Caching and Error States

      9. Routing with React Router

      • Introduction to React Router

        • Setting Up React Router

        • Basic Routing Concepts

        • Nested Routes and URL Parameters

      • React Router DOM V6

        • New Features in React Router V6

        • Route Protection with Private Routes

        • Handling 404 Pages

      10. Form Management with React Hook Form & Yup

      • Introduction to React Hook Form

        • Why Use React Hook Form?

        • Setting Up React Hook Form

        • Handling Form Validation with Yup

      • Advanced Form Handling

        • Dynamic Forms

        • Handling Form Submissions

      11. Deploying React Applications

      • Preparing for Deployment

        • Optimizing Your React App for Production

        • Managing Environment Variables

      • Deployment Strategies

        • Deploying to Vercel, Netlify, and Azure

        • Continuous Integration/Continuous Deployment (CI/CD) with GitHub Actions

      12. Best Practices and Common Pitfalls

      • Code Structuring

        • Organizing Files and Folders

        • Naming Conventions and Best Practices

      • Avoiding Common Mistakes

        • Managing State and Props Effectively

        • Ensuring Performance Optimization

        • Handling Errors Gracefully

      13. Conclusion

      • Recap of Key Concepts

      • Further Learning Resources

      • Next Steps in React and TypeScript Mastery

      1. Intro to React

      React TypeScript
      React TypeScript

      1.1 Overview of React

      React Quickstart

      Before you start, you should have a basic understanding of:

      1. [x] What is HTML

      2. [x] What is CSS

      3. [x] What is DOM

      4. [x] What is ES6

      5. [x] What is Node.js

      6. [x] What is npm

      What is React?

      react
      react
      • React, sometimes referred to as a frontend JavaScript framework, is a JavaScript library created by Facebook.

      • React is a tool for building UI components.

      • React has solidified its position as the go-to JavaScript front-end framework in the current tech landscape. It’s fascinating to see how it’s seamlessly woven itself into the development practices of well-established corporations and budding startups alike.

      What is React used for?

      1. Web development :

        • This is where React got its start and where you’ll find it used most often. React is component-based. An example of a component could be a form or even just a form field or button on a website. In React, you build up complete applications using components like these by nesting them.

        • Components in React can manage their own state and communicate that state to child components. By “state,” we mean the data that populates the web application.

      2. Mobile app development :

        • React Native is a JavaScript framework that uses React. With React Native, developers can apply web-based React principles to creating mobile apps for Android and iOS. Here, React is used to connect the mobile user interface of the application to the phone’s operating system.

      3. Desktop app development :

        • Developers can also use React with Electron, another JavaScript library, to create cross-platform desktop apps. Some apps you may know about that are built with Electron include Visual Studio Code, Slack, Skype, Discord, WhatsApp, and WordPress Desktop.

      React.JS History

      React.js, a popular JavaScript library for building user interfaces, particularly for single-page applications, has a fascinating history that reflects its evolution and growing adoption in the web development community. Here's a brief history of React.js in bullet points:

      • 2011: React.js created by Facebook's Jordan Walke for internal use.

      • 2013: Open-sourced at JSConf US; introduced virtual DOM.

      • 2014: Facebook introduced Flux, influencing state management in React.

      • 2015: React Native launched, expanding React to mobile apps.

      • 2015: React v0.14 split core into react and react-dom.

      • 2016: React v15 brought performance improvements and prop-types.

      • 2017: React Fiber (v16) restructured core for better responsiveness.

      • 2018: Hooks introduced in v16.8, transforming component design.

      • 2019: Experimental Suspense and Concurrent Mode introduced.

      • 2020: React v17 focused on easier upgrades.

      • 2022: React 18 brought full Concurrent Mode and enhanced UI responsiveness.

      • 2023: React 19 is the latest major release of the React JavaScript library, bringing a range of new features and improvements aimed at enhancing both developer experience and application performance. Some of the key updates include:

        1. React Compiler: A significant new feature, the React Compiler automates many performance optimizations, like memoization, which were previously handled manually using hooks like useMemo and useCallback. This simplifies the code and makes React apps faster and more efficient.

        2. Actions and Form Handling: React 19 introduces a new way to handle form submissions and state changes using "Actions." This feature simplifies managing asynchronous operations, making it easier to handle loading states, errors, and successful form submissions.

        3. New Hooks: Several new hooks have been introduced, such as useOptimistic, which allows for optimistic UI updates (i.e., updating the UI immediately while awaiting server confirmation), and use, which simplifies asynchronous operations within components. Additionally, the useFormStatus and useActionState hooks make managing form state more intuitive.

        4. Server Components: React 19 enhances server-side rendering by allowing server components, similar to features in frameworks like Next.js. This can lead to faster page loads and improved SEO.

        5. Improved Metadata Management: Managing document metadata like titles and meta tags is now easier and more integrated into React components, eliminating the need for third-party libraries like react-helmet.

        6. Background Asset Loading: React 19 introduces background loading of assets (like images and scripts), which helps improve page load times and overall user experience by preloading resources in the background as users navigate through the app.

      1.2 Thinking in React

      "Thinking in React" is a concept that describes the process of designing and building user interfaces with React.js. It emphasizes breaking down the UI into components, managing data flow, and structuring the application in a way that aligns with React's component-based architecture. Here’s a concise breakdown:

      1. Break Down the UI into Components

      1. Start with a Mockup: Look at your UI and identify the different parts that can be broken down into components.

      • Imagine that you already have a JSON API and a mockup from a designer.

        • The JSON API returns some data that looks like this:

          [
             { category: "Fruits", price: "$1", stocked: true, name: "Apple" },
             { category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit" },
             { category: "Fruits", price: "$2", stocked: false, name: "Passionfruit" },
          @@ -3501,7 +3501,7 @@
             );
           }
           
          -export default App;

      Step 6: Running the Application

      Start your application with:

      npm run dev or pnpm run dev

      Handling Asynchronous Actions

      Redux Toolkit (Thunk)

      In many applications, we need to perform asynchronous operations such as fetching data from an API, posting data to a server, or dealing with side effects like authentication and file uploads. Redux Toolkit provides a simple and powerful way to handle these scenarios through thunks, which are async functions that dispatch actions.

      Using createAsyncThunk from Redux Toolkit, you can handle async operations efficiently while managing loading, success, and error states.

      Project Overview: Fetching Data from an API

      Let's build a small project that focuses on handling asynchronous actions using Redux Toolkit. This project will demonstrate:

      • Fetching a list of posts from a public API (jsonplaceholder.typicode.com).

      • Managing loading, success, and error states for the async action.

      • Displaying the fetched data in a React component.

      • Best practices for structuring and managing async actions.

      Key Concepts:

      1. Thunk: A function that delays its execution or dispatches other functions (async functions).

      2. createAsyncThunk: A Redux Toolkit method to handle async logic and automatically dispatch actions based on the promise state (pending, fulfilled, rejected).

      1. Project Setup

      1. Initialize the Project:

        • Use Vite with React and TypeScript.

        npm create vite@latest async-redux-app -- --template react-ts
        +export default App;

      Step 6: Running the Application

      Start your application with:

      npm run dev or pnpm run dev

      Redux Thunk - Handling Asynchronous Actions

      Redux Toolkit (Thunk)

      In many applications, we need to perform asynchronous operations such as fetching data from an API, posting data to a server, or dealing with side effects like authentication and file uploads. Redux Toolkit provides a simple and powerful way to handle these scenarios through thunks, which are async functions that dispatch actions.

      Using createAsyncThunk from Redux Toolkit, you can handle async operations efficiently while managing loading, success, and error states.

      Project Overview: Fetching Data from an API

      Let's build a small project that focuses on handling asynchronous actions using Redux Toolkit. This project will demonstrate:

      • Fetching a list of posts from a public API (jsonplaceholder.typicode.com).

      • Managing loading, success, and error states for the async action.

      • Displaying the fetched data in a React component.

      • Best practices for structuring and managing async actions.

      Key Concepts:

      1. Thunk: A function that delays its execution or dispatches other functions (async functions).

      2. createAsyncThunk: A Redux Toolkit method to handle async logic and automatically dispatch actions based on the promise state (pending, fulfilled, rejected).

      1. Project Setup

      1. Initialize the Project:

        • Use Vite with React and TypeScript.

        npm create vite@latest async-redux-app -- --template react-ts
         cd async-redux-app
         npm install
      2. Install Redux Toolkit and React Redux:

        npm install @reduxjs/toolkit react-redux axios
      3. Folder Structure:

        We’ll organize our code to follow best practices with a clear separation of concerns.

        src/
         ├── components/
        @@ -3660,7 +3660,7 @@
           );
         };
         
        -export default App;

      6. Running the Application

      You can now run the application using:

      npm run dev

      Open the browser at http://localhost:5173 to see the fetched posts.

      Explanation of Best Practices

      1. Using createAsyncThunk:

        • We used createAsyncThunk to handle the lifecycle of async operations. This is a best practice in Redux Toolkit as it helps to reduce boilerplate and automatically handles the states of a promise.

      2. Service Layer:

        • We created a postService.ts file that encapsulates the API logic. This decouples business logic from the Redux slice, making the code cleaner and easier to maintain.

      3. Separation of Concerns:

        • We separated concerns by using redux/ for state management, services/ for API

      End2End: React ReduxThunk ExpressMongoDb Posts CRUD APP

      This is a fully functional CRUD app using TypeScript for both the Express backend and the React frontend. The backend will use MongoDB for storing posts, and the frontend will use **Redux Toolkit(Thunk) ** to interact with the API for managing CRUD operations.

      Step 1: Setting Up the Backend with Express, MongoDB, and TypeScript

      1.1 Backend Folder Structure

      Here's the folder structure for the TypeScript-based Express backend:

      backend/
      +export default App;

6. Running the Application

You can now run the application using:

npm run dev

Open the browser at http://localhost:5173 to see the fetched posts.

Explanation of Best Practices

  1. Using createAsyncThunk:

    • We used createAsyncThunk to handle the lifecycle of async operations. This is a best practice in Redux Toolkit as it helps to reduce boilerplate and automatically handles the states of a promise.

  2. Service Layer:

    • We created a postService.ts file that encapsulates the API logic. This decouples business logic from the Redux slice, making the code cleaner and easier to maintain.

  3. Separation of Concerns:

    • We separated concerns by using redux/ for state management, services/ for API

End2End: React ReduxThunk ExpressMongoDb Posts CRUD APP

This is a fully functional CRUD app using TypeScript for both the Express backend and the React frontend. The backend will use MongoDB for storing posts, and the frontend will use **Redux Toolkit(Thunk) ** to interact with the API for managing CRUD operations.

Step 1: Setting Up the Backend with Express, MongoDB, and TypeScript

1.1 Backend Folder Structure

Here's the folder structure for the TypeScript-based Express backend:

backend/
 ├── src/
 │   ├── config/
 │   │   └── db.ts
@@ -3674,11 +3674,11 @@
 ├── .env
 ├── tsconfig.json
 ├── package.json
-└── package-lock.json

1.2 Backend Dependencies

First, set up your backend by initializing a Node.js project with TypeScript support:

mkdir backend
+└── package-lock.json

1.2 Backend Dependencies

First, set up your backend by initializing a Node.js project with TypeScript support:

mkdir backend
 cd backend
 npm init -y
 npm install express mongoose dotenv cors
-npm install --save-dev Typescript tsx  @types/node @types/cors

1.3 TypeScript Configuration

Create a tsconfig.json file in the backend root:

{
+npm install --save-dev Typescript tsx  @types/node @types/cors

1.3 TypeScript Configuration

Create a tsconfig.json file in the backend root:

{
   "compilerOptions": {
     "target": "ES2020",
     "module": "commonjs",
@@ -3688,7 +3688,7 @@
     "outDir": "./dist"
   },
   "include": ["src/**/*"]
-}

1.4 MongoDB Connection

Create a new folder called config/ and a file db.ts for connecting to MongoDB:

  • File: src/config/db.ts

import mongoose from 'mongoose';
+}

1.4 MongoDB Connection

Create a new folder called config/ and a file db.ts for connecting to MongoDB:

  • File: src/config/db.ts

import mongoose from 'mongoose';
 import dotenv from 'dotenv';
 
 dotenv.config();
@@ -3703,7 +3703,7 @@
   }
 };
 
-export default connectDB;
  • File: .env

Add your MongoDB connection string:

MONGO_URI=mongodb://localhost:27017/posts

1.5 Create Post Model

Define the Post model that corresponds to the structure of posts in the MongoDB database.

  • File: src/models/postModel.ts

import mongoose, { Schema, Document } from 'mongoose';
+export default connectDB;
  • File: .env

Add your MongoDB connection string:

MONGO_URI=mongodb://localhost:27017/posts

1.5 Create Post Model

Define the Post model that corresponds to the structure of posts in the MongoDB database.

  • File: src/models/postModel.ts

import mongoose, { Schema, Document } from 'mongoose';
 
 export interface IPost extends Document {
   title: string;
@@ -3717,7 +3717,7 @@
 
 const Post = mongoose.model<IPost>('Post', postSchema);
 
-export default Post;

1.6 Create Post Controller

The controller will handle the logic for each API endpoint.

  • File: src/controllers/postController.ts

import { Request, Response } from 'express';
+export default Post;

1.6 Create Post Controller

The controller will handle the logic for each API endpoint.

  • File: src/controllers/postController.ts

import { Request, Response } from 'express';
 import Post, { IPost } from '../models/postModel';
 
 // Fetch all posts
@@ -3776,7 +3776,7 @@
   } catch (error) {
     res.status(500).json({ message: 'Server error' });
   }
-};

1.7 Create Routes for Posts

We define the API routes for performing CRUD operations on posts.

  • File: src/routes/postRoutes.ts

import express from 'express';
+};

1.7 Create Routes for Posts

We define the API routes for performing CRUD operations on posts.

  • File: src/routes/postRoutes.ts

import express from 'express';
 import { getPosts, createPost, updatePost, deletePost } from '../controllers/postController';
 
 const router = express.Router();
@@ -3784,7 +3784,7 @@
 router.route('/').get(getPosts).post(createPost);
 router.route('/:id').put(updatePost).delete(deletePost);
 
-export default router;

1.8 Initialize Express Server

Now let's create the main server file and set up middleware.

  • File: src/server.ts

import express from 'express';
+export default router;

1.8 Initialize Express Server

Now let's create the main server file and set up middleware.

  • File: src/server.ts

import express from 'express';
 import cors from 'cors';
 import dotenv from 'dotenv';
 import connectDB from './config/db';
@@ -3804,7 +3804,7 @@
 const PORT = process.env.PORT || 5000;
 app.listen(PORT, () => {
   console.log(`Server running on port ${PORT}`);
-});

Run the backend server:

npm run dev

Step 2: Setting Up the Frontend (React + Redux Toolkit)

Now let's connect the backend API to a React + Redux Toolkit frontend.

2.1 Frontend Folder Structure

frontend/
+});

Run the backend server:

npm run dev

Step 2: Setting Up the Frontend (React + Redux Toolkit)

Now let's connect the backend API to a React + Redux Toolkit frontend.

2.1 Frontend Folder Structure

frontend/
 ├── src/
 │   ├── components/
 │   │   └── PostList.tsx
@@ -3821,11 +3821,11 @@
 │   └── main.tsx
 ├── tsconfig.json
 ├── package.json
-└── package-lock.json

2.2 Frontend Dependencies

  1. Initialize the frontend using Vite with TypeScript:

    mkdir frontend
    +└── package-lock.json

2.2 Frontend Dependencies

  1. Initialize the frontend using Vite with TypeScript:

    mkdir frontend
     cd frontend
     npm create vite@latest async-frontend -- --template react-ts
     cd async-frontend
    -npm install
  2. Install required packages:

    npm install @reduxjs/toolkit react-redux axios lucide-react

2.3 Redux Setup for Posts (CRUD Actions)

  1. Post Service:

    • Path: src/features/posts/services/postService.ts

    import axios from 'axios';
    +npm install
  2. Install required packages:

    npm install @reduxjs/toolkit react-redux axios lucide-react

2.3 Redux Setup for Posts (CRUD Actions)

  1. Post Service:

    • Path: src/features/posts/services/postService.ts

    import axios from 'axios';
     
     const API_URL = 'http://localhost:5000/api/posts';
     
    @@ -3847,7 +3847,7 @@
     export const deletePost = async (id: string) => {
       const response = await axios.delete(`${API_URL}/${id}`);
       return response.data;
    -};
  2. Redux Slice:

    • Path: src/features/posts/redux/postSlice.ts

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
+};
  • Redux Slice:

    • Path: src/features/posts/redux/postSlice.ts

  • import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
     import { fetchPosts, createPost, updatePost, deletePost } from '../services/postService';
     
     interface Post {
    @@ -3926,7 +3926,7 @@
       },
     });
     
    -export default postSlice.reducer;
    1. Store Configuration:

      • Path: src/store/store.ts

      import { configureStore } from '@reduxjs/toolkit';
      +export default postSlice.reducer;
      1. Store Configuration:

        • Path: src/store/store.ts

        import { configureStore } from '@reduxjs/toolkit';
         import postReducer from '../features/posts/redux/postSlice';
         
         const store = configureStore({
        @@ -3938,7 +3938,7 @@
         export type RootState = ReturnType<typeof store.getState>;
         export type AppDispatch = typeof store.dispatch;
         
        -export default store;

    2.4 React Components to Manage Posts

    1. Post List Component:

      • Path: src/components/PostList.tsx

      import React, { useEffect } from 'react';
      +export default store;

    2.4 React Components to Manage Posts

    1. Post List Component:

      • Path: src/components/PostList.tsx

      import React, { useEffect } from 'react';
       import { useDispatch, useSelector } from 'react-redux';
       import { fetchPostsThunk, deletePostThunk } from '../features/posts/redux/postSlice';
       import { RootState, AppDispatch } from '../store/store';
      @@ -3967,7 +3967,7 @@
         );
       };
       
      -export default PostList;
    2. Post Form Component:

      • path src/components/PostForm.tsx

    import React, { useState } from 'react';
    +export default PostList;
  • Post Form Component:

    • path src/components/PostForm.tsx

  • import React, { useState } from 'react';
     import { useDispatch } from 'react-redux';
     import { createPostThunk } from '../features/posts/redux/postSlice';
     import { AppDispatch } from '../store/store';
    @@ -4029,7 +4029,7 @@
       );
     };
     
    -export default PostForm;
    1. App Component:

      • Path: src/App.tsx

      import React from 'react';
      +export default PostForm;
      1. App Component:

        • Path: src/App.tsx

        import React from 'react';
         import PostList from './components/PostList';
         
         const App = () => {
        @@ -4041,9 +4041,9 @@
           );
         };
         
        -export default App;

    Step 3: Running the Full Stack Application

    1. Run the Backend:

    cd backend
    -npm run dev
    1. Run the Frontend:

    cd frontend
    -npm run dev

    Open your browser at http://localhost:5173/ to view the app. The app will fetch, display, create, update, and delete posts with full CRUD functionality using React + Redux Toolkit for the frontend and Express + MongoDB for the backend.

    This setup provides a clean, scalable way to manage async actions in Redux, ensuring best practices are followed throughout the stack.

    RTK Query for Data Fetching

    What is RTK Query?

    RTK Query is a powerful data-fetching and caching tool that is built directly into Redux Toolkit. It simplifies the process of data fetching in Redux applications by abstracting away many of the complexities that come with handling loading, caching, re-fetching, and synchronizing server state.

    Unlike traditional Redux approaches (using thunk), which require you to manually manage async operations, RTK Query does a lot of the heavy lifting for you.

    Benefits of Using RTK Query

    1. Automatic Caching: RTK Query automatically caches your API responses and avoids unnecessary network requests. When data is fetched, it stores that data and serves it on subsequent requests until it's invalidated.

    2. Simplicity: RTK Query abstracts much of the boilerplate code you'd typically write with redux-thunk. You no longer need to manage loading, error, or success states manually.

    3. Optimized Performance: It efficiently handles state updates and only re-fetches data when necessary (e.g., when cache expiration happens).

    4. Automatic Refetching: If a component relies on a particular piece of data, RTK Query can automatically re-fetch that data when necessary, such as when a dependent component is re-rendered or when the cache becomes stale.

    5. Code Reusability: You can define API endpoints once and then use them across multiple components without re-writing data fetching logic.

    RTK Query Main Architecture and Components

    1. createApi:

    • This is the core function in RTK Query. It defines your endpoints (API operations such as GET, POST, PUT, and DELETE) and how your application interacts with the backend.

    • Example: Defining API endpoints for fetching posts, creating a post, and deleting a post.

    1. Base Query:

    • baseQuery is a function that acts as the low-level fetcher for your endpoints. Typically, this will use fetch or axios to make HTTP requests. RTK Query allows you to customize baseQuery for all your API operations.

    • Example: You can configure a global axios instance or fetch to handle authentication tokens, error handling, and more.

    1. Endpoints:

    • Endpoints represent the actual API operations such as GET /posts, POST /users, etc. Each endpoint defines how data is fetched, updated, or deleted.

    1. Hooks:

    • RTK Query automatically generates React hooks for every endpoint. These hooks are used inside your React components to trigger data fetching, mutation (POST, PUT, DELETE), and cache handling.

    Best Way to Use RTK Query

    1. Define API Services in a Centralized Place: Create a central API slice where you define all endpoints and configuration.

    2. Leverage Auto-Generated Hooks: RTK Query automatically provides hooks (e.g., useGetPostsQuery, useAddPostMutation) which you can use in your components to interact with the API.

    3. Cache Management: Use RTK Query's built-in cache management to optimize performance. You can manually invalidate the cache when needed.

    4. Error Handling: RTK Query includes mechanisms for automatic error handling, but you can also customize and intercept errors globally in the baseQuery.

    RTK Query Vs Redux Thunk

    RTK-Query.png
    RTK-Query.png

    In redux we typically model network fetching and caching like this.

    • You have a component, which subscribes to data from the redux store via selectors

    • In a useEffect hook you check if the data doesn’t exist and then dispatch a thunk

    • Your thunk will use a tool like fetch or axios to grab data from over the network

    • As it fetches data your thunk will dispatch actions into the redux store such as pending, fulfilled and rejected (error).

    • This is complicated to setup and maintain

    RTK Query provides a better way.

    RTK Query generates hooks for you at runtime that allow you to query and cache data or run mutations. Loading and error states are available automatically in your component via the hook and cache invalidation is easily manageable.

    This diagram illustrates the difference between handling cached data and fetching network data using Redux Thunk and RTK Query.

    Let's break down each part:

    Top Left: Invalidating Cached Data (Redux)

    • With Redux and Thunk, to invalidate cached data or refresh data, you have to handle it manually:

      • You dispatch an action via a thunk or use useEffect in the component to trigger data invalidation.

      • You manually dispatch actions to refetch or invalidate specific data.

      • This requires handling the logic for when and how to refresh the cached data, often involving multiple steps.

    Top Middle: Using Cached Data (Redux)

    • With Redux, when you want to reuse cached data across components:

      • The data is stored in the Redux store.

      • Components access the cached data using selectors.

      • If the data is already available in the store, components simply render the cached data without needing to refetch.

      • The challenge is managing cache invalidation and ensuring that data is still up-to-date.

    Top Right: Fetching & Caching Network Data (Redux + Thunk)

    • In this case:

      • You use a Thunk action to fetch data (e.g., with fetch or axios).

      • You dispatch actions to initiate the request, handle loading, and update the Redux store with the fetched data.

      • Selectors are used to access the data in components.

      • Components also use useEffect or other hooks to trigger the fetching action at the right time (e.g., on mount or user interaction).

      • This approach works but requires manual management of when to fetch, update, and cache data.

    Bottom Left: Invalidating Cached Data (RTK Query)

    • With RTK Query, invalidating cached data is simplified:

      • RTK Query provides automatic cache management via tags and automatic data refetching based on the tags.

      • The component simply interacts with the API layer provided by RTK Query through custom hooks (useQuery, useMutation), and RTK Query handles the caching and invalidation logic.

      • You don’t need to manually dispatch actions for invalidating or refetching data—RTK Query does it based on its cache policies.

    Bottom Middle: Using Cached Data (RTK Query)

    • With RTK Query, using cached data is seamless:

      • Components use custom query hooks (auto-generated by RTK Query) to access data.

      • The data is fetched and cached by RTK Query, and any subsequent renders or components that need the same data will automatically use the cached version.

      • No manual management is needed; RTK Query handles caching internally.

    Bottom Right: Fetching & Caching Network Data (RTK Query)

    • RTK Query abstracts away much of the complexity:

      • RTK Query handles everything: it automatically caches, refetches, and synchronizes data across your components.

      • You simply call the API via query hooks in your components, and RTK Query takes care of dispatching actions, fetching data, storing it in Redux, and managing loading and error states.

      • The message "THINGS YOU NO LONGER HAVE TO WORRY ABOUT" in the diagram indicates that RTK Query eliminates the need for manually managing cache invalidation, error handling, and re-fetching logic.

    Summary of the Diagram

    • Redux with Thunk: You need to manually handle actions, thunks, cache invalidation, and re-fetching data when needed. It provides flexibility but requires more boilerplate code and management.

    • RTK Query: Simplifies the data fetching and caching process by automatically managing cache invalidation, re-fetching, and keeping data up-to-date. It provides hooks for fetching and mutating data, while abstracting away many complexities like caching and syncing server state with the Redux store.

    End2End: React RTK Query ExpressMongoDb Posts CRUD APP

    Let's create a full-stack CRUD application to store user profiles using React + Redux Toolkit Query on the frontend and Express + MongoDB on the backend.

    Backend Setup (Express + MongoDB + TypeScript)

    1. Backend Folder Structure:

    backend/
    +export default App;

    Step 3: Running the Full Stack Application

    1. Run the Backend:

    cd backend
    +npm run dev
    1. Run the Frontend:

    cd frontend
    +npm run dev

    Open your browser at http://localhost:5173/ to view the app. The app will fetch, display, create, update, and delete posts with full CRUD functionality using React + Redux Toolkit for the frontend and Express + MongoDB for the backend.

    This setup provides a clean, scalable way to manage async actions in Redux, ensuring best practices are followed throughout the stack.

    RTK Query - Data Fetching

    What is RTK Query?

    RTK Query is a powerful data-fetching and caching tool that is built directly into Redux Toolkit. It simplifies the process of data fetching in Redux applications by abstracting away many of the complexities that come with handling loading, caching, re-fetching, and synchronizing server state.

    Unlike traditional Redux approaches (using thunk), which require you to manually manage async operations, RTK Query does a lot of the heavy lifting for you.

    Benefits of Using RTK Query

    1. Automatic Caching: RTK Query automatically caches your API responses and avoids unnecessary network requests. When data is fetched, it stores that data and serves it on subsequent requests until it's invalidated.

    2. Simplicity: RTK Query abstracts much of the boilerplate code you'd typically write with redux-thunk. You no longer need to manage loading, error, or success states manually.

    3. Optimized Performance: It efficiently handles state updates and only re-fetches data when necessary (e.g., when cache expiration happens).

    4. Automatic Refetching: If a component relies on a particular piece of data, RTK Query can automatically re-fetch that data when necessary, such as when a dependent component is re-rendered or when the cache becomes stale.

    5. Code Reusability: You can define API endpoints once and then use them across multiple components without re-writing data fetching logic.

    RTK Query Main Architecture and Components

    1. createApi:

    • This is the core function in RTK Query. It defines your endpoints (API operations such as GET, POST, PUT, and DELETE) and how your application interacts with the backend.

    • Example: Defining API endpoints for fetching posts, creating a post, and deleting a post.

    1. Base Query:

    • baseQuery is a function that acts as the low-level fetcher for your endpoints. Typically, this will use fetch or axios to make HTTP requests. RTK Query allows you to customize baseQuery for all your API operations.

    • Example: You can configure a global axios instance or fetch to handle authentication tokens, error handling, and more.

    1. Endpoints:

    • Endpoints represent the actual API operations such as GET /posts, POST /users, etc. Each endpoint defines how data is fetched, updated, or deleted.

    1. Hooks:

    • RTK Query automatically generates React hooks for every endpoint. These hooks are used inside your React components to trigger data fetching, mutation (POST, PUT, DELETE), and cache handling.

    Best Way to Use RTK Query

    1. Define API Services in a Centralized Place: Create a central API slice where you define all endpoints and configuration.

    2. Leverage Auto-Generated Hooks: RTK Query automatically provides hooks (e.g., useGetPostsQuery, useAddPostMutation) which you can use in your components to interact with the API.

    3. Cache Management: Use RTK Query's built-in cache management to optimize performance. You can manually invalidate the cache when needed.

    4. Error Handling: RTK Query includes mechanisms for automatic error handling, but you can also customize and intercept errors globally in the baseQuery.

    RTK Query Vs Redux Thunk

    RTK-Query.png
    RTK-Query.png

    In redux we typically model network fetching and caching like this.

    • You have a component, which subscribes to data from the redux store via selectors

    • In a useEffect hook you check if the data doesn’t exist and then dispatch a thunk

    • Your thunk will use a tool like fetch or axios to grab data from over the network

    • As it fetches data your thunk will dispatch actions into the redux store such as pending, fulfilled and rejected (error).

    • This is complicated to setup and maintain

    RTK Query provides a better way.

    RTK Query generates hooks for you at runtime that allow you to query and cache data or run mutations. Loading and error states are available automatically in your component via the hook and cache invalidation is easily manageable.

    This diagram illustrates the difference between handling cached data and fetching network data using Redux Thunk and RTK Query.

    Let's break down each part:

    Top Left: Invalidating Cached Data (Redux)

    • With Redux and Thunk, to invalidate cached data or refresh data, you have to handle it manually:

      • You dispatch an action via a thunk or use useEffect in the component to trigger data invalidation.

      • You manually dispatch actions to refetch or invalidate specific data.

      • This requires handling the logic for when and how to refresh the cached data, often involving multiple steps.

    Top Middle: Using Cached Data (Redux)

    • With Redux, when you want to reuse cached data across components:

      • The data is stored in the Redux store.

      • Components access the cached data using selectors.

      • If the data is already available in the store, components simply render the cached data without needing to refetch.

      • The challenge is managing cache invalidation and ensuring that data is still up-to-date.

    Top Right: Fetching & Caching Network Data (Redux + Thunk)

    • In this case:

      • You use a Thunk action to fetch data (e.g., with fetch or axios).

      • You dispatch actions to initiate the request, handle loading, and update the Redux store with the fetched data.

      • Selectors are used to access the data in components.

      • Components also use useEffect or other hooks to trigger the fetching action at the right time (e.g., on mount or user interaction).

      • This approach works but requires manual management of when to fetch, update, and cache data.

    Bottom Left: Invalidating Cached Data (RTK Query)

    • With RTK Query, invalidating cached data is simplified:

      • RTK Query provides automatic cache management via tags and automatic data refetching based on the tags.

      • The component simply interacts with the API layer provided by RTK Query through custom hooks (useQuery, useMutation), and RTK Query handles the caching and invalidation logic.

      • You don’t need to manually dispatch actions for invalidating or refetching data—RTK Query does it based on its cache policies.

    Bottom Middle: Using Cached Data (RTK Query)

    • With RTK Query, using cached data is seamless:

      • Components use custom query hooks (auto-generated by RTK Query) to access data.

      • The data is fetched and cached by RTK Query, and any subsequent renders or components that need the same data will automatically use the cached version.

      • No manual management is needed; RTK Query handles caching internally.

    Bottom Right: Fetching & Caching Network Data (RTK Query)

    • RTK Query abstracts away much of the complexity:

      • RTK Query handles everything: it automatically caches, refetches, and synchronizes data across your components.

      • You simply call the API via query hooks in your components, and RTK Query takes care of dispatching actions, fetching data, storing it in Redux, and managing loading and error states.

      • The message "THINGS YOU NO LONGER HAVE TO WORRY ABOUT" in the diagram indicates that RTK Query eliminates the need for manually managing cache invalidation, error handling, and re-fetching logic.

    Summary of the Diagram

    • Redux with Thunk: You need to manually handle actions, thunks, cache invalidation, and re-fetching data when needed. It provides flexibility but requires more boilerplate code and management.

    • RTK Query: Simplifies the data fetching and caching process by automatically managing cache invalidation, re-fetching, and keeping data up-to-date. It provides hooks for fetching and mutating data, while abstracting away many complexities like caching and syncing server state with the Redux store.

    End2End: React RTK Query ExpressMongoDb Posts CRUD APP

    Let's create a full-stack CRUD application to store user profiles using React + Redux Toolkit Query on the frontend and Express + MongoDB on the backend.

    Backend Setup (Express + MongoDB + TypeScript)

    1. Backend Folder Structure:

    backend/
     ├── src/
     │   ├── config/
     │   │   └── db.ts
    @@ -4395,4 +4395,437 @@
     
     export default App;

    Final Steps to Run the Application

    1. Run the Backend:

    cd backend
     npm run dev
    1. Run the Frontend:

    cd frontend
    -npm run dev

    Open the frontend at http://localhost:5175/. The app will fetch, create, update, and delete user profiles, fully utilizing RTK Query to handle the frontend's API interaction and caching.

    Key Features of RTK Query in This Application:

    • Efficient Caching: RTK Query caches all user data and only re-fetches if the data becomes invalidated.

    • Simplified Data Fetching: The useGetUsersQuery, useAddUserMutation, useDeleteUserMutation, and useUpdateUserMutation hooks handle the API requests without manually managing loading or error states.

    • Auto-Generated Hooks: The API slice automatically provides reusable hooks, making it easier to manage CRUD operations for users.

    This setup shows how you can build a scalable, performant, and modern full-stack CRUD application using RTK Query and Redux Toolkit for efficient data fetching and management.

    \ No newline at end of file +npm run dev

    Open the frontend at http://localhost:5175/. The app will fetch, create, update, and delete user profiles, fully utilizing RTK Query to handle the frontend's API interaction and caching.

    Key Features of RTK Query in This Application:

    This setup shows how you can build a scalable, performant, and modern full-stack CRUD application using RTK Query and Redux Toolkit for efficient data fetching and management.

    15. Advanced State Management with Zustand

    zustand.png
    zustand.png

    A small, fast, and scalable bearbones state management solution. Zustand has a comfy API based on hooks. It isn't boilerplatey or opinionated, but has enough convention to be explicit and flux-like.

    Don't disregard it because it's cute, it has claws! Lots of time was spent to deal with common pitfalls, like the dreaded zombie child problem, React concurrency, and context loss between mixed renderers. It may be the one state manager in the React space that gets all of these right.

    Sure! Let’s dive into Zustand with React and TypeScript, step-by-step, just like we did with Redux.

    What is Zustand?

    Zustand is a lightweight and fast state management library for React, similar to Redux but simpler to use. It's perfect for projects where you want to avoid the complexity of Redux Toolkit or don't need the full Redux ecosystem.

    Setting Up a Project with Zustand and TypeScript

    Step 1: Setting Up the Project

    1. Initialize a new React project using Vite (or any other method like CRA). Here’s how to do it with Vite for TypeScript:

      npm create vite@latest zustand-ts-app -- --template react-ts
      +cd zustand-ts-app
      +npm install
    2. Install Zustand:

      npm install zustand

    15.1 Counter Example

    Sure! Let's update your counter example to use the latest version of Zustand. As of now, the latest stable version is 4.5.5¹.

    1. Counter Example with Zustand and TypeScript

    First, let's build a simple counter app using Zustand, which will allow you to increment, decrement, and reset the counter.

    Step 1: Create a Counter Store with Zustand

    File: src/store/useCounterStore.ts

    import { create } from 'zustand';
    +
    +// Define the counter store state interface
    +interface CounterState {
    +  count: number;
    +  increment: () => void;
    +  decrement: () => void;
    +  reset: () => void;
    +}
    +
    +// Zustand store for managing the counter
    +export const useCounterStore = create<CounterState>((set) => ({
    +  count: 0,
    +  increment: () => set((state) => ({ count: state.count + 1 })),
    +  decrement: () => set((state) => ({ count: state.count - 1 })),
    +  reset: () => set({ count: 0 }),
    +}));

    Explanation:

    • The Zustand store has three methods (increment, decrement, reset) and a count state.

    • The set function modifies the store’s state.

    Step 2: Create the Counter Component

    File: src/components/Counter.tsx

    import React from 'react';
    +import { useCounterStore } from '../store/useCounterStore';
    +
    +const Counter: React.FC = () => {
    +  const count = useCounterStore((state) => state.count);
    +  const increment = useCounterStore((state) => state.increment);
    +  const decrement = useCounterStore((state) => state.decrement);
    +  const reset = useCounterStore((state) => state.reset);
    +
    +  return (
    +    <div className="flex flex-col items-center space-y-4">
    +      <h1 className="text-2xl font-bold">Counter: {count}</h1>
    +      <div className="space-x-4">
    +        <button
    +          onClick={increment}
    +          className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
    +        >
    +          Increment
    +        </button>
    +        <button
    +          onClick={decrement}
    +          className="bg-red-500 text-white px-4 py-2 rounded hover:bg-red-600"
    +        >
    +          Decrement
    +        </button>
    +        <button
    +          onClick={reset}
    +          className="bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-600"
    +        >
    +          Reset
    +        </button>
    +      </div>
    +    </div>
    +  );
    +};
    +
    +export default Counter;

    Explanation:

    • We use useCounterStore to access the count, increment, decrement, and reset methods.

    • The component renders buttons to interact with the counter.

    Step 3: Bring It Together in App Component

    File: src/App.tsx

    import React from 'react';
    +import Counter from './components/Counter';
    +
    +const App: React.FC = () => {
    +  return (
    +    <div className="container mx-auto p-4">
    +      <h1 className="text-3xl font-bold text-center mb-6">Zustand Counter App</h1>
    +      <Counter />
    +    </div>
    +  );
    +};
    +
    +export default App;

    Step 4: Run the App

    Start your app by running:

    npm run dev

    Summary:

    • The counter example helped introduce Zustand's basic store management and state update.

    Feel free to ask any questions or

    15.2 End2End: React Zustand ExpressMongoDb Posts CRUD APP

    I can see you've shared an image showing the structure of your app. Let's go step-by-step to refactor your current app (built with RTK Query) into Zustand with TypeScript using the latest practices.

    Steps to Refactor RTK Query to Zustand

    1. Remove RTK Query Dependencies: Since we're moving from Redux Toolkit (RTK Query) to Zustand, you no longer need @reduxjs/toolkit or any other Redux dependencies. First, remove them from your package.json:

      npm uninstall @reduxjs/toolkit react-redux

      also remember to remove the provider from Main.tsx

    import { Provider } from 'react-redux';
    +
    +<Provider store={store}>
    +</Provider>
    1. Install Zustand: Next, install Zustand:

      npm install zustand
    2. Convert the API Logic: We'll now handle fetching and managing posts using Zustand instead of RTK Query.

    Step 1: Create a Zustand Store for Posts

    Instead of postApi.ts (RTK Query), we will now use Zustand to manage CRUD operations and state.

    • File: src/store/usePostStore.ts

    import create from 'zustand';
    +import axios from 'axios';
    +
    +export interface Post {
    +  _id: string;
    +  title: string;
    +  body: string;
    +}
    +
    +interface PostState {
    +  posts: Post[];
    +  loading: boolean;
    +  error: string | null;
    +  fetchPosts: () => void;
    +  createPost: (post: Omit<Post, '_id'>) => Promise<void>;
    +  updatePost: (id: string, post: Omit<Post, '_id'>) => Promise<void>;
    +  deletePost: (id: string) => Promise<void>;
    +}
    +
    +// Zustand store for managing posts and CRUD operations
    +export const usePostStore = create<PostState>((set) => ({
    +  posts: [],
    +  loading: false,
    +  error: null,
    +
    +  // Fetch all posts
    +  fetchPosts: async () => {
    +    set({ loading: true, error: null });
    +    try {
    +      const response = await axios.get('http://localhost:5000/api/posts');
    +      set({ posts: response.data, loading: false });
    +    } catch (error) {
    +      set({ loading: false, error: 'Error fetching posts' });
    +    }
    +  },
    +
    +  // Create a new post
    +  createPost: async (post) => {
    +    set({ loading: true, error: null });
    +    try {
    +      const response = await axios.post('http://localhost:5000/api/posts', post);
    +      set((state) => ({
    +        posts: [...state.posts, response.data],
    +        loading: false,
    +      }));
    +    } catch (error) {
    +      set({ loading: false, error: 'Error creating post' });
    +    }
    +  },
    +
    +  // Update a post
    +  updatePost: async (id, updatedPost) => {
    +    set({ loading: true, error: null });
    +    try {
    +      const response = await axios.put(`http://localhost:5000/api/posts/${id}`, updatedPost);
    +      set((state) => ({
    +        posts: state.posts.map((post) => (post._id === id ? response.data : post)),
    +        loading: false,
    +      }));
    +    } catch (error) {
    +      set({ loading: false, error: 'Error updating post' });
    +    }
    +  },
    +
    +  // Delete a post
    +  deletePost: async (id) => {
    +    set({ loading: true, error: null });
    +    try {
    +      await axios.delete(`http://localhost:5000/api/posts/${id}`);
    +      set((state) => ({
    +        posts: state.posts.filter((post) => post._id !== id),
    +        loading: false,
    +      }));
    +    } catch (error) {
    +      set({ loading: false, error: 'Error deleting post' });
    +    }
    +  },
    +}));

    Step 2: Refactor the PostForm.tsx Component

    Now that we’ve replaced RTK Query with Zustand, let's update the components to use Zustand's store.

    • File: src/components/PostForm.tsx

    import React, { useState } from 'react';
    +import { usePostStore } from '../store/usePostStore';
    +
    +const PostForm: React.FC = () => {
    +  const [title, setTitle] = useState('');
    +  const [body, setBody] = useState('');
    +  const createPost = usePostStore((state) => state.createPost);
    +
    +  const handleSubmit = async (e: React.FormEvent) => {
    +    e.preventDefault();
    +    if (title && body) {
    +      await createPost({ title, body });
    +      setTitle('');
    +      setBody('');
    +    }
    +  };
    +
    +  return (
    +    <div className="container mx-auto px-4 my-6">
    +      <form
    +        className="bg-white shadow-lg rounded-lg p-6 max-w-lg mx-auto"
    +        onSubmit={handleSubmit}
    +      >
    +        <h2 className="text-2xl font-bold mb-6">Create a New Post</h2>
    +        <div className="mb-4">
    +          <label htmlFor="title" className="block text-gray-700">
    +            Title
    +          </label>
    +          <input
    +            type="text"
    +            id="title"
    +            className="w-full px-4 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-400"
    +            value={title}
    +            onChange={(e) => setTitle(e.target.value)}
    +            required
    +          />
    +        </div>
    +        <div className="mb-4">
    +          <label htmlFor="body" className="block text-gray-700">
    +            Body
    +          </label>
    +          <textarea
    +            id="body"
    +            className="w-full px-4 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-400"
    +            value={body}
    +            onChange={(e) => setBody(e.target.value)}
    +            required
    +          ></textarea>
    +        </div>
    +        <button
    +          type="submit"
    +          className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 transition"
    +        >
    +          Submit
    +        </button>
    +      </form>
    +    </div>
    +  );
    +};
    +
    +export default PostForm;

    Step 3: Refactor the PostList.tsx Component

    We'll refactor the PostList to use Zustand for fetching, updating, and deleting posts.

    • File: src/components/PostList.tsx

    import React, { useEffect, useState } from 'react';
    +import { usePostStore } from '../store/usePostStore';
    +import { Trash2, Edit2, Check, X } from 'lucide-react';
    +import ClockLoader from 'react-spinners/ClockLoader';
    +
    +const PostList: React.FC = () => {
    +  const { posts, loading, error, fetchPosts, deletePost, updatePost } = usePostStore();
    +  const [editingId, setEditingId] = useState<string | null>(null);
    +  const [editTitle, setEditTitle] = useState('');
    +  const [editBody, setEditBody] = useState('');
    +
    +  useEffect(() => {
    +    fetchPosts();
    +  }, [fetchPosts]);
    +
    +  const startEditing = (id: string, title: string, body: string) => {
    +    setEditingId(id);
    +    setEditTitle(title);
    +    setEditBody(body);
    +  };
    +
    +  const saveEdit = async () => {
    +    if (editingId) {
    +      await updatePost(editingId, { title: editTitle.trim(), body: editBody.trim() });
    +      setEditingId(null);
    +    }
    +  };
    +
    +  const cancelEdit = () => {
    +    setEditingId(null);
    +    setEditTitle('');
    +    setEditBody('');
    +  };
    +
    +  if (loading) {
    +    return <ClockLoader color="#36d7b7" />;
    +  }
    +
    +  if (error) return <p className="text-red-500 text-center">Error: {error}</p>;
    +
    +  return (
    +    <div className="container mx-auto px-4 min-w-full">
    +      <h1 className="text-3xl font-bold text-center my-6">Posts</h1>
    +      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
    +        {posts.map((post) => (
    +          <div key={post._id} className="bg-white shadow-lg rounded-lg p-6 flex flex-col justify-between">
    +            {editingId === post._id ? (
    +              <>
    +                {/* Editing Mode */}
    +                <div>
    +                  <input
    +                    type="text"
    +                    value={editTitle}
    +                    onChange={(e) => setEditTitle(e.target.value)}
    +                    className="w-full mb-2 p-2 border rounded"
    +                  />
    +                  <textarea
    +                    value={editBody}
    +                    onChange={(e) => setEditBody(e.target.value)}
    +                    className="w-full mb-2 p-2 border rounded"
    +                  ></textarea>
    +                </div>
    +                <div className="flex justify-end space-x-2">
    +                  <button onClick={saveEdit} className="p-2 text-green-600 hover:text-green-800">
    +                    <Check size={20} />
    +                  </button>
    +                  <button onClick={cancelEdit} className="p-2 text-red-600 hover:text-red-800">
    +                    <X size={20} />
    +                  </button>
    +                </div>
    +              </>
    +            ) : (
    +              <>
    +                {/* Viewing Mode */}
    +                <div>
    +                  <h2 className="text-xl font-semibold mb-4">{post.title}</h2>
    +                  <p className="text-gray-700">{post.body}</p>
    +                </div>
    +                <div className="flex justify-between mt-4">
    +                  <button
    +                    className="p-2 text-blue-600 hover:text-blue-800"
    +                    onClick={() => startEditing(post._id, post.title, post.body)}
    +                  >
    +                    <Edit2 size={20} />
    +                  </button>
    +                  <button
    +                    className="p-2 text-red-600 hover:text-red-800"
    +                    onClick={() => deletePost(post._id)}
    +                  >
    +                    <Trash2 size={20} />
    +                  </button>
    +                </div>
    +              </>
    +            )}
    +          </div>
    +        ))}
    +      </div>
    +    </div>
    +  );
    +};
    +
    +export default PostList;

    Conclusion:

    • We replaced RTK Query with Zustand for managing the state of posts.

    • Zustand's store is simpler and doesn't require boilerplate code.

    • The flow for fetching, creating, updating, and deleting posts remains similar, but Zustand provides a more minimalistic and straightforward approach.

    15.3 End2End: React Zustand ReactQuery ExpressMongoDb Posts CRUD APP

    Combining Zustand with React Query can give you powerful state management and data fetching capabilities. Here's how you can integrate them:

    Step 1: Install Dependencies

    First, make sure you have the necessary packages installed:

    npm install zustand @tanstack/react-query axios

    Step 2: Create a Zustand Store

    We'll create a Zustand store to manage the state of our posts.

    File: src/store/usePostStore.ts

    import { create } from 'zustand';
    +import { devtools } from 'zustand/middleware';
    +import axios from 'axios';
    +
    +// Define the Post interface
    +export interface Post {
    +  _id: string;
    +  title: string;
    +  body: string;
    +}
    +
    +// Define the state interface for the Zustand store
    +interface PostState {
    +  posts: Post[];
    +  setPosts: (posts: Post[]) => void;
    +}
    +
    +// Create the Zustand store with devtools middleware for debugging
    +export const usePostStore = create<PostState>()(
    +  devtools((set) => ({
    +    posts: [], // Initial state for posts
    +    setPosts: (posts) => set({ posts }), // Function to update posts in the state
    +  }))
    +);

    Step 3: Set Up React Query

    Next, we'll set up React Query to handle data fetching.

    File: src/hooks/usePosts.ts

    import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
    +import axios from 'axios';
    +import { usePostStore } from '../store/usePostStore';
    +
    +// Function to fetch posts from the API
    +const fetchPosts = async () => {
    +  const response = await axios.get('http://localhost:5000/api/posts');
    +  return response.data;
    +};
    +
    +// Custom hook to manage posts using React Query and Zustand
    +export const usePosts = () => {
    +  const setPosts = usePostStore((state) => state.setPosts);
    +  const queryClient = useQueryClient();
    +
    +  // Fetch posts and update Zustand store on success
    +  const { data, error, isLoading } = useQuery(['posts'], fetchPosts, {
    +    onSuccess: (data) => {
    +      setPosts(data);
    +    },
    +  });
    +
    +  // Mutation to create a new post
    +  const createPost = useMutation(
    +    (newPost) => axios.post('http://localhost:5000/api/posts', newPost),
    +    {
    +      onSuccess: () => {
    +        queryClient.invalidateQueries(['posts']); // Invalidate queries to refetch posts
    +      },
    +    }
    +  );
    +
    +  // Mutation to update an existing post
    +  const updatePost = useMutation(
    +    ({ id, updatedPost }) =>
    +      axios.put(`http://localhost:5000/api/posts/${id}`, updatedPost),
    +    {
    +      onSuccess: () => {
    +        queryClient.invalidateQueries(['posts']); // Invalidate queries to refetch posts
    +      },
    +    }
    +  );
    +
    +  // Mutation to delete a post
    +  const deletePost = useMutation(
    +    (id) => axios.delete(`http://localhost:5000/api/posts/${id}`),
    +    {
    +      onSuccess: () => {
    +        queryClient.invalidateQueries(['posts']); // Invalidate queries to refetch posts
    +      },
    +    }
    +  );
    +
    +  return { data, error, isLoading, createPost, updatePost, deletePost };
    +};

    Step 4: Create the Component

    Now, let's create a component to display and manage posts.

    File: src/components/PostList.tsx

    import React from 'react';
    +import { usePosts } from '../hooks/usePosts';
    +import { usePostStore } from '../store/usePostStore';
    +
    +const PostList: React.FC = () => {
    +  const { data: posts, error, isLoading, createPost, updatePost, deletePost } = usePosts();
    +  const postStore = usePostStore();
    +
    +  if (isLoading) return <div>Loading...</div>; // Display loading state
    +  if (error) return <div>Error: {error.message}</div>; // Display error state
    +
    +  return (
    +    <div>
    +      <h1>Posts</h1>
    +      <ul>
    +        {postStore.posts.map((post) => (
    +          <li key={post._id}>
    +            <h2>{post.title}</h2>
    +            <p>{post.body}</p>
    +            <button onClick={() => deletePost.mutate(post._id)}>Delete</button>
    +            <button onClick={() => updatePost.mutate({ id: post._id, updatedPost: { title: 'Updated Title', body: 'Updated Body' } })}>
    +              Update
    +            </button>
    +          </li>
    +        ))}
    +      </ul>
    +      <button onClick={() => createPost.mutate({ title: 'New Post', body: 'New Body' })}>Add Post</button>
    +    </div>
    +  );
    +};
    +
    +export default PostList;

    Step 5: Integrate in App Component

    Finally, integrate everything in your main app component.

    File: src/App.tsx

    import React from 'react';
    +import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
    +import PostList from './components/PostList';
    +
    +// Create a QueryClient instance
    +const queryClient = new QueryClient();
    +
    +const App: React.FC = () => {
    +  return (
    +    <QueryClientProvider client={queryClient}>
    +      <div className="container mx-auto p-4">
    +        <h1 className="text-3xl font-bold text-center mb-6">Zustand and React Query App</h1>
    +        <PostList />
    +      </div>
    +    </QueryClientProvider>
    +  );
    +};
    +
    +export default App;

    Explanation:

    • Zustand Store: Manages the local state of posts.

    • React Query: Handles data fetching, caching, and synchronization.

    • PostList Component: Displays posts and provides CRUD operations.

    \ No newline at end of file diff --git a/Writerside/pdfSourceR.pdf b/Writerside/pdfSourceR.pdf index 67f1fcc..8fb6719 100644 Binary files a/Writerside/pdfSourceR.pdf and b/Writerside/pdfSourceR.pdf differ diff --git a/Writerside/r.tree b/Writerside/r.tree index d72e756..977079e 100644 --- a/Writerside/r.tree +++ b/Writerside/r.tree @@ -69,11 +69,17 @@ - - + + + + + + + + \ No newline at end of file diff --git a/Writerside/redirection-rules.xml b/Writerside/redirection-rules.xml index fcd87c6..b64b41f 100644 --- a/Writerside/redirection-rules.xml +++ b/Writerside/redirection-rules.xml @@ -263,4 +263,12 @@ 11-Best-Practices-and-Common-Pitfalls.html + + Created after removal of "Thunk ExpressMongoDb Posts CRUD APP" from React-TypeScript + End2End-React-ReduxThunk-ExpressMongoDb-Posts-CRUD-APP.html + + + Created after removal of "15.2 TodoApp Example" from React-TypeScript + TodoApp-Example.html + \ No newline at end of file diff --git a/Writerside/topics/15-1-Counter-Example.md b/Writerside/topics/15-1-Counter-Example.md new file mode 100644 index 0000000..3cb096b --- /dev/null +++ b/Writerside/topics/15-1-Counter-Example.md @@ -0,0 +1,119 @@ +# 15.1 Counter Example + +Sure! Let's update your counter example to use the latest version of Zustand. As of now, the latest stable version is 4.5.5¹. + +### 1. Counter Example with Zustand and TypeScript + +First, let's build a simple counter app using Zustand, which will allow you to increment, decrement, and reset the counter. + +#### Step 1: Create a Counter Store with Zustand + +**File: `src/store/useCounterStore.ts`** + +```typescript +import { create } from 'zustand'; + +// Define the counter store state interface +interface CounterState { + count: number; + increment: () => void; + decrement: () => void; + reset: () => void; +} + +// Zustand store for managing the counter +export const useCounterStore = create((set) => ({ + count: 0, + increment: () => set((state) => ({ count: state.count + 1 })), + decrement: () => set((state) => ({ count: state.count - 1 })), + reset: () => set({ count: 0 }), +})); +``` + +**Explanation:** + +- The Zustand store has three methods (`increment`, `decrement`, `reset`) and a `count` state. +- The `set` function modifies the store’s state. + +#### Step 2: Create the Counter Component + +**File: `src/components/Counter.tsx`** + +```typescript +import React from 'react'; +import { useCounterStore } from '../store/useCounterStore'; + +const Counter: React.FC = () => { + const count = useCounterStore((state) => state.count); + const increment = useCounterStore((state) => state.increment); + const decrement = useCounterStore((state) => state.decrement); + const reset = useCounterStore((state) => state.reset); + + return ( +
    +

    Counter: {count}

    +
    + + + +
    +
    + ); +}; + +export default Counter; +``` + +**Explanation:** + +- We use `useCounterStore` to access the `count`, `increment`, `decrement`, and `reset` methods. +- The component renders buttons to interact with the counter. + +#### Step 3: Bring It Together in App Component + +**File: `src/App.tsx`** + +```typescript +import React from 'react'; +import Counter from './components/Counter'; + +const App: React.FC = () => { + return ( +
    +

    Zustand Counter App

    + +
    + ); +}; + +export default App; +``` + +#### Step 4: Run the App + +Start your app by running: + +```bash +npm run dev +``` + + +### Summary: +- The **counter** example helped introduce Zustand's basic store management and state update. + +Feel free to ask any questions or \ No newline at end of file diff --git a/Writerside/topics/15-3-End2End-React-Zustand-ReactQuery-ExpressMongoDb-Posts-CRUD-APP.md b/Writerside/topics/15-3-End2End-React-Zustand-ReactQuery-ExpressMongoDb-Posts-CRUD-APP.md new file mode 100644 index 0000000..6c42c1e --- /dev/null +++ b/Writerside/topics/15-3-End2End-React-Zustand-ReactQuery-ExpressMongoDb-Posts-CRUD-APP.md @@ -0,0 +1,188 @@ +# 15.3 End2End: React Zustand ReactQuery ExpressMongoDb Posts CRUD APP + + +Combining Zustand with React Query can give you powerful state management and data fetching capabilities. Here's how you can integrate them: + +### Step 1: Install Dependencies + +First, make sure you have the necessary packages installed: + +```bash +npm install zustand @tanstack/react-query axios +``` + +### Step 2: Create a Zustand Store + +We'll create a Zustand store to manage the state of our posts. + +**File: `src/store/usePostStore.ts`** + +```typescript +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; +import axios from 'axios'; + +// Define the Post interface +export interface Post { + _id: string; + title: string; + body: string; +} + +// Define the state interface for the Zustand store +interface PostState { + posts: Post[]; + setPosts: (posts: Post[]) => void; +} + +// Create the Zustand store with devtools middleware for debugging +export const usePostStore = create()( + devtools((set) => ({ + posts: [], // Initial state for posts + setPosts: (posts) => set({ posts }), // Function to update posts in the state + })) +); + +``` + +### Step 3: Set Up React Query + +Next, we'll set up React Query to handle data fetching. + +**File: `src/hooks/usePosts.ts`** + +```typescript +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; +import axios from 'axios'; +import { usePostStore } from '../store/usePostStore'; + +// Function to fetch posts from the API +const fetchPosts = async () => { + const response = await axios.get('http://localhost:5000/api/posts'); + return response.data; +}; + +// Custom hook to manage posts using React Query and Zustand +export const usePosts = () => { + const setPosts = usePostStore((state) => state.setPosts); + const queryClient = useQueryClient(); + + // Fetch posts and update Zustand store on success + const { data, error, isLoading } = useQuery(['posts'], fetchPosts, { + onSuccess: (data) => { + setPosts(data); + }, + }); + + // Mutation to create a new post + const createPost = useMutation( + (newPost) => axios.post('http://localhost:5000/api/posts', newPost), + { + onSuccess: () => { + queryClient.invalidateQueries(['posts']); // Invalidate queries to refetch posts + }, + } + ); + + // Mutation to update an existing post + const updatePost = useMutation( + ({ id, updatedPost }) => + axios.put(`http://localhost:5000/api/posts/${id}`, updatedPost), + { + onSuccess: () => { + queryClient.invalidateQueries(['posts']); // Invalidate queries to refetch posts + }, + } + ); + + // Mutation to delete a post + const deletePost = useMutation( + (id) => axios.delete(`http://localhost:5000/api/posts/${id}`), + { + onSuccess: () => { + queryClient.invalidateQueries(['posts']); // Invalidate queries to refetch posts + }, + } + ); + + return { data, error, isLoading, createPost, updatePost, deletePost }; +}; + +``` + +### Step 4: Create the Component + +Now, let's create a component to display and manage posts. + +**File: `src/components/PostList.tsx`** + +```typescript +import React from 'react'; +import { usePosts } from '../hooks/usePosts'; +import { usePostStore } from '../store/usePostStore'; + +const PostList: React.FC = () => { + const { data: posts, error, isLoading, createPost, updatePost, deletePost } = usePosts(); + const postStore = usePostStore(); + + if (isLoading) return
    Loading...
    ; // Display loading state + if (error) return
    Error: {error.message}
    ; // Display error state + + return ( +
    +

    Posts

    +
      + {postStore.posts.map((post) => ( +
    • +

      {post.title}

      +

      {post.body}

      + + +
    • + ))} +
    + +
    + ); +}; + +export default PostList; + +``` + +### Step 5: Integrate in App Component + +Finally, integrate everything in your main app component. + +**File: `src/App.tsx`** + +```typescript +import React from 'react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import PostList from './components/PostList'; + +// Create a QueryClient instance +const queryClient = new QueryClient(); + +const App: React.FC = () => { + return ( + +
    +

    Zustand and React Query App

    + +
    +
    + ); +}; + +export default App; + +``` + +### Explanation: + +- **Zustand Store**: Manages the local state of posts. +- **React Query**: Handles data fetching, caching, and synchronization. +- **PostList Component**: Displays posts and provides CRUD operations. \ No newline at end of file diff --git a/Writerside/topics/End2End-React-ReduxThunk-ExpressMongoDb-Posts-CRUD-APP.md b/Writerside/topics/1End2End-React-ReduxThunk-ExpressMongoDb-Posts-CRUD-APP.md similarity index 99% rename from Writerside/topics/End2End-React-ReduxThunk-ExpressMongoDb-Posts-CRUD-APP.md rename to Writerside/topics/1End2End-React-ReduxThunk-ExpressMongoDb-Posts-CRUD-APP.md index 4be3a00..9b9ee6d 100644 --- a/Writerside/topics/End2End-React-ReduxThunk-ExpressMongoDb-Posts-CRUD-APP.md +++ b/Writerside/topics/1End2End-React-ReduxThunk-ExpressMongoDb-Posts-CRUD-APP.md @@ -1,5 +1,6 @@ # End2End: React ReduxThunk ExpressMongoDb Posts CRUD APP + This is a **fully functional CRUD app** using **TypeScript** for both the **Express backend** and the **React frontend**. The backend will use **MongoDB** for storing posts, and the frontend will use **Redux Toolkit(Thunk) ** to interact with the API for managing CRUD operations. ### **Step 1: Setting Up the Backend with Express, MongoDB, and TypeScript** @@ -401,7 +402,7 @@ export default postSlice.reducer; ``` 3. **Store Configuration**: - - Path: `src/store/store.ts` + - Path: `src/store/store.ts` ```javascript import { configureStore } from '@reduxjs/toolkit'; @@ -460,8 +461,8 @@ export default postSlice.reducer; ``` 3. **Post Form Component**: - - path `src/components/PostForm.tsx` - + - path `src/components/PostForm.tsx` + ```javascript import React, { useState } from 'react'; import { useDispatch } from 'react-redux'; diff --git a/Writerside/topics/Advanced-State-Management-with-Zustand.md b/Writerside/topics/Advanced-State-Management-with-Zustand.md new file mode 100644 index 0000000..e6857a6 --- /dev/null +++ b/Writerside/topics/Advanced-State-Management-with-Zustand.md @@ -0,0 +1,32 @@ +# 15. Advanced State Management with Zustand + +![zustand.png](zustand.png) + +A small, fast, and scalable bearbones state management solution. Zustand has a comfy API based on hooks. It isn't boilerplatey or opinionated, but has enough convention to be explicit and flux-like. + +Don't disregard it because it's cute, it has claws! Lots of time was spent to deal with common pitfalls, like the dreaded zombie child problem, React concurrency, and context loss between mixed renderers. It may be the one state manager in the React space that gets all of these right. + +Sure! Let’s dive into **Zustand** with **React** and **TypeScript**, step-by-step, just like we did with **Redux**. + +### What is **Zustand**? +**Zustand** is a lightweight and fast state management library for React, similar to Redux but simpler to use. It's perfect for projects where you want to avoid the complexity of Redux Toolkit or don't need the full Redux ecosystem. + +### Setting Up a Project with **Zustand** and **TypeScript** + +#### Step 1: Setting Up the Project + +1. **Initialize a new React project using Vite** (or any other method like CRA). Here’s how to do it with **Vite** for TypeScript: + + ```bash + npm create vite@latest zustand-ts-app -- --template react-ts + cd zustand-ts-app + npm install + ``` + +2. **Install Zustand**: + + ```bash + npm install zustand + ``` + +--- diff --git a/Writerside/topics/End2End-React-Zustand-ExpressMongoDb-Posts-CRUD-APP.md b/Writerside/topics/End2End-React-Zustand-ExpressMongoDb-Posts-CRUD-APP.md new file mode 100644 index 0000000..b6328c3 --- /dev/null +++ b/Writerside/topics/End2End-React-Zustand-ExpressMongoDb-Posts-CRUD-APP.md @@ -0,0 +1,308 @@ +# 15.2 End2End: React Zustand ExpressMongoDb Posts CRUD APP + +I can see you've shared an image showing the structure of your app. Let's go step-by-step to refactor your current app (built with RTK Query) into **Zustand** with **TypeScript** using the latest practices. + +### **Steps to Refactor RTK Query to Zustand** + +1. **Remove RTK Query Dependencies**: + Since we're moving from Redux Toolkit (RTK Query) to Zustand, you no longer need `@reduxjs/toolkit` or any other Redux dependencies. First, remove them from your `package.json`: + ```bash + npm uninstall @reduxjs/toolkit react-redux + ``` + also remember to remove the provider from `Main.tsx` +```javascript +import { Provider } from 'react-redux'; + + + +``` + +2. **Install Zustand**: + Next, install Zustand: + ```bash + npm install zustand + ``` + +3. **Convert the API Logic**: + We'll now handle fetching and managing posts using Zustand instead of RTK Query. + +--- + +### **Step 1: Create a Zustand Store for Posts** + +Instead of `postApi.ts` (RTK Query), we will now use Zustand to manage CRUD operations and state. + +- **File: `src/store/usePostStore.ts`** + +```javascript +import create from 'zustand'; +import axios from 'axios'; + +export interface Post { + _id: string; + title: string; + body: string; +} + +interface PostState { + posts: Post[]; + loading: boolean; + error: string | null; + fetchPosts: () => void; + createPost: (post: Omit) => Promise; + updatePost: (id: string, post: Omit) => Promise; + deletePost: (id: string) => Promise; +} + +// Zustand store for managing posts and CRUD operations +export const usePostStore = create((set) => ({ + posts: [], + loading: false, + error: null, + + // Fetch all posts + fetchPosts: async () => { + set({ loading: true, error: null }); + try { + const response = await axios.get('http://localhost:5000/api/posts'); + set({ posts: response.data, loading: false }); + } catch (error) { + set({ loading: false, error: 'Error fetching posts' }); + } + }, + + // Create a new post + createPost: async (post) => { + set({ loading: true, error: null }); + try { + const response = await axios.post('http://localhost:5000/api/posts', post); + set((state) => ({ + posts: [...state.posts, response.data], + loading: false, + })); + } catch (error) { + set({ loading: false, error: 'Error creating post' }); + } + }, + + // Update a post + updatePost: async (id, updatedPost) => { + set({ loading: true, error: null }); + try { + const response = await axios.put(`http://localhost:5000/api/posts/${id}`, updatedPost); + set((state) => ({ + posts: state.posts.map((post) => (post._id === id ? response.data : post)), + loading: false, + })); + } catch (error) { + set({ loading: false, error: 'Error updating post' }); + } + }, + + // Delete a post + deletePost: async (id) => { + set({ loading: true, error: null }); + try { + await axios.delete(`http://localhost:5000/api/posts/${id}`); + set((state) => ({ + posts: state.posts.filter((post) => post._id !== id), + loading: false, + })); + } catch (error) { + set({ loading: false, error: 'Error deleting post' }); + } + }, +})); +``` + +--- + +### **Step 2: Refactor the `PostForm.tsx` Component** + +Now that we’ve replaced RTK Query with Zustand, let's update the components to use Zustand's store. + +- **File: `src/components/PostForm.tsx`** + +```javascript +import React, { useState } from 'react'; +import { usePostStore } from '../store/usePostStore'; + +const PostForm: React.FC = () => { + const [title, setTitle] = useState(''); + const [body, setBody] = useState(''); + const createPost = usePostStore((state) => state.createPost); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (title && body) { + await createPost({ title, body }); + setTitle(''); + setBody(''); + } + }; + + return ( +
    +
    +

    Create a New Post

    +
    + + setTitle(e.target.value)} + required + /> +
    +
    + + +
    + +
    +
    + ); +}; + +export default PostForm; +``` + +--- + +### **Step 3: Refactor the `PostList.tsx` Component** + +We'll refactor the `PostList` to use Zustand for fetching, updating, and deleting posts. + +- **File: `src/components/PostList.tsx`** + +```javascript +import React, { useEffect, useState } from 'react'; +import { usePostStore } from '../store/usePostStore'; +import { Trash2, Edit2, Check, X } from 'lucide-react'; +import ClockLoader from 'react-spinners/ClockLoader'; + +const PostList: React.FC = () => { + const { posts, loading, error, fetchPosts, deletePost, updatePost } = usePostStore(); + const [editingId, setEditingId] = useState(null); + const [editTitle, setEditTitle] = useState(''); + const [editBody, setEditBody] = useState(''); + + useEffect(() => { + fetchPosts(); + }, [fetchPosts]); + + const startEditing = (id: string, title: string, body: string) => { + setEditingId(id); + setEditTitle(title); + setEditBody(body); + }; + + const saveEdit = async () => { + if (editingId) { + await updatePost(editingId, { title: editTitle.trim(), body: editBody.trim() }); + setEditingId(null); + } + }; + + const cancelEdit = () => { + setEditingId(null); + setEditTitle(''); + setEditBody(''); + }; + + if (loading) { + return ; + } + + if (error) return

    Error: {error}

    ; + + return ( +
    +

    Posts

    +
    + {posts.map((post) => ( +
    + {editingId === post._id ? ( + <> + {/* Editing Mode */} +
    + setEditTitle(e.target.value)} + className="w-full mb-2 p-2 border rounded" + /> + +
    +
    + + +
    + + ) : ( + <> + {/* Viewing Mode */} +
    +

    {post.title}

    +

    {post.body}

    +
    +
    + + +
    + + )} +
    + ))} +
    +
    + ); +}; + +export default PostList; +``` + +--- + +### **Conclusion**: +- We replaced **RTK Query** with **Zustand** for managing the state of posts. +- Zustand's store is simpler and doesn't require boilerplate code. +- The flow for **fetching**, **creating**, **updating**, and **deleting** posts remains similar, but Zustand provides a more minimalistic and straightforward approach. \ No newline at end of file diff --git a/Writerside/topics/Handling-Asynchronous-Actions.md b/Writerside/topics/Handling-Asynchronous-Actions.md index 6ccf777..bbea04a 100644 --- a/Writerside/topics/Handling-Asynchronous-Actions.md +++ b/Writerside/topics/Handling-Asynchronous-Actions.md @@ -1,4 +1,4 @@ -# Handling Asynchronous Actions +# Redux Thunk - Handling Asynchronous Actions ### **Redux Toolkit (Thunk)** In many applications, we need to perform asynchronous operations such as fetching data from an API, posting data to a server, or dealing with side effects like authentication and file uploads. Redux Toolkit provides a simple and powerful way to handle these scenarios through **thunks**, which are async functions that dispatch actions. diff --git a/Writerside/topics/RTK-Query-for-Data-Fetching.md b/Writerside/topics/RTK-Query-for-Data-Fetching.md index 331679a..c67e694 100644 --- a/Writerside/topics/RTK-Query-for-Data-Fetching.md +++ b/Writerside/topics/RTK-Query-for-Data-Fetching.md @@ -1,4 +1,4 @@ -# RTK Query for Data Fetching +# RTK Query - Data Fetching ### **What is RTK Query?** diff --git a/Writerside/webHelpR2-all.zip b/Writerside/webHelpR2-all.zip index 434ac28..d7d87d7 100644 Binary files a/Writerside/webHelpR2-all.zip and b/Writerside/webHelpR2-all.zip differ