1. 程式人生 > 其它 >React狀態管理解決方案

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