状态管理库
在 React App 中使用 5 种最流行的库/APIS(使用最现代和最新版本的库)如何在 React App程序中使用全局状态管理,并且达到一样的效果。
- Recoil
- MobX
- XState
- Redux (with hooks)
- Context
入门
先创建一个新的 React App,将使用它来开始实践:
使用npx create-react-app react-state-examples
cd 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 } = Notes
function 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} } = state
return (
<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 来管理全局状态就不会错。