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> ); }; exportdefault 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