状态管理库
在 React App 中使用 5 种最流行的库/APIS(使用最现代和最新版本的库)如何在 React App程序中使用全局状态管理,并且达到一样的效果。
- Recoil
- MobX
- XState
- Redux (with hooks)
- Context
入门
先创建一个新的 React App,将使用它来开始实践:
使用npx create-react-app react-state-examplescd react-state-examples
start命令启动。npm start
Recoil
Recoil Docs
代码行数:30
Recoil 的点是基于 Hooks 的 API。与其他一些库相比,Recoil 的 API 比大多数库更容易。Recoil 实践
开始使用Recoil前,先安装依赖:
接下来,将npm install recoil
RecoilRoot添加到 App 程序的根/入口点:
下一步,要创建一些状态,将使用来自Recoil 的import App from './App'import { RecoilRoot } from 'recoil'export default function Main() {return (<RecoilRoot><App /></RecoilRoot>);}
atom并设置key和一些初始状态:
现在,可以在app的任何位置使用来自 Recoil 的import { atom } from 'recoil'const notesState = atom({key: 'notesState', // unique ID (with respect to other atoms/selectors)default: [], // default value (aka initial state)});
useRecoilState。这是使用 Recoil 实现的笔记 App:import React, { useState } from 'react';import { RecoilRoot, atom, useRecoilState } from 'recoil';const notesState = atom({key: 'notesState', // unique ID (with respect to other atoms/selectors)default: [], // default value (aka initial state)});export default function Main() {return (<RecoilRoot><App /></RecoilRoot>);}function App() {const [notes, setNotes] = useRecoilState(notesState);const [input, setInput] = useState('')function createNote() {const notesArray = [...notes, input]setNotes(notesArray)setInput('')}return (<div><h1>My notes app</h1><button onClick={createNote}>Create Note</button><input value={input} onChange={e => setInput(e.target.value)} />{ notes.map(note => <p key={note}>Note: {note}</p>) }</div>);}
Recoil selectors
来自文档selectors 用于计算基于 state 的派生属性。这能避免冗余 state,通常无需使用 reducers 来保持状态同步和有效。相反,最小状态集存储在 atoms 中。
使用 Recoil selectors,可以根据 state 计算派生属性,例如,可能是已过滤的待办事项数组(在todo app 中)或已发货的订单数组(在电子商务应用程序中):
import { selector, useRecoilValue } from 'recoil'const completedTodosState = selector({key: 'todosState',get: ({get}) => {const todos = get(todosState)return todos.filter(todo => todo.completed)}})const completedTodos = useRecoilValue(completedTodosState)
结论
recoil 文档说:Recoil 是一个用于 React 状态管理的实验性使用工具集。
Mobx
MobX React Lite Docs
代码行数: 30
MobX React 现在有一个轻量级版本(MobX React Lite),这个版本专门针对函数组件而诞生,它的有点是速度更快,更小。
MobX 具有可观察者和观察者的概念,然而可观察的API有所改变,那就是不必指定希望被观察的每个项,而是可以使用 makeAutoObservable 来处理所有事情。
如果希望数据是响应的并且需要修改 store ,则可以用observer来包装组件。
MobX 实践
开始使用Mobx前,先安装依赖:
npm install mobx mobx-react-lite
该应用的状态已在 Store 中创建和管理。
应用的 store 如下所示:
import { makeAutoObservable } from 'mobx'class NoteStore {notes = []createNote(note) {this.notes = [...this.notes, note]}constructor() {/* makes all data in store observable, replaces @observable */makeAutoObservable(this)}}const Notes = new NoteStore()
然后,可以导入notes,并在 app 中的任何位置使用它们。要使组件是可观察修改,需要将其包装在observer中:
import { observer } from 'mobx-react-lite'import { notes } from './NoteStore'const App = observer(() => <h1>{notes[0]|| "No notes"}</h1>)
看看它们如何一起运行的:
import React, { useState } from 'react'import { observer } from "mobx-react-lite"import { makeAutoObservable } from 'mobx'class NoteStore {notes = []createNote(note) {this.notes = [...this.notes, note]}constructor() {makeAutoObservable(this)}}const Notes = new NoteStore()const App = observer(() => {const [input, setInput] = useState('')const { notes } = Notesfunction onCreateNote() {Notes.createNote(input)setInput('')}return (<div><h1>My notes app</h1><button onClick={onCreateNote}>Create Note</button><input value={input} onChange={e => setInput(e.target.value)} />{ notes.map(note => <p key={note}>Note: {note}</p>) }</div>)})export default App
总结
MobX 已经诞生了一段时间,它很好用。与许多其他公司一样,在企业公司的大量线上应用中使用了它。
XState
XState Docs
代码行数:44
XState 试图解决现代UI复杂性的问题,并且依赖于有限状态机的思想和实现。
XState 是由 David Khourshid, 创建的。它的实现方式是与其他库截然不同的。它的复杂性比其他任何一种都要高,但是关于状态如何工作的思维模型确实很 cool 而且对于提高能力很有帮助。
XState 在这里的使用不是特别好,因为它更适合在更复杂的状态下使用。
XState实践
要开始使用XState,请安装这些库:
npm install xstate @xstate/react
要创建machine,请使用xstate中的Machine实用程序。这是将用于 Notes app 的machine:
import { Machine } from 'xstate'const notesMachine = Machine({id: 'notes',initial: 'ready',context: {notes: [],note: ''},states: {ready: {},},on: {"CHANGE": {actions: [assign({note: (_, event) => event.value})]},"CREATE_NOTE": {actions: [assign({note: "",notes: context => [...context.notes, context.note]})]}}})
将使用的数据存储在 context 中。在这里,有一个 notes 列表 和一个 input 输入框。有两种操作,一种用于创建 note(CREATE_NOTE),另一种用于设置 input(CHANGE)。
整个示例:
import React from 'react'import { useService } from '@xstate/react'import { Machine, assign, interpret } from 'xstate'const notesMachine = Machine({id: 'notes',initial: 'ready',context: {notes: [],note: ''},states: {ready: {},},on: {"CHANGE": {actions: [assign({note: (_, event) => event.value})]},"CREATE_NOTE": {actions: [assign({note: "",notes: context => [...context.notes, context.note]})]}}})const service = interpret(notesMachine).start()export default function App() {const [state, send] = useService(service)const { context: { note, notes} } = statereturn (<div><h1>My notes app</h1><button onClick={() => send({ type: 'CREATE_NOTE' })}>Create Note</button><input value={note} onChange={e => send({ type: 'CHANGE', value: e.target.value})} />{ notes.map(note => <p key={note}>Note: {note}</p>) }</div>)}
要在应用中修改状态,使用 xstate-react 中的 useService hooks。
总结
XState 就像劳斯莱斯 或者说 状态管理的瑞士军刀。可以做很多事情,但是所有功能都带来额外的复杂性。
Redux
React Redux docs
代码行数:33
Redux 是整个 React 生态系统中最早,最成功的状态管理库之一。如今它依然很强大。
新的 Redux Hooks API 使 redux 使用起来不再那么麻烦,而且使用起来也更容易。
Redux Toolkit 还改进了 Redux,并大大降低了学习曲线。
Redux 实践
开始使用Redux前,先安装依赖:
npm install @reduxjs-toolkit react-redux
要使用 Redux,需要创建和配置以下内容:
- A store
- Reducers
- A provider
为了帮助解释所有这些工作原理,在实现 Redux 中的 Notes app 的代码中做了注释:
import React, { useState } from 'react'import { Provider, useDispatch, useSelector } from 'react-redux'import { configureStore, createReducer, combineReducers } from '@reduxjs/toolkit'function App() {const [input, setInput] = useState('')/* useSelector 允许你检索你想使用的状态,在我们的例子中是notes数组。 */const notes = useSelector(state => state.notes)/* dispatch 允许我们向 store 发送更新信息 */const dispatch = useDispatch()function onCreateNote() {dispatch({ type: 'CREATE_NOTE', note: input })setInput('')}return (<div><h1>My notes app</h1><button onClick={onCreateNote}>Create Note</button><input value={input} onChange={e => setInput(e.target.value)} />{ notes.map(note => <p key={note}>Note: {note}</p>) }</div>);}/* 在这里,我们创建了一个 reducer,它将在`CREATE_NOTE`动作被触发时更新note数组。 */const notesReducer = createReducer([], {'CREATE_NOTE': (state, action) => [...state, action.note]})/* Here we create the store using the reducers in the app */const reducers = combineReducers({ notes: notesReducer })const store = configureStore({ reducer: reducers })function Main() {return (/* 在这里,我们使用app中的reducer来创建store。 */<Provider store={store}><App /></Provider>)}export default Main
总结
如果正在寻找一个具有庞大社区、大量文档以及大量问答的库,那么Redux是一个非常靠谱的选择。因为它已诞生了很长时间,只要在 Google 搜索,或多或少都能找到一些相关的答案。
在使用异步操作(例如数据获取)时,通常需要添加其他中间件,这会增加它的成本和复杂性。
Redux 起初很难学习。一旦熟悉了框架,就可以很容易地使用和理解它。过去,对于新开发人员而言,有时会感到不知所措,但是随着 Redux Hooks 和 Redux Toolkit 的改进,学习过程变得容易得多,仍然强烈建议 Redux 作为前置的选择。
Context
Context docs
代码行数: 31
context 的优点在于,不需要安装和依赖其他库,它是 React 的一部分。
使用 context 非常简单,当尝试管理大量不同的 context 值时,问题通常会出现在一些大或者复杂的应用程序中,因此通常必须构建自己的抽象来自己管理这些情况。
Context 实践
要创建和使用 context ,请直接从React导入钩子。下面是它的工作原理:
/* 1. Import the context hooks */import React, { useState, createContext, useContext } from 'react';/* 2. Create a piece of context */const NotesContext = createContext();/* 3. Set the context using a provider */<NotesContext.Provider value={{ notes: ['note1', 'note2'] }}><App /></NotesContext.Provider>/* 4. Use the context */const { notes } = useContext(NotesContext);
全部代码
import React, { useState, createContext, useContext } from 'react';const NotesContext = createContext();export default function Main() {const [notes, setNotes] = useState([])function createNote(note) {const notesArray = [...notes, note]setNotes(notesArray)}return (<NotesContext.Provider value={{ notes, createNote }}><App /></NotesContext.Provider>);}function App() {const { notes, createNote } = useContext(NotesContext);const [input, setInput] = useState('')function onCreateNote() {createNote(input)setInput('')}return (<div><h1>My notes app</h1><button onClick={onCreateNote}>Create Note</button><input value={input} onChange={e => setInput(e.target.value)} />{ notes.map(note => <p key={note}>Note: {note}</p>) }</div>);}
总结
context 是一种管理 app 状态的真正可靠且直接的方法。它的API可能不如其他一些库那么好,但是如果了解如何使用它,并且可以在 app 中使用它创建正确的数据抽象,那么选择 context 来管理全局状态就不会错。
