从0到1写一个简单的react状态管理库
状态管理是react中必备的一项利器,能够自己实现一个,可以加深理解,提升自身实力
下面实现一个简洁的状态管理库
该实现包括一个 createStore 函数用于创建 store 对象,以及 useStore 自定义钩子用于连接 React 组件到 store。
// State类型
type State<T> = T | (() => T);
// Store对象类型
type Store<T> = {
getState: () => T;
setState: (state: State<T>) => void;
subscribe: (listener: () => void) => () => void;
};
// createStore函数,用于创建store对象
function createStore<T>(initialState: T): Store<T> {
let state: T = initialState;
let listeners: Array<() => void> = [];
// 获取当前state
function getState(): T {
return state;
}
// 更新state
function setState(newState: State<T>) {
// 如果新的state是一个函数,那么执行该函数并将执行结果作为新的state
if (typeof newState === 'function') {
state = newState();
} else {
state = newState;
}
// 触发所有的监听器
listeners.forEach((listener) => listener());
}
// 订阅state的变化
function subscribe(listener: () => void): () => void {
listeners.push(listener);
// 返回一个函数,用于取消订阅
return () => {
listeners = listeners.filter((l) => l !== listener);
};
}
return { getState, setState, subscribe };
}
// 自定义钩子,用于连接React组件到store
function useStore<T>(store: Store<T>): T {
const [state, setState] = useState(store.getState());
useEffect(() => {
// 每当store发生变化时,更新组件状态
return store.subscribe(() => {
setState(store.getState());
});
}, [store]);
return state;
}
用法
// 创建 store 对象
const counterStore = createStore({
count: 0,
});
// 定义 actions
const actions = {
increment: () => {
counterStore.setState((prevState) => ({ count: prevState.count + 1 }));
},
decrement: () => {
counterStore.setState((prevState) => ({ count: prevState.count - 1 }));
},
};
// 使用 useStore 钩子连接组件到 store
function Counter() {
const { count } = useStore(counterStore);
return (
<div>
<p>Count: {count}</p>
<button onClick={actions.increment}>Increment</button>
<button onClick={actions.decrement}>Decrement</button>
</div>
);
}
// 渲染组件
ReactDOM.render(<Counter />, document.getElementById('root'));
在这个示例中,我们首先使用 createStore 函数创建了一个名为 counterStore 的 store 对象,并传递了一个初始状态对象。接下来,我们定义了两个名为 increment 和 decrement 的 actions,它们分别用于将 count 属性加一和减一。
最后,我们使用 useStore 钩子将组件连接到 counterStore 对象,并获取当前的 count 状态。我们在组件中渲染了当前的 count 值,并添加了两个按钮,用于调用 increment 和 decrement 方法。当我们点击这些按钮时,组件将更新并显示更新后的 count 值。
以下是一个更复杂的使用示例,展示如何在 Todo 应用程序中使用上述状态管理器实现状态管理。
首先,我们定义一个 todo 的状态类型:
type Todo = {
id: number;
text: string;
completed: boolean;
};
type TodoState = {
todos: Todo[];
};
接下来,我们使用 createStore 函数创建一个名为 todoStore 的 store 对象,并传递初始状态对象 { todos: [] } :
const todoStore = createStore<TodoState>({ todos: [] });
我们可以使用 setState 方法来添加、更新和删除 todo:
const addTodo = (text: string) => {
const newTodo: Todo = {
id: Date.now(),
text,
completed: false,
};
todoStore.setState((prevState) => ({
todos: [...prevState.todos, newTodo],
}));
};
const updateTodo = (id: number, updates: Partial<Todo>) => {
todoStore.setState((prevState) => {
const updatedTodos = prevState.todos.map((todo) =>
todo.id === id ? { ...todo, ...updates } : todo
);
return {
todos: updatedTodos,
};
});
};
const deleteTodo = (id: number) => {
todoStore.setState((prevState) => ({
todos: prevState.todos.filter((todo) => todo.id !== id),
}));
};
我们还可以定义一个名为 useTodos 的自定义钩子,用于将 todoStore 与组件连接起来:
const useTodos = () => {
const todos = useStore(todoStore);
const add = useCallback(addTodo, []);
const update = useCallback(updateTodo, []);
const remove = useCallback(deleteTodo, []);
return { todos, add, update, remove };
};
在组件中,我们可以使用 useTodos 钩子获取当前的 todos 数组以及 add、update 和 remove 方法,以添加、更新和删除 todo
function TodoList() {
const { todos, add, update, remove } = useTodos();
const [newTodoText, setNewTodoText] = useState("");
const handleAddTodo = (e: React.FormEvent) => {
e.preventDefault();
if (newTodoText.trim()) {
add(newTodoText);
setNewTodoText("");
}
};
return (
<div>
<h1>Todos</h1>
<form onSubmit={handleAddTodo}>
<input
type="text"
placeholder="Add a todo"
value={newTodoText}
onChange={(e) => setNewTodoText(e.target.value)}
/>
<button type="submit">Add</button>
</form>
<ul>
{todos.map((todo) => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={(e) => update(todo.id, { completed: e.target.checked })}
/>
<span>{todo.text}</span>
<button onClick={() => remove(todo.id)}>Delete</button>
</li>
))}
</ul>
</div>
);
}
进一步扩展
-
添加中间件支持:可以通过添加中间件来增强状态管理器的功能,例如记录状态变更、实现异步 action、执行副作用等。
-
支持更多的 store 对象:可以扩展状态管理器,以支持多个 store 对象,并允许使用者在不同的组件中选择不同的 store 对象。
-
支持更复杂的状态结构:可以扩展状态管理器,以支持更复杂的状态结构,例如树形结构或嵌套结构。这可能需要引入新的 API,例如获取子状态或递归设置状态。
-
添加状态持久化支持:可以添加状态持久化支持,以允许状态在不同会话或页面之间进行保存和恢复。
-
添加时间旅行支持:可以添加时间旅行支持,以允许用户回溯和前进到不同的状态版本,并实现撤销和重做操作。
-
添加调试支持:可以添加调试支持,以允许开发者查看状态变化并进行调试。例如,可以实现一个开发者工具,用于显示状态树、操作日志和当前状态的快照。
转载自:https://juejin.cn/post/7210003299109535802