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中文网其他相关文章!