React状态管理解决方案
今天我将用一个简单的博客程序,来展示React现在常用三种不同状态管理方案。
纯React hooks
Redux
Context
下面来看每种方式
纯React Hooks 方式
当我们的应用还不够大时,使用React Hools的响应式数据处理方式足够我们日常开发应用。
例子
import Posts from "./Components/Posts"; import PostForm from "./Components/PostForm"; import { useState } from "react"; const App = () => { const [PostData, setPostData] = useState([]); const addPost = (formData) => { setPostData([formData, ...PostData]); }; return ( <div className="app-container"> <h1>React hooks 方式</h1> <PostForm addPost={addPost} /> <Posts setPostData={setPostData} PostData={PostData} /> </div> ); }; export default App;
在上面的代码中,PostForm组件和Posts组件都需要传入相同props,如果应用中还有很多相同的兄弟组件,那么我们将每个组件都需要传入props。
如果有嵌套多层的组件结构,那么就需要一层一层的props进行传递,直到最深的子组件。
所以,这时我们就需要一个能够集中管理数据的地方,这时Redux出场了
状态管理 Redux
Redux是最出名的,也是最早的,使用人数较多的状态管理解决方案。
但学习Redux也很头疼,首先它有很多的理念需要知道,然后在项目中配置Redux还需要大量的代码。
Redux基于伟大的Flux架构,它主要提供actions和reducers和全局store来让我们管理状态。
Flux架构可以理解为基于事件驱动状态,比如用户点击按钮,或者提交表单,页面首次加载等等。当发生这些动作时会触发action处理函数更新状态,状态变化导致用户界面也会变化,需要更新状态的组件会在全局store进行订阅,一旦reducers对状态进行任何更改,就会立即收到更新。简单理解就是发布订阅的设计模式。
React中的Redux状态管理需要redux和react-redux两个依赖库。
例子
import { Provider } from "react-redux"; import store from "./store"; import Posts from "./Components/Posts"; import PostForm from "./Components/PostForm"; const App = () => ( <Provider store={store}> <div className="app-container"> <h1>使用Redux管理状态</h1> <PostForm /> <Posts /> </div> </Provider> ); export default App;
这时我们可以看到, PostForm
组件和Posts
组件不再需要传入props
,就可以在组件内使用和修改全局store中的状态。
但是,还有但是,我们需要在组件内多写很多的代码才能实现这一点,主要看组件的最后两行代码。
PostForm组件代码
import { useState } from "react"; import { connect } from "react-redux"; import { object } from "prop-types"; import { createNewPost } from "../actions/postActions"; const initialFormState = { title: "", body: "" }; const PostForm = ({ createNewPost }) => { const [formData, setFormData] = useState(initialFormState); const handleChange = (ev) => { setFormData({ ...formData, [ev.target.name]: ev.target.value, }); }; const handlePostIt = (ev) => { ev.preventDefault(); createNewPost(formData); setFormData(initialFormState); }; return ( <div className="postform-container"> <label htmlFor="title">Title</label> <input type="text" name="title" onChange={handleChange} value={formData.title} /> <br /> <label htmlFor="body">Post</label> <textarea name="body" onChange={handleChange} value={formData.body} /> <br /> <button type="submit" onClick={handlePostIt}> 发布 </button> </div> ); }; const mapStateToProps = (state) => ({}); export default connect(mapStateToProps, { createNewPost })(PostForm);
Posts组件代码
import { useEffect } from "react"; import { func, array, object } from "prop-types"; import { connect } from "react-redux"; import { fetchPosts } from "../actions/postActions"; const Posts = ({ posts, fetchPosts }) => { useEffect(() => { fetch("/posts") .then((res) => res.json()) .then((posts) => { fetchPosts(posts); }); }, []); return ( <div className="posts-container"> <div> <h1>博文列表</h1> </div> {posts.map((post, index) => ( <div key={index}> <div className="post-title">{post.title}</div> <div className="post-body">{post.body}</div> </div> ))} </div> ); }; Posts.propTypes = { posts: array.isRequired, }; const mapStateToProps = (state) => ({ posts: state.posts.items, }); export default connect(mapStateToProps, { fetchPosts })(Posts);
每个组件后面都需要写这些代码。
到这里你可能说,这和React hooks的方式没差多少,那用Redux的痛点在哪呢?
接着看
reducers/index.js
文件
import { combineReducers } from "redux"; import postReducer from "./postReducer"; export default combineReducers({ posts: postReducer, });
actions文件
import { FETCH_POSTS, NEW_POST } from "./types"; export const fetchPosts = (postsData) => { return { type: FETCH_POSTS, payload: postsData, }; }; export const createNewPost = (postData) => { return { type: NEW_POST, payload: postData, }; };
post reducers文件
import { FETCH_POSTS, NEW_POST } from "../actions/types"; const initialState = { items: [], item: { title: "", body: "" }, }; const postReducer = (state = initialState, action) => { if (action.type === FETCH_POSTS) { return { ...state, items: action.payload, }; } else if (action.type === NEW_POST) { return { ...state, items: [action.payload, ...state.items], }; } else return state; }; export default postReducer;
store状态文件
import { createStore } from "redux"; import rootReducer from "./reducers"; const initialState = { posts: { items: [], item: { title: "标题", body: "内容" }, }, }; const store = createStore(rootReducer, initialState); export default store;
types文件
export const FETCH_POSTS = "FETCH_POSTS"; export const NEW_POST = "NEW_POST";
这是使用Redux的必备全套配置,第一次配,简直是麻烦要死。
Context 方式
如果现在有一种方式,具备Redux的状态管理架构,而无需大量的配置代码,也无需外部依赖。
是的,这就是React发布了Context作为内置功能,允许我们创建全局的状态。只要几行就可以配置成功。
使用useReducer hook,可以模拟redux的模式,使用Context可以访问程序中任何位置的全局状态,只需要在根节点使用provider包裹即可。
看下面的代码
App.js
import Posts from "./Components/Posts"; import PostForm from "./Components/PostForm"; import { useEffect, createContext, useReducer } from "react"; const appReducer = (state, action) => { switch (action.type) { case "FETCH_POSTS": return [...state, ...action.payload]; case "NEW_POST": return [action.payload, ...state]; default: return state; } }; export const AppContext = createContext(); const App = () => { const [state, dispatch] = useReducer(appReducer, []); return ( <AppContext.Provider value={[state, dispatch]}> <div className="app-container"> <h1>Context 状态管理</h1> <PostForm /> <Posts /> </div> </AppContext.Provider> ); }; export default App;
Posts.js
import { useEffect, useContext } from "react"; import { AppContext } from "../App"; const Posts = () => { const [state, dispatch] = useContext(AppContext); useEffect(() => { fetch("/posts") .then((res) => res.json()) .then((posts) => { dispatch({ type: "FETCH_POSTS", payload: posts }); }); }, []); return ( <div className="posts-container"> <div> <h1>博文列表</h1> </div> {state.map((post, index) => ( <div key={index}> <div className="post-title">{post.title}</div> <div className="post-body">{post.body}</div> </div> ))} </div> ); }; export default Posts;
PostForm.js
import { useState, useContext } from "react"; import { AppContext } from "../App"; const PostForm = () => { const [state, dispatch] = useContext(AppContext); const [formData, setFormData] = useState({ title: "", body: "", }); const handleChange = (ev) => { setFormData({ ...formData, [ev.target.name]: ev.target.value, }); }; const handlePostIt = (ev) => { ev.preventDefault(); dispatch({ type: "NEW_POST", payload: formData }); setFormData({ title: "", body: "" }); }; return ( <div className="postform-container"> <label htmlFor="title">标题</label> <input type="text" name="title" onChange={handleChange} value={formData.title} /> <br /> <label htmlFor="body">内容</label> <textarea name="body" onChange={handleChange} value={formData.body} /> <br /> <button type="submit" onClick={handlePostIt}> 发布 </button> </div> ); }; export default PostForm;
App.js中的appReducer函数,就相当于替带了Redux中那些烦人的代码。
使用React Hooks和Context就可以用更少的代码实现全局状态管理,而且这些功能已经内置在React中,不再需要三方的依赖。
不过,Redux并不是没有用处,Redux提供的功能也并不只有状态管理,所以需要Redux的其他功能,继续使用Redux就好。
实际上,这三种方式是现在React开发的常用方式,只使用hooks适合简单的小型应用,所以只要在你需要全局状态管理的时候再去选择Redux和Context。
最后
引用Redux 的创造者 Dan Abramov的话
“如果你不知道是否需要 Redux,那就是不需要它。”
“只有遇到 React 实在解决不了的问题,你才需要 Redux 。”
————————————————
版权声明:本文为CSDN博主「小帅的编程笔记」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/cmdfas/article/details/120558205