2021年我的React状态管理总结

863 阅读5分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

今天我将用一个简单的博客程序,来展示React现在常用三种不同状态管理方案。

  1. 纯React hooks
  2. Redux
  3. 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架构,它主要提供actionsreducers和全局store来让我们管理状态。

Flux架构可以理解为基于事件驱动状态,比如用户点击按钮,或者提交表单,页面首次加载等等。当发生这些动作时会触发action处理函数更新状态,状态变化导致用户界面也会变化,需要更新状态的组件会在全局store进行订阅,一旦reducers对状态进行任何更改,就会立即收到更新。简单理解就是发布订阅的设计模式。

React中的Redux状态管理需要reduxreact-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 。"

感谢大家的阅读,如果本文对你有帮助,欢迎点赞+评论+关注

也欢迎关注我的公号(点击关注),每天更新一篇有用的内容。