Skip to content

Commit

Permalink
added zustand and react query
Browse files Browse the repository at this point in the history
  • Loading branch information
kelcho-spense committed Sep 12, 2024
1 parent 1101625 commit ec38405
Show file tree
Hide file tree
Showing 13 changed files with 1,124 additions and 29 deletions.
Binary file added Writerside/images/zustand.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
477 changes: 455 additions & 22 deletions Writerside/pdfSourceR.html

Large diffs are not rendered by default.

Binary file modified Writerside/pdfSourceR.pdf
Binary file not shown.
10 changes: 8 additions & 2 deletions Writerside/r.tree
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,17 @@
<toc-element topic="12-React-Folder-Structure.md"/>
<toc-element topic="12-Advanced-State-Management-with-Redux-Toolkit.md">
<toc-element topic="Setting-Up-Redux-Toolkit.md"/>
<toc-element topic="Handling-Asynchronous-Actions.md"/>
<toc-element topic="End2End-React-ReduxThunk-ExpressMongoDb-Posts-CRUD-APP.md"/>
<toc-element topic="Handling-Asynchronous-Actions.md">
<toc-element topic="1End2End-React-ReduxThunk-ExpressMongoDb-Posts-CRUD-APP.md"/>
</toc-element>
<toc-element topic="RTK-Query-for-Data-Fetching.md">
<toc-element topic="RTK-Query-Vs-Redux-Thunk.md"/>
<toc-element topic="End2End-React-RTK-Query-ExpressMongoDb-Posts-CRUD-APP.md"/>
</toc-element>
</toc-element>
<toc-element topic="Advanced-State-Management-with-Zustand.md">
<toc-element topic="15-1-Counter-Example.md"/>
<toc-element topic="End2End-React-Zustand-ExpressMongoDb-Posts-CRUD-APP.md"/>
<toc-element topic="15-3-End2End-React-Zustand-ReactQuery-ExpressMongoDb-Posts-CRUD-APP.md"/>
</toc-element>
</instance-profile>
8 changes: 8 additions & 0 deletions Writerside/redirection-rules.xml
Original file line number Diff line number Diff line change
Expand Up @@ -263,4 +263,12 @@
</description>
<accepts>11-Best-Practices-and-Common-Pitfalls.html</accepts>
</rule>
<rule id="44c49b77">
<description>Created after removal of "Thunk ExpressMongoDb Posts CRUD APP" from React-TypeScript</description>
<accepts>End2End-React-ReduxThunk-ExpressMongoDb-Posts-CRUD-APP.html</accepts>
</rule>
<rule id="5fe8442d">
<description>Created after removal of "15.2 TodoApp Example" from React-TypeScript</description>
<accepts>TodoApp-Example.html</accepts>
</rule>
</rules>
119 changes: 119 additions & 0 deletions Writerside/topics/15-1-Counter-Example.md
Original file line number Diff line number Diff line change
@@ -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<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`**

```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 (
<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`**

```typescript
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:

```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
Original file line number Diff line number Diff line change
@@ -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<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`**

```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 <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`**

```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 (
<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.
Original file line number Diff line number Diff line change
@@ -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**
Expand Down Expand Up @@ -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';
Expand Down Expand Up @@ -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';
Expand Down
Loading

0 comments on commit ec38405

Please sign in to comment.