在 React 中,父组件向子组件传递数据是通过使用 props(属性)来实现的。props 是父组件传递给子组件的数据。子组件可以通过 this.props 来访问这些数据。
下面是一个简单的示例:
import React from 'react';
import ReactDOM from 'react-dom/client';
const root = ReactDOM.createRoot(document.getElementById('root'));
// 创建子组件
class ChildComponent extends React.Component {
render() {
return (
<div>
<p>I am a child component</p>
<p>My parent says: {this.props.messageFromParent}</p>
</div>
);
}
}
// 创建父组件
class ParentComponent extends React.Component {
render() {
return (
<div>
<p>I am a parent component</p>
{/* 父组件通过props向子组件传递数据 */}
<ChildComponent messageFromParent='Hello, child!' />
</div>
);
}
}
root.render(<ParentComponent/>);
在这个示例中:
ParentComponent
是父组件,ChildComponent
是子组件。<ChildComponent messageFromParent='Hello, child!' />
将 messageFromParent
这个 prop 传递给子组件。这个 prop 的值是 'Hello, child!'
。this.props.messageFromParent
来访问这个 prop。在 React 中,子组件向父组件传递数据通常是通过回调函数实现的。父组件向子组件传递一个函数作为 prop,然后子组件在适当的时候调用这个函数,并传递数据给它。
以下是一个简单的示例:
import React,{useState} from 'react';
import ReactDOM from 'react-dom/client';
const root = ReactDOM.createRoot(document.getElementById('root'));
// 子组件
function ChildComponent(props) {
return (
<div>
<p>I am a child component</p>
<button onClick={() => props.sendMessageToParent('Hello, parent!')}>
Send Message to Parent
</button>
</div>
);
}
// 父组件
function ParentComponent() {
const [messageFromChild, setMessageFromChild] = useState('');
function handleMessageFromChild(message) {
setMessageFromChild(message);
}
return (
<div>
<p>I am a parent component</p>
<ChildComponent sendMessageToParent={handleMessageFromChild} />
<p>Message from child: {messageFromChild}</p>
</div>
);
}
root.render(<ParentComponent/>);
在这个示例中:
ParentComponent
是父组件,ChildComponent
是子组件。handleMessageFromChild
函数,这个函数接收一个消息并将其设置为父组件的状态。然后,父组件将这个函数作为 sendMessageToParent
这个 prop 传递给子组件。sendMessageToParent
这个 prop(即 handleMessageFromChild
函数)会被调用,并传递一个消息给它。在React中,兄弟组件之间不能直接通信。他们必须通过他们的共同父组件进行通信。一个兄弟组件将其数据传递给父组件,然后父组件将数据传递给另一个兄弟组件。
以下是一个简单的示例:
import React,{useState} from 'react';
import ReactDOM from 'react-dom/client';
const root = ReactDOM.createRoot(document.getElementById('root'));
// 创建第一个子组件
function FirstChildComponent(props) {
return (
<div>
<p>I am the first child component</p>
<button onClick={() => props.sendMessageToParent('Hello, sibling!')}>
Send Message to Sibling
</button>
</div>
);
}
// 创建第二个子组件
function SecondChildComponent(props) {
return (
<div>
<p>I am the second child component</p>
<p>Message from sibling: {props.messageFromSibling}</p>
</div>
);
}
// 创建父组件
function ParentComponent() {
const [message, setMessage] = useState('');
function handleMessage(message) {
setMessage(message);
}
return (
<div>
<p>I am the parent component</p>
<FirstChildComponent sendMessageToParent={handleMessage} />
<SecondChildComponent messageFromSibling={message} />
</div>
);
}
root.render(<ParentComponent/>);
在这个示例中:
ParentComponent
是父组件,FirstChildComponent
和 SecondChildComponent
是子组件。sendMessageToParent
这个 prop(即 handleMessage
函数),并传递一个消息给它。messageFromSibling
这个 prop 传递给第二个子组件。messageFromSibling
这个 prop,并将其显示在页面上。React 的 Context API 可以用于在组件树中跨层级传递数据,这非常适合实现多语言功能,因为它可以让你在根组件处设置当前的语言,然后所有的子组件都能访问到这个语言设置,而不用一层层的传递。
以下是一个简单的示例:
import React, { useState,useContext,createContext } from 'react';
import ReactDOM from 'react-dom/client';
const root = ReactDOM.createRoot(document.getElementById('root'));
// 创建一个 Context
const LanguageContext = createContext();
// 创建一个使用 Context 的子组件
function TextComponent() {
const language = useContext(LanguageContext);
const text = language === 'en' ? 'Hello, world!' : '你好,世界!';
return <p>{text}</p>;
}
// 创建一个包含子组件的组件
function App() {
const [language, setLanguage] = useState('en');
function handleChangeLanguage(event) {
setLanguage(event.target.value);
}
return (
<LanguageContext.Provider value={language}>
<select value={language} onChange={handleChangeLanguage}>
<option value="en">English</option>
<option value="zh">中文</option>
</select>
<TextComponent />
</LanguageContext.Provider>
);
}
root.render(<App />);
在这个示例中:
createContext
创建了一个 LanguageContext
。App
组件是一个包含 TextComponent
的组件。它有一个 language
状态和一个 handleChangeLanguage
函数来改变这个状态。App
组件使用 LanguageContext.Provider
组件并传递 language
状态作为 value
prop。这样,language
就可以在 LanguageContext
的所有消费者(即子组件)中使用了。TextComponent
是一个消费 LanguageContext
的组件。它使用 useContext
钩子来获取 language
的值,并根据这个值显示不同的文本。App
组件还包含一个下拉菜单,用于改变 language
的值。在React中,全局变量和事件可以用于实现数据上报。一种常见的方法是在全局对象(例如,window
对象)上挂载临时数据和事件处理函数。
以下是一个简单的示例:
import React, { useEffect } from 'react';
import ReactDOM from 'react-dom/client';
const root = ReactDOM.createRoot(document.getElementById('root'));
window.global = {
data: {},
events: {}
};
function ReportDataComponent() {
function handleButtonClick() {
const dataToReport = {
clicked: true
};
window.global.data = dataToReport;
window.dispatchEvent(new CustomEvent('reportData', {
detail: dataToReport
}));
}
return <button onClick={handleButtonClick}>Click me</button>;
}
function HandleReportComponent() {
useEffect(() => {
window.addEventListener('reportData', handleReportData);
return () => {
window.removeEventListener('reportData', handleReportData);
};
}, []);
function handleReportData(event) {
console.log('Reported data:', event.detail);
}
return <div>Check the console after clicking the button</div>;
}
function App() {
return <div>
<ReportDataComponent />
<HandleReportComponent />
</div>;
}
root.render(<App />);
在这个示例中:
window
对象上创建了一个 global
对象,用于存储临时数据和事件处理函数。App
组件是一个简单的组件,包含一个按钮。App
组件挂载时,我们添加了一个全局事件 reportData
。当这个事件被触发时,我们会调用 handleReportData
函数来处理数据上报的逻辑。window.global.data
中,并触发 reportData
事件。handleReportData
函数会接收到 reportData
事件,并处理数据上报的逻辑。在这个示例中,我们只是将数据打印到控制台。Redux: 英文文档 是一个用于管理应用程序状态的库。它通常与 React 一起使用,但实际上并不依赖于 React。Redux 帮助你管理应用程序的全局状态,使得可以跨组件共享状态。下面是 Redux 的工作原理:
React-Redux: 英文文档
动作 (Actions):这些是表示 UI 中发生了什么事情的普通 JavaScript 对象。它们必须具有一个 type
属性,也可以包含其他附加数据。例如:{ type: 'ADD_TODO', text: 'Buy milk' }
。
Reducer:一个接受当前的 state
和一个 action
,然后返回新的 state
的函数。重要的是,reducer 必须是纯函数;它们不修改输入状态,而是返回一个新的状态。例如:
function todoReducer(state = initialState, action) {
switch (action.type) {
case 'ADD_TODO':
return [...state, { text: action.text, completed: false }];
default:
return state;
}
}
Store:存储是一个保存应用程序状态树的对象。在 Redux 应用程序中应该只有一个单一的存储。该存储具有一些简单的方法,如 dispatch
,getState
和 subscribe
。
dispatch(action)
是最常用的。它用于将动作发送到存储。
getState()
返回应用程序的当前状态。subscribe(listener)
注册一个在状态变化时调用的函数。这是创建存储并分派一个动作的简单示例:
import { createStore } from 'redux';
const store = createStore(todoReducer);
store.dispatch({ type: 'ADD_TODO', text: 'Read a bit of the book' });
React-Redux 是一个提供绑定以将 Redux 与 React 一起使用的库。它提供了 Provider
组件和 connect
函数。
Provider:Provider
组件使得 Redux 存储可供任何包装在 connect
函数中的嵌套组件使用。
import { Provider } from 'react-redux';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
connect:connect
函数将一个 React 组件连接到 Redux 存储。它不会修改传递给它的组件类;相反,它会返回一个新的,已连接的组件类供你使用。connect
函数接受两个参数,都是可选的:
mapStateToProps
:此函数用于从存储中选择连接的组件需要的数据的一部分。每次存储状态更改时都会调用它。它接收整个存储状态,并应返回组件需要的数据的对象。
mapDispatchToProps
:此参数可以是一个函数或一个对象。如果它是一个函数,它将作为 dispatch
传递给你的组件。如果它是一个对象,则假定其中的每个函数都是一个 Redux 动作创建器。一个具有相同函数名称的对象,但绑定到 Redux 存储中,将合并到组件的属性中。
const mapStateToProps = (state) => ({
todos: state.todos,
});
const mapDispatchToProps = {
toggleTodo,
};
export default connect(mapStateToProps, mapDispatchToProps)(TodoList);
将 Redux 和 React-Redux 一起使用,可以以可预测的方式使用动作、reducers 和中央存储来管理应用程序的全局状态并在组件之间共享。这种设置还使得更容易测试和调试你的应用程序。
@reduxjs/toolkit: 英文文档 是 Redux 的官方工具集,它简化了 Redux 的使用,以减轻 Redux 的学习曲线和降低维护成本。这个库提供了许多实用的工具,使你能够更方便、更简洁地写 Redux 代码。
Redux Toolkit 包含以下几个部分:
createSlice: 这是 Redux Toolkit 中最重要的函数之一。它接受一个初始状态、一组 reducer 函数,和一个 slice 名字,然后自动生成 action creators 和 action types。这大大减少了我们需要手动编写的代码量。
createAction: 这个函数用于创建 action creator 函数。一个 action creator 是一个返回具有 type
和 payload
属性的对象的函数。
createReducer: 这个函数允许你指定一个初始状态和一个处理函数的对象,它会返回一个新的 reducer 函数。
configureStore: 这个函数用于设置 store,它将多个 reducer 合并成一个 root reducer,并添加一些中间件,比如 Redux DevTools。
createAsyncThunk: 这是 Redux Toolkit 中一个非常有用的工具,它允许你方便地处理异步逻辑。这个函数接受一个 action type 和一个返回 promise 的函数,并返回一个 thunk action creator。
以下是一个简单的例子,展示了如何使用 @reduxjs/toolkit
:
import { createSlice, configureStore } from '@reduxjs/toolkit'
// 使用 createSlice 创建一个 slice
const counterSlice = createSlice({
name: 'counter',
initialState: 0,
reducers: {
increment: state => state + 1,
decrement: state => state - 1
}
})
// 提取 action creators 和 reducer
const { actions, reducer } = counterSlice
export const { increment, decrement } = actions
// 创建 store
const store = configureStore({
reducer: {
counter: reducer
}
})
export default store;
在上面的例子中,我们创建了一个名为 counter
的 slice,它有两个 action:increment
和 decrement
。createSlice
函数自动为我们生成了对应的 action creators 和 action types。然后我们使用 configureStore
函数创建了 store,并将 counter
reducer 添加到了 store 中。
以下是一个简单的 TodoApp
的示例。这个示例使用了 React, Redux, 和 React-Redux,并使用了 Redux Toolkit 来简化 Redux 的使用。在这个示例中,TodoApp
组件是应用程序的主要组件,它包含 AddTodo
和 TodoList
组件。AddTodo
组件包含一个表单来添加新的待办事项,TodoList
组件包含一个列表的 TodoItem
组件,每个 TodoItem
组件显示一个待办事项的文本和一个复选框来标记待办事项为完成或未完成。
在 store.js
文件中,我们使用 configureStore
函数创建了 Redux 存储,并使用 todoReducer
作为根 reducer。在 todoSlice.js
文件中,我们使用 createSlice
函数创建了一个包含初始状态、reducer 和 action 的 slice。
在 App.js
文件中,我们使用 useSelector
hook 从 Redux 存储中选择 todos
状态,然后传递给 TodoList
组件。我们还使用 useDispatch
hook 创建一个 dispatch 函数,然后将它传递给 handleAddTodo
和 handleToggleTodo
函数,这些函数分别用于添加新的待办事项和切换待办事项的完成状态。
AddTodo
组件包含一个表单,用户可以输入待办事项的文本,然后点击“添加”按钮来添加新的待办事项。当表
单提交时,它会调用 onAdd
函数,并将文本作为参数传递。
TodoList
组件接受一个 todos
数组和一个 onToggle
函数作为 props。它遍历 todos
数组并为每个待办事项创建一个 TodoItem
组件。每个 TodoItem
组件接受一个 todo
对象和一个 onToggle
函数作为 props。
TodoItem
组件显示待办事项的文本,并包含一个复选框,用户可以切换待办事项的完成状态。当复选框的状态改变时,它会调用 onToggle
函数,并将 todo.id
作为参数传递。
src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import ReduxApp from './ReduxApp';
ReactDOM.createRoot(document.getElementById('root')).render(
<ReduxApp />
);
src\redux\store.js
// 从 '@reduxjs/toolkit' 导入 configureStore 方法。
import { configureStore } from '@reduxjs/toolkit';
// 从 './reducers/todoSlice' 导入 todoReducer。
import todoReducer from './todoSlice';
// 使用 configureStore 方法创建 store,并将 todoReducer 作为 todos 的 reducer。
export const store = configureStore({
reducer: {
todos: todoReducer,
},
});
src\redux\todoSlice.js
// 引入 @reduxjs/toolkit 包中的 createSlice 方法
import { createSlice } from '@reduxjs/toolkit';
// 定义初始状态,包含两个待办事项对象
const initialState = [
{ id: '1', text: 'Learn React', completed: false },
{ id: '2', text: 'Learn Redux', completed: false },
];
// 使用 createSlice 方法创建一个名为 'todos' 的 slice
const todosSlice = createSlice({
name: 'todos',
initialState,
reducers: {
// 定义一个 addTodo 的 reducer,接收 action.payload 中的 id 和 text,
// 并向 state 中添加一个新的待办事项
addTodo: (state, action) => {
const { id, text } = action.payload;
state.push({ id, text, completed: false });
},
// 定义一个 toggleTodo 的 reducer,接收 action.payload 中的 id,
// 并更改与 id 匹配的待办事项的 completed 状态
toggleTodo: (state, action) => {
const todo = state.find((todo) => todo.id === action.payload);
if (todo) {
todo.completed = !todo.completed;
}
},
},
});
// 导出 todosSlice.actions 对象,包含 addTodo 和 toggleTodo action creators
export const { addTodo, toggleTodo } = todosSlice.actions;
// 导出 todosSlice.reducer 作为默认导出,以便在配置 store 时使用
export default todosSlice.reducer;
src\ReduxApp.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { addTodo, toggleTodo } from './redux/todoSlice';
function TodoApp() {
const todos = useSelector((state) => state.todos);
const dispatch = useDispatch();
const handleAddTodo = (text) => {
const id = todos.length + 1;
dispatch(addTodo({ id, text }));
};
const handleToggleTodo = (id) => {
dispatch(toggleTodo(id));
};
return (
<div>
<h1>Todo List</h1>
<AddTodo onAdd={handleAddTodo} />
<TodoList todos={todos} onToggle={handleToggleTodo} />
</div>
);
}
function AddTodo({ onAdd }) {
const [text, setText] = React.useState('');
const handleChange = (event) => {
setText(event.target.value);
};
const handleSubmit = (event) => {
event.preventDefault();
onAdd(text);
setText('');
};
return (
<form onSubmit={handleSubmit}>
<input type="text" value={text} onChange={handleChange} />
<button type="submit">添加</button>
</form>
);
}
function TodoList({ todos, onToggle }) {
return (
<ul>
{todos.map((todo) => (
<TodoItem key={todo.id} todo={todo} onToggle={onToggle} />
))}
</ul>
);
}
function TodoItem({ todo, onToggle }) {
return (
<li>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onToggle(todo.id)}
/>
<span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
{todo.text}
</span>
</li>
);
}
export default TodoApp;
MobX: 英文文档 是一个简单、可扩展的状态管理库,它通过反应性编程(reactive programming)原理使状态管理变得非常简单和直观。
下面是 MobX 的一些核心概念:
Observable State(可观察状态): MobX 允许你创建可观察的状态。你可以将对象、数组、类实例甚至是普通变量标记为可观察的。
Observer(观察者): 这是一个可以自动响应状态变化的函数。通常,这是一个 React 组件,但也可以是任何能够响应状态变化的函数。
Actions(动作): 这是会修改状态的方法。在 MobX 中,你只能通过动作来修改状态。
Computed Values(计算值): 这是由当前状态派生的值。当状态发生变化时,所有依赖该状态的计算值都会自动更新。
Reaction(反应): 这是一种特殊的函数,它不会产生新的值,但会产生一些副作用,比如打印到控制台、发起网络请求等。
如何使用MobX
安装 MobX: 你可以使用 npm 或者 yarn 来安装 MobX。
npm install mobx --save
创建可观察的状态:
使用 observable
函数或 @observable
装饰器将你的状态标记为可观察的。
import { observable } from 'mobx';
const todo = observable({
title: 'Learn MobX',
completed: false,
});
创建观察者:
使用 observer
函数或 @observer
装饰器将你的 React 组件标记为观察者。
import { observer } from 'mobx-react-lite';
const TodoView = observer(({ todo }) => <div>{todo.title}</div>);
修改状态:
使用 action
函数或 @action
装饰器将你的方法标记为动作。
import { action } from 'mobx';
const toggleCompleted = action(() => {
todo.completed = !todo.completed;
});
使用计算值:
使用 computed
函数或 @computed
装饰器将你的方法标记为计算值。
import { computed } from 'mobx';
const uppercaseTitle = computed(() => todo.title.toUpperCase());
使用反应:
使用 reaction
或 autorun
函数创建反应。
import { reaction } from 'mobx';
reaction(
() => todo.completed,
completed => console.log(`Todo completed: ${completed}`)
);
MobX 通过反应性系统自动跟踪状态变化,并在需要时更新观察者。这意味着你不需要担心组件何时或如何更新,MobX 会为你处理这些事情。
注意: MobX 6+ 版本推荐使用 makeObservable
、makeAutoObservable
方法来创建 observable、action 和 computed,而不是使用 @observable
、@action
和 @computed
装饰器。这是因为 JavaScript 装饰器仍然是一个实验性的特性。
src\index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import MobxApp from './MobxApp';
ReactDOM.createRoot(document.getElementById('root')).render(
<MobxApp />
);
src\mobx\store.js
// 导入 mobx 的 makeAutoObservable 函数
import { makeAutoObservable } from "mobx"
// 创建 TodoStore 类
class TodoStore {
// 初始化 todos 列表
todos = [
{ id: 1, text: 'Learn React', completed: false },
{ id: 2, text: 'Learn MobX', completed: false }
]
// 构造函数
constructor() {
// 将当前对象转化为 observable 对象
makeAutoObservable(this)
}
// 添加 todo 的方法
addTodo(text) {
this.todos.push({ id: Math.random(), text, completed: false })
}
// 切换 todo 完成状态的方法
toggleTodo(id) {
const todo = this.todos.find(todo => todo.id === id)
if (todo) todo.completed = !todo.completed
}
// 计算已完成 todo 数量的 getter 方法
get completedTodosCount() {
return this.todos.filter(todo => todo.completed).length
}
}
// 创建 TodoStore 实例
const todoStore = new TodoStore()
// 导出 TodoStore 实例
export default todoStore
src\MobxApp.js
import React, { useState } from 'react';
import todoStore from './mobx/store';
import { observer } from 'mobx-react';
const TodoList = observer(() => {
const [newTodo, setNewTodo] = useState('');
const handleNewTodoChange = (e) => {
setNewTodo(e.target.value);
};
const handleAddTodo = () => {
todoStore.addTodo(newTodo);
setNewTodo('');
};
return (
<div>
<h1>Todos</h1>
<input
type="text"
value={newTodo}
onChange={handleNewTodoChange}
/>
<button onClick={handleAddTodo}>
Add Todo
</button>
<ul>
{todoStore.todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => todoStore.toggleTodo(todo.id)}
/>
{todo.text}
</li>
))}
</ul>
<p>Completed Todos: {todoStore.completedTodosCount}</p>
</div>
);
});
export default TodoList;
Recoil: 英文文档 是 Facebook 开发的一种新的状态管理库,它提供了一种灵活、高效的方式来管理 React 应用的状态。Recoil 通过 atoms 和 selectors 来管理和共享状态。
Atoms: Atoms 是 Recoil 的基本单位。它们是可以被多个组件订阅的状态片段。当一个 atom 被更新时,所有订阅了该 atom 的组件将被重新渲染。
Selectors: Selectors 用于根据 atoms 或其他 selectors 计算派生数据。可以将它们视为 pure functions,它们接受 atoms 或其他 selectors 作为输入,并返回一个新的值。
在下面示例中,我们首先导入了我们刚刚创建的 todoListState
atom 和 filteredTodoListState
selector。
然后,我们使用 useRecoilState
和 useRecoilValue
hooks 分别订阅了 todoListState
和 filteredTodoListState
。
addTodo
函数用于向 todo 列表中添加一个新的 todo。toggleTodo
函数用于切换指定索引的 todo 的完成状态。
在组件的 return
语句中,我们显示了一个输入框、一个添加按钮和一个 todo 列表。当用户点击添加按钮时,将调用 addTodo
函数。当用户点击一个 todo 时,将调用 toggleTodo
函数。
import React from 'react';
import ReactDOM from 'react-dom/client';
import RecoilApp from './RecoilApp';
import {RecoilRoot} from 'recoil';
ReactDOM.createRoot(document.getElementById('root')).render(
<RecoilRoot><RecoilApp/></RecoilRoot>
);
src\RecoilApp.js
import React, { useRef } from 'react';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
import { todoListState, filteredTodoListState, filterState } from './recoil';
function App() {
const inputRef = useRef();
const [todoList, setTodoList] = useRecoilState(todoListState);
const setFilter = useSetRecoilState(filterState);
const filteredTodoList = useRecoilValue(filteredTodoListState);
const addTodo = text => {
setTodoList([...todoList, {
text,
isComplete: false
}]);
};
const toggleTodo = index => {
setTodoList(oldTodoList => {
const newTodoList = [...oldTodoList];
newTodoList[index] = {
...newTodoList[index],
isComplete: !newTodoList[index].isComplete
};
return newTodoList;
});
};
const completedTodos = todoList.filter(todo => todo.isComplete).length;
return (
<div>
<input type="text" ref={inputRef} />
<button onClick={() => addTodo(inputRef.current.value)}>Add</button>
<div>
Filter:
<select onChange={e => setFilter(e.target.value)}>
<option value="SHOW_ALL">All</option>
<option value="SHOW_COMPLETED">Completed</option>
<option value="SHOW_UNCOMPLETED">Uncompleted</option>
</select>
</div>
<ul>
{filteredTodoList.map((todo, index) => (
<li key={index}>
<input type="checkbox" checked={todo.isComplete} onChange={() => toggleTodo(index)} />
{todo.isComplete ? <s>{todo.text}</s> : todo.text}
</li>
))}
</ul>
<p>Completed Todos: {completedTodos}</p>
</div>
);
}
export default App;
src\recoil\atoms.js
import { atom } from 'recoil';
export const todoListState = atom({
key: 'todoListState',
default: [], // 这是待办事项的数组
});
export const filterState = atom({
key: 'filterState',
default: 'SHOW_ALL',
});
在这个例子中,我们使用 recoil
的 atom
函数创建了一个名为 todoListState
的 atom。这个 atom 代表了我们的 todo 列表的状态。它的 key
是 'todoListState'
,这是一个唯一的字符串,用于在整个应用程序中标识这个 atom。它的 default
值是一个空数组,表示 todo 列表的初始状态是空的。
src\recoil\selectors.js
import { selector } from 'recoil';
import { todoListState, filterState } from './atoms';
export const filteredTodoListState = selector({
key: 'filteredTodoListState',
get: ({ get }) => {
const todoList = get(todoListState);
const filter = get(filterState);
switch (filter) {
case 'SHOW_COMPLETED':
return todoList.filter(todo => todo.isComplete);
case 'SHOW_UNCOMPLETED':
return todoList.filter(todo => !todo.isComplete);
case 'SHOW_ALL':
default:
return todoList;
}
}
});
在这个例子中,我们首先从 recoil
包中导入 selector
函数,然后从 atoms.js
文件中导入 todoListState
。
接着,我们使用 selector
函数创建了一个新的状态 filteredTodoListState
。这个状态是根据 todoListState
计算得来的。selector
函数接受一个对象,该对象包含 key
和 get
属性。key
是一个字符串,用于标识这个状态。get
是一个函数,它接受一个对象,该对象包含一个 get
函数。我们可以使用这个 get
函数来获取其他的状态。
在这个例子中,我们获取了 todoListState
,然后过滤了这个列表,只保留了没有完成的待办事项。最后,get
函数返回这个过滤后的列表。
这样,每当 todoListState
改变时,filteredTodoListState
也会自动更新。
src\recoil\index.js
export * from './atoms';
export * from './selectors';
Jotai 是一个小型、原子化的状态管理库,用于 React。与其他状态管理解决方案(如 Redux、MobX 或 Recoil)相比,Jotai 试图提供一个简洁的 API,并保持轻量。它基于 React 的新的 hooks API,使用了“atom”这一概念来描述状态。
基本概念
1. Atom
在 Jotai 中,状态被分解成多个独立的、可组合的单元,称为 "atom"。每个原子都有一个唯一的 key 和一个默认值。原子是 Jotai 中的最小状态单位,并可以被任何组件订阅。
例如:
import { atom } from 'jotai';
export const countAtom = atom({
key: 'countState', // 唯一键
default: 0, // 默认值
});
2. useAtom
useAtom
是一个 hook,允许组件订阅 atom 的值。当 atom 的值发生变化时,所有订阅了该 atom 的组件都会重新渲染。
例如:
import { useAtom } from 'jotai';
import { countAtom } from './atoms';
function Counter() {
const [count, setCount] = useAtom(countAtom);
return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount(prev => prev + 1)}>Increment</button>
</div>
);
}
3. Selector
除了 atom,Jotai 还提供了 selector,它们是派生状态,也就是说,它们的值是基于一个或多个 atom 的值计算得出的。
例如,基于上面的 countAtom,我们可以创建一个 selector 来判断 count 是否是偶数:
import { selector } from 'jotai';
import { countAtom } from './atoms';
export const doubleSelector = selector({
key: 'doubleState',
get: ({ get }) => {
const count = get(countAtom);
return count * 2 ;
},
});
Valtio: 英文文档 是一个非常简单和轻量级的状态管理库,它是基于 JavaScript 的 Proxy 对象构建的。Valtio 的核心概念是 "state" 和 "snapshot"。
State: 你可以创建一个普通的 JavaScript 对象作为你的状态对象。这个对象会被 Valtio 转换为可观察的代理对象。
Snapshot: Valtio 会基于当前的状态对象创建一个快照。这个快照是一个只读的、不可变的对象。你可以使用这个快照在你的组件中读取状态,而不用担心引起不必要的渲染。
在 state.js
文件中,我们使用 Valtio 的 proxy
函数创建了一个可观察的状态对象。
在 TodoApp.js
文件中,我们使用 Valtio 的 useSnapshot
Hook 创建了一个状态的快照。然后我们使用这个快照来读取 todos
的状态。
useSnapshot
Hook 会返回一个只读的、不可变的快照对象。这个快照对象会在状态发生变化时自动更新,但是它不会引起组件的重新渲染。只有当组件中实际使用的状态发生变化时,组件才会重新渲染。
我们在 input
标签的 onChange
事件处理器中直接修改了状态对象。这会触发组件的重新渲染。
src\ValtioApp.js
import React, { useState } from 'react'; // 引入 React 和 useState Hook
import { useSnapshot } from 'valtio'; // 引入 valtio 的 useSnapshot Hook
import state from './valtio/state'; // 引入 state
// 定义 App 组件
const App = () => {
const snapshot = useSnapshot(state); // 创建一个 state 的 snapshot
const [newTodo, setNewTodo] = useState(''); // 创建一个叫做 newTodo 的 state
// 定义 handleAddTodo 函数
const handleAddTodo = () => {
if (newTodo) {
state.todos.push({ text: newTodo, completed: false }); // 添加新的 todo 到 todos 列表
setNewTodo(''); // 重置 newTodo
}
};
const completedTodos = snapshot.todos.filter(todo => todo.completed).length; // 计算完成的 todos 的数量
// 返回 JSX
return (
<div>
<h1>Todo List</h1>
<input
type="text"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)} // 当输入变化时更新 newTodo
/>
<button onClick={handleAddTodo}>Add Todo</button>
<ul>
{snapshot.todos.map((todo, index) => (
<li key={index}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => (state.todos[index].completed = !todo.completed)} // 当复选框变化时更新 todo 的 completed 状态
/>
{todo.text}
</li>
))}
</ul>
<p>Completed Todos: {completedTodos}</p>
</div>
);
};
export default App; // 导出 App 组件
src\valtio\state.js
// 引入 valtio 的 proxy 函数
import { proxy } from 'valtio'
// 使用 proxy 创建一个响应式的状态对象
const state = proxy({
todos: [
{ text: 'Learn React', completed: true },
{ text: 'Learn Valtio', completed: false },
],
})
// 导出状态对象
export default state;
Zustand: 英文文档 是一个非常简单且轻量的状态管理库,它避免了 Redux 中的一些复杂性,比如 reducer、action creator 等。它提供了一个可以全局访问的 store 对象,你可以从任何组件中读取状态或者触发更新。
Zustand 的基本使用:
create
函数创建一个 store。import create from 'zustand';
const useStore = create(set => ({
count: 0,
increase: () => set(state => ({ count: state.count + 1 })),
decrease: () => set(state => ({ count: state.count - 1 })),
}));
在上面的例子中,我们创建了一个包含 count
、increase
和 decrease
的 store。
import useStore from './path_to_your_store';
const Component = () => {
const { count, increase, decrease } = useStore();
return (
<div>
<button onClick={decrease}>-</button>
{count}
<button onClick={increase}>+</button>
</div>
);
};
src\ZustandApp.js
import React, { useState } from 'react';
import useStore from './zustand/store';
const App = () => {
const { todos, addTodo, toggleTodo } = useStore();
const [newTodo, setNewTodo] = useState('');
const handleAddTodo = () => {
if (newTodo) {
addTodo(newTodo);
setNewTodo('');
}
};
const completedTodos = todos.filter(todo => todo.completed).length;
return (
<div>
<h1>Todo List</h1>
<input
type="text"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
/>
<button onClick={handleAddTodo}>Add Todo</button>
<ul>
{todos.map((todo, index) => (
<li key={index}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(index)}
/>
{todo.text}
</li>
))}
</ul>
<p>Completed Todos: {completedTodos}</p>
</div>
);
};
export default App;
src\zustand\store.js
import { create } from 'zustand';
const useStore = create(set => ({
todos: [],
addTodo: (text) => set(state => ({
todos: [...state.todos, { text, completed: false }]
})),
toggleTodo: (index) => set(state => {
const todos = [...state.todos];
todos[index].completed = !todos[index].completed;
return { todos };
})
}));
export default useStore;