-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1101625
commit ec38405
Showing
13 changed files
with
1,124 additions
and
29 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Large diffs are not rendered by default.
Oops, something went wrong.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
188 changes: 188 additions & 0 deletions
188
...e/topics/15-3-End2End-React-Zustand-ReactQuery-ExpressMongoDb-Posts-CRUD-APP.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.