今天我将用一个简单的博客程序,来展示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 。”
感谢大家的阅读,如果本文对你有帮助,欢迎点赞+评论+关注
关注我的公号(点击关注),每天更新一篇有用的内容。
|