Redux 是什麼?
Redux 是 JS 應用程式的靈活狀態容器,可單獨管理我們的應用程式狀態。它在單一儲存中管理應用程式狀態,從而更輕鬆地處理整個應用程式中的複雜狀態邏輯。
為什麼要 Redux?
在正常流程中,我們需要進行道具鑽探以在元件之間傳遞狀態。有些關卡不需要這裡的狀態,這是一種負擔。此外,提升大型中型應用程式的狀態並不是可擴展的解決方案,因為它需要進行結構性變更。這就是為什麼我們需要 redux 來管理狀態。這裡的所有狀態都保存在儲存中,無論哪個元件需要,它們都可以訂閱該儲存。 Redux 透過強制執行單向資料流來確保可預測的狀態管理、更輕鬆的偵錯並提高可擴展性。
操作: 描述發生的事情的物件。它通常包含一個類型和一個可選的有效負載。 (命令)
調度: 用於向儲存發送操作以更新狀態的函數。 (事件發生)
Reducer: 一個純函數,它接受目前狀態和一個操作,然後傳回一個新狀態。 (動作調度時觸發的函數)
安裝: npm i @reduxjs/toolkit react-redux
建立切片:
切片是單一功能的 Redux 減速機邏輯和操作的集合。準備回調允許我們在操作有效負載到達減速器之前對其進行自訂。
import { createSlice, nanoid } from "@reduxjs/toolkit"; const postSlice = createSlice({ name: "posts", initialState: [], reducers: { addPost: { reducer: (state, action) => { state.push(action.payload); }, prepare: (title, content) => ({ payload: { id: nanoid(), title, content }, }), }, deletePost: (state, action) => { return state.filter((post) => post.id != action.payload); }, }, }); export const { addPost, deletePost } = postSlice.actions; export default postSlice.reducer;
正在建立商店:
import { configureStore } from "@reduxjs/toolkit"; import postReducer from "../features/posts/postSlice"; export const store = configureStore({ reducer: { posts: postReducer }, });
與提供者一起包裹:
import { Provider } from "react-redux"; import { store } from "./app/store.jsx"; createRoot(document.getElementById("root")).render( <StrictMode> <Provider store={store}> <App /> </Provider> </StrictMode> );
在組件中使用:
const PostList = ({ onEdit }) => { const posts = useSelector((state) => state.posts); const dispatch = useDispatch(); return ( <div className="w-full grid grid-cols-1 gap-6 mt-12"> {posts.map((post) => ( <div key={post.id}></div> ))} </div> ); };
Redux 瀏覽器擴充: Redux DevTools
const store = configureStore({ reducer: rootReducer, devTools: process.env.NODE_ENV !== 'production', });
在 Redux 中,非同步操作(如 API 呼叫)是使用中間件處理的,因為 Redux 預設僅支援同步狀態更新。用於處理非同步操作的最常見中間件是 Redux Thunk、帶有 createAsyncThunk 的 Redux Toolkit (RTK) 和 Redux Saga。
實作:
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; // Fetch all posts export const fetchPosts = createAsyncThunk('posts/fetchPosts', async () => { const response = await fetch('https://jsonplaceholder.typicode.com/posts'); return response.json(); }); // Initial State const initialState = { posts: [], post: null, loading: false, error: null, }; // Slice const postsSlice = createSlice({ name: 'posts', initialState, reducers: {}, extraReducers: (builder) => { builder // Fetch all posts .addCase(fetchPosts.pending, (state) => { state.loading = true; }) .addCase(fetchPosts.fulfilled, (state, action) => { state.loading = false; state.posts = action.payload; }) .addCase(fetchPosts.rejected, (state, action) => { state.loading = false; state.error = action.error.message; }) }, }); export default postsSlice.reducer;
用例:
import React, { useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { fetchPosts, createPost, updatePost, deletePost } from './postsSlice'; const Posts = () => { const dispatch = useDispatch(); const { posts, loading, error } = useSelector((state) =>state.posts); useEffect(() => { dispatch(fetchPosts()); }, [dispatch]); const handleCreate = () => { dispatch(createPost({ title: 'New Post', body: 'This is a new post' })); }; if (loading) return <p>Loading...</p>; if (error) return <p>Error: {error}</p>; return ( <div> <h1>Posts</h1> <button onClick={handleCreate}>Create Post</button> </div> ); }; export default Posts;
中介軟體
Redux 中的中介軟體攔截分派的操作,允許日誌記錄、崩潰報告或處理非同步邏輯。中間件讓我們可以自訂調度過程。
const blogPostMiddleware = (storeAPI) => (next) => (action) => { if (action.type === 'posts/publishPost') { const contentLength = action.payload.content.length; if (contentLength < 50) { console.warn('Post content is too short. Must be at least 50 characters.'); return; } console.log('Publishing post:', action.payload.title); } return next(action); }; const store = configureStore({ reducer: rootReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(blogPostMiddleware), });
選擇器
選擇器有助於訪問該州的特定部分。
export const selectCount = (state) =>狀態.計數器.值;
錯誤處理
透過適當的狀態管理有效地處理錯誤。
import { createSlice, nanoid } from "@reduxjs/toolkit"; const postSlice = createSlice({ name: "posts", initialState: [], reducers: { addPost: { reducer: (state, action) => { state.push(action.payload); }, prepare: (title, content) => ({ payload: { id: nanoid(), title, content }, }), }, deletePost: (state, action) => { return state.filter((post) => post.id != action.payload); }, }, }); export const { addPost, deletePost } = postSlice.actions; export default postSlice.reducer;
RTK 查詢簡化了資料取得、快取和同步。 RTK 查詢自動快取請求並避免不必要的重新獲取,從而提高效能。
設定 RTK 查詢
import { configureStore } from "@reduxjs/toolkit"; import postReducer from "../features/posts/postSlice"; export const store = configureStore({ reducer: { posts: postReducer }, });
組件中的使用
import { Provider } from "react-redux"; import { store } from "./app/store.jsx"; createRoot(document.getElementById("root")).render( <StrictMode> <Provider store={store}> <App /> </Provider> </StrictMode> );
Immer 允許我們編寫直接「改變」狀態的邏輯,同時保持更新在幕後不可變。
const PostList = ({ onEdit }) => { const posts = useSelector((state) => state.posts); const dispatch = useDispatch(); return ( <div className="w-full grid grid-cols-1 gap-6 mt-12"> {posts.map((post) => ( <div key={post.id}></div> ))} </div> ); };
Mutate:直接變更資料。例如,修改物件或陣列。
不可變:我們不是直接修改數據,而是創建一個應用更改的新副本,保持原始數據不變。
Immer 的工作原理
Immer 幫助我們編寫看起來像是在改變資料的程式碼(即直接更改資料),但它會自動使變更在幕後保持不變。這對於在處理 JavaScript 中的不可變資料結構時避免常見錯誤很有用。
範例:沒有 Immer(突變):
const store = configureStore({ reducer: rootReducer, devTools: process.env.NODE_ENV !== 'production', });
使用 Immer(不變性):
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; // Fetch all posts export const fetchPosts = createAsyncThunk('posts/fetchPosts', async () => { const response = await fetch('https://jsonplaceholder.typicode.com/posts'); return response.json(); }); // Initial State const initialState = { posts: [], post: null, loading: false, error: null, }; // Slice const postsSlice = createSlice({ name: 'posts', initialState, reducers: {}, extraReducers: (builder) => { builder // Fetch all posts .addCase(fetchPosts.pending, (state) => { state.loading = true; }) .addCase(fetchPosts.fulfilled, (state, action) => { state.loading = false; state.posts = action.payload; }) .addCase(fetchPosts.rejected, (state, action) => { state.loading = false; state.error = action.error.message; }) }, }); export default postsSlice.reducer;
這使得使用 Redux(或任何狀態管理)變得更容易,因為我們不必手動複製和更新狀態; Immer 會自動為我們做這件事。
為了在頁面刷新時保持 Redux 狀態,我們可以整合 Redux Persist。這會將您的 Redux 狀態儲存在本機儲存或會話儲存中,並在刷新應用程式時重新載入它。
安裝:
npm install redux-persist
實作:
import React, { useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { fetchPosts, createPost, updatePost, deletePost } from './postsSlice'; const Posts = () => { const dispatch = useDispatch(); const { posts, loading, error } = useSelector((state) =>state.posts); useEffect(() => { dispatch(fetchPosts()); }, [dispatch]); const handleCreate = () => { dispatch(createPost({ title: 'New Post', body: 'This is a new post' })); }; if (loading) return <p>Loading...</p>; if (error) return <p>Error: {error}</p>; return ( <div> <h1>Posts</h1> <button onClick={handleCreate}>Create Post</button> </div> ); }; export default Posts;
用 Persisit Gate 包裹:
const blogPostMiddleware = (storeAPI) => (next) => (action) => { if (action.type === 'posts/publishPost') { const contentLength = action.payload.content.length; if (contentLength < 50) { console.warn('Post content is too short. Must be at least 50 characters.'); return; } console.log('Publishing post:', action.payload.title); } return next(action); }; const store = configureStore({ reducer: rootReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(blogPostMiddleware), });
使用 sessionStorage 而不是 localStorage:
將儲存變更為基於會話(瀏覽器關閉時清除):
initialState: { items: [], status: 'idle', error: null, }, .addCase(fetchData.rejected, (state, action) => { state.status = 'failed'; state.error = action.error.message; });
選擇性堅持:
只保留狀態的特定部分:
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; const api = createApi({ reducerPath: 'api', baseQuery: fetchBaseQuery({ baseUrl: 'https://jsonplaceholder.typicode.com' }), endpoints: (builder) => ({ getPosts: builder.query({ query: () => '/posts', }), getPostById: builder.query({ query: (id) => `/posts/${id}`, }), createPost: builder.mutation({ query: (newPost) => ({ url: '/posts', method: 'POST', body: newPost, }), }), updatePost: builder.mutation({ query: ({ id, ...updatedPost }) => ({ url: `/posts/${id}`, method: 'PUT', body: updatedPost, }), }), deletePost: builder.mutation({ query: (id) => ({ url: `/posts/${id}`, method: 'DELETE', }), }), }), }); export const { useGetPostsQuery, useGetPostByIdQuery, useCreatePostMutation, useUpdatePostMutation, useDeletePostMutation, } = api; export default api;
我建立了一個簡單的部落格項目,其中包含react、redux和具有CRUD功能的ant design。你可以去看看。
專案連結 - Redux 部落格應用程式
?掌握 Redux Toolkit 並提升您的 React 應用程式!
以上是面向 React 開發人員的綜合 Redux 工具包說明的詳細內容。更多資訊請關注PHP中文網其他相關文章!