Kea
Kea
Kea 是用于构建复杂 React 应用的生产级别的状态管理框架。随着应用程序的增长,它可以很好地扩展,并有助于保持状态整洁。
Kea 建立在 Redux 之上,并利用其基本功能原理:
- 应用中的每个操作都以一个操作(递增计数器)开始。
- 这些操作将更新保存实际数据的减速器(计数器)。
- 此数据以全局状态存储,该状态由 Redux 管理。
- 您可以从此状态通过选择器(在状态中找到计数器)获取值(计数器为 1)。
- 动作也可能触发侦听器,这些侦听器是与外部 API 对话,读取值或调度其他动作的纯异步函数。
- 所有相关的动作,缩减器,选择器和侦听器都分组为一个逻辑(counterLogic)。
- React 组件连接到此逻辑并提取所有需要的动作和值。
核心概念
Actions
Kea 将核心业务逻辑封装在 Logic 中:
import { kea } from 'kea'
const logic = kea({ ... })
在 Logic 中我们首先要定义操作的 Actions:
const logic = kea({
actions: () => ({
addToCounter: (amount) => ({ amount }),
setName: (name) => ({ name }),
submitForm: (values, page) => ({ values, page }),
actionWithoutArguments: true,
}),
});
将动作视为调度到队列的事件。他们自己什么也不做,减速器和侦听器(如下所述)等待操作并做出相应反应。
import { useActions } from "kea";
function BigButton() {
const { addToCounter } = useActions(logic);
return (
<button onClick={() => addToCounter(1000)}>Add one thousand! 🤩</button>
);
}
Reducer
Reducers 的定义相较于 Redux 中也简化了很多:
const logic = kea({
actions: () => ({
increment: (amount) => ({ amount }),
decrement: (amount) => ({ amount }),
}),
reducers: () => ({
counter: [
0,
{
increment: (state, { amount }) => state + amount,
decrement: (state, { amount }) => state - amount,
},
],
}),
});
不过需要注意的是,Reducer 中不可以去修改某些值,而应该返回不可变对象:
{
addTodo: (state, { todo }) => [...state, todo], // ❤️❤️❤️ Always do this!
addTodo: (state, { todo }) => state.push(todo), // ☠️☠️☠️ NEVER do this!
}
Listener
在 Kea 中还可以添加 Listener 来进行异步处理:
const logic = kea({
actions: () => ({
loadUsers: true,
setUsers: (users) => ({ users }),
}),
listeners: () => ({
loadUsers: async () => {
const users = await api.get("users");
actions.setUsers(users);
},
}),
reducers: () => ({
users: [
[],
{
setUsers: (_, { users }) => users,
},
],
}),
});
有时候我们在 Listener 中还需要根据当前的 Store 值进行某些操作,这个概念在 Kea 中被称为 Values:
const logic = kea({
// actions, reducers, ...
listeners: ({ actions, values }) => ({
fetchDetails: async () => {
const { username } = values; // 👈 get the latest username
const details = await api.fetchDetails({ username });
actions.setDetails(details);
},
}),
});
Selector
而 Selector 则用于组合获取到 Store 中的值:
const logic = kea({
actions: () => ({
setMonth: (month) => ({ month }),
setRecords: (records) => ({ records }),
}),
reducers: () => ({
month: [
"2020-04",
{
setMonth: (_, { month }) => month,
},
],
records: [
[],
{
setRecords: (_, { records }) => records,
},
],
}),
selectors: ({ selectors }) => ({
recordsForSelectedMonth: [
() => [selectors.month, selectors.records],
(month, records) => {
return records.filter((r) => r.month === month);
},
],
}),
});
const { recordsForSelectedMonth } = useValues(logic);
在 React 中使用
函数式组件
import { kea, useActions } from 'kea'
const logic = kea({ ... })
function MyComponent () {
const { increment } = useActions(logic)
return <button onClick={increment}>Increment</button>
}
import { kea, useValues } from 'kea'
const logic = kea({ ... })
function MyComponent () {
const { counter, doubleCounter } = useValues(logic)
return <div>{counter} * 2 = {doubleCounter}</div>
}
类组件
const logic = kea({
actions: () => ({
doSomething: true,
doSomethingElse: true,
}),
reducers: () => ({
firstOne: ["default", { doSomething: () => "did it" }],
secondOne: ["default", { doSomething: () => "did it" }],
}),
});
class MyComponent extends Component {
render() {
const { firstOne, secondOne } = this.props;
// The following two lines are equivalent as
// `this.actions` is a shorthand for `this.props.actions`
const { doSomething, doSomethingElse } = this.actions;
const { doSomething, doSomethingElse } = this.props.actions;
return <div />;
}
}
const MyConnectedComponent = logic(MyComponent);
import { connect } from "kea";
@connect({
actions: [menuLogic, ["openMenu", "closeMenu"]],
values: [menuLogic, ["isOpen as isMenuOpen"], accountLogic, ["currentUser"]],
})
class MyComponent extends Component {
render() {
const { currentUser } = this.props;
const { closeMenu } = this.actions;
return <button onClick={closeMenu}>{currentUser.name}</button>;
}
}