English | 简体中文
基于 React Hooks 的轻量级状态管理框架。
🕹 CodeSandbox demos 🕹 | ||||
---|---|---|---|---|
Counter | Todos |
hooks-store
是基于 React Hooks 实现的轻量级状态管理框架,具有以下特征:
- 最小和熟悉的 API: 没有额外的学习成本,只需要了解 React Hooks;
- 中心化: 很方便地进行数据初始化和状态联动;
- 状态只读 API: 支持只读状态而不订阅状态的更新;
- 良好的兼容性: 类组件兼容和良好的 TypeScript 类型检查和推断。
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import { createStore } from '@ice/hooks-store';
// 1️⃣ 自定义 Hooks
function useCounter() {
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
return {
count,
increment,
};
}
const hooks = {
useCounter
};
// 2️⃣ 创建 Store
const store = createStore(hooks);
// 3️⃣ 消费 Hooks
const { useHooks } = store;
function Button() {
const { increment } = useHooks('useCounter');
return (
<button type="button" onClick={increment}> + </button>
);
}
function Count() {
const { count } = useHooks('useCounter');
return (<span>{count}</span>);
}
// 4️⃣ 通过 Provider 绑定 Store 到视图
const { Provider } = store;
function App() {
return (
<Provider>
<Count />
<Button />
</Provider>
);
}
const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);
使用 @ice/hooks-store 需要 React 在 16.8.0 版本以上。
npm install @ice/hooks-store --save
在某些场景下,您可能只希望调用 Hooks 返回的方法更新状态而不订阅 Hooks 状态的更新。
例如「快速开始」示例中的 Button 组件,您没有在组件中消费 Hooks 的状态,因此可能不期望 Hooks 状态的变化触发组件的重新渲染。
这时候您可以使用 getHooks
API,看下面的示例,可以与上面的示例进行比较:
const { getHooks } = store;
function Button() {
function handleIncrement() {
getHooks('useCounter').increment();
}
return (
<button type="button" onClick={handleIncrement}> + </button>
);
}
在某些场景下,您可能期望 A Hooks 的某个状态的变更触发 B Hooks 某个状态的更新。我们把这种行为称为「Hooks 联动」。 例如下面的场景:
- 我们有一个 useTodos,记录了所有的任务列表。
- 我们有一个 useUser,该 hooks 返回一个 todos 字段,记录了当前用户拥有的任务数。
- 每当 useTodos 的任务列表发生了变更,用户持有的任务数就需要保持同步。
import { useEffect, useState } from 'react';
import produce from 'immer';
import '@/store';
function useUser() {
const [state, setState] = useState({ todos: 0 });
const [todos] = store.useHooks('useTodos');
useEffect(() => {
setState(produce((draft) => {
draft.todos = todos.length;
}));
}, [ todos ]);
return [state, setState];
}
import { useState } from 'react';
import produce from 'immer';
import '@/store';
function useTodos() {
const [state, setState] = useState([
{
name: 'angular',
},
]);
function setTodos(todos) {
setState(todos);
const [, setUser] = store.getHooks('useUser');
setUser(produce((draft) => {
draft.todos = todos.length;
}));
}
return [state, { setTodos }];
}
您仍然可以在类组件中获取和订阅 Hooks:
import { Component } from 'react';
import store from '@/store';
import useTodos from '@/hooks/useTodos';
const { withHooks } = store;
interface MaHooksToProp {
useTodos: ReturnType<typeof useTodos>; // 这个字段是 withHooks 自动添加的
}
interface CustomProp {
title: string; // 用户自定义的 props
}
type Props = CustomProp & MaHooksToProp;
class Todos extends Component<Props> {
render() {
const { title, useTodos } = this.props;
const [ state, actions ] = useTodos;
return (
<div>
{
state.map(({ name }, index) => {
return (<div key={index}>
{name}
<button onClick={() => actions.remove(index)}>
删除
</button>
</div>);
})
}
</div>
);
}
}
export default withHooks('useTodos')<MaHooksToProp, Props>(Todos);
✔ | ✔ | ✔ | 9+ ✔ | ✔ | ✔ | ✔ |
欢迎通过 issue 反馈问题。
如果对 @ice/hooks-store
感兴趣,请参考 CONTRIBUTING.md 学习如何贡献代码。
钉钉群 | GitHub issues | Gitter |
---|---|---|
issues | gitter |