likes
comments
collection
share

从0到1写一个简单的react状态管理库

作者站长头像
站长
· 阅读数 19

状态管理是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>
  );
}

进一步扩展

  1. 添加中间件支持:可以通过添加中间件来增强状态管理器的功能,例如记录状态变更、实现异步 action、执行副作用等。

  2. 支持更多的 store 对象:可以扩展状态管理器,以支持多个 store 对象,并允许使用者在不同的组件中选择不同的 store 对象。

  3. 支持更复杂的状态结构:可以扩展状态管理器,以支持更复杂的状态结构,例如树形结构或嵌套结构。这可能需要引入新的 API,例如获取子状态或递归设置状态。

  4. 添加状态持久化支持:可以添加状态持久化支持,以允许状态在不同会话或页面之间进行保存和恢复。

  5. 添加时间旅行支持:可以添加时间旅行支持,以允许用户回溯和前进到不同的状态版本,并实现撤销和重做操作。

  6. 添加调试支持:可以添加调试支持,以允许开发者查看状态变化并进行调试。例如,可以实现一个开发者工具,用于显示状态树、操作日志和当前状态的快照。