Skip to content

Commit 0088d86

Browse files
authored
Merge pull request #28 from filefoxper/4.2.0
## v4.2.0 2022-03-19
2 parents 5416c39 + 3775375 commit 0088d86

File tree

16 files changed

+583
-24
lines changed

16 files changed

+583
-24
lines changed

docs/api.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,4 +134,24 @@ export declare function useWeakSharing<
134134

135135
returns a sharingRef.
136136

137-
It is the hook way for using the `agent-reducer` API [weakSharing](https://filefoxper.github.io/agent-reducer/#/api?id=weaksharing) in component.
137+
It is the hook way for using the `agent-reducer` API [weakSharing](https://filefoxper.github.io/agent-reducer/#/api?id=weaksharing) in component.
138+
139+
## useAgentEffect
140+
141+
creates a effect to listen the state changes of model or agent.
142+
143+
```typescript
144+
export declare function useAgentEffect<S, T extends Model<S>=Model<S>>(
145+
callback:EffectCallback<S>,
146+
target:T,
147+
...methods:(((...args:any[])=>any)|string)[]
148+
):void;
149+
```
150+
151+
* callback - a function which accepts params as `prevState`, `currentState`, `methodName`. It is triggered when the state of `target` changes.
152+
* target - model or agent, which you want to listen the state change from.
153+
* methods - optional, if you don't need it, the effect `callback` will triggerd immediately when using `useAgentEffect`, if you add them, only the state changes caused by these methods can trigger the effect `callback`.
154+
155+
returns void.
156+
157+
Go to [tutorial](/tutorial?id=use-agent-effect) to see, how to use it.

docs/changes.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,8 @@
8080

8181
## v4.1.7 2022-02-14
8282

83-
* [bug] resolve the problem about typescript error to `useModel` API, when the param is a class with constructor params.
83+
* [bug] resolve the problem about typescript error to `useModel` API, when the param is a class with constructor params.
84+
85+
## v4.2.0 2022-03-19
86+
87+
* [update] create API [useAgentEffect](/api?id=useagenteffect).

docs/tutorial.md

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -856,4 +856,133 @@ export default function NewFeatures() {
856856

857857
`Agents` base on a same model object can update state synchronously. Using this feature of `agent-reducer` can make your code more simple. And use API [useAgentSelector](/api?id=useagentselector) and [useAgentMethods](/api?id=useagentmethods) can make your component render more less with `agent-reducer` model sharing.
858858

859-
You can do more things to make a search page model better.
859+
You can do more things to make a search page model better.
860+
861+
## Use agent effect
862+
863+
If you need effect API of `agent-reducer` directly, you can refer to [document](https://filefoxper.github.io/agent-reducer/#/guides?id=effect), if you are more interest to [effect decorator](https://filefoxper.github.io/agent-reducer/#/guides?id=effect-decorator) you can take look at the guids of agent-reducer. But, here we just introduce API [useAgentEffect](/api?id=useagenteffect) .
864+
865+
how to add a agent effect to model in react?
866+
867+
```typescript
868+
useAgentEffect((prevState, currentState, methodName)=>{
869+
// `prevState` is the model state before this change.
870+
// `currentState` is the model state right now.
871+
// `methodName` is the name of model or agent method
872+
// which leads this change.
873+
// If this effect is caused by effect mount,
874+
// param `methodName` is `null`.
875+
......
876+
// return function destroy() {
877+
// ......
878+
// }
879+
// if returns a function,
880+
// this function will be called before effect callback triggered again.
881+
// It is often used for clean or destroy something.
882+
},model, ...methods);
883+
```
884+
885+
We can use this API to try `SearchParamsModel.feedback`.
886+
887+
```typescript
888+
import React, {memo, useCallback, useEffect} from 'react';
889+
import {
890+
useAgentReducer,
891+
useMiddleWare,
892+
useAgentMethods,
893+
useAgentSelector,
894+
useModelProvider,
895+
useModel, useAgentEffect
896+
} from "./ar";
897+
import SimpleTodoList, {SearchParamsModel} from "./model";
898+
import {ContentInput, PageContent, PriorLevelSelect, SearchContent} from "@/components";
899+
import {Button, Pagination, Table} from "antd";
900+
import Column from "antd/lib/table/Column";
901+
import {PriorLevel, SearchParams, State} from "@/type";
902+
903+
const SearchParamComponent = memo(() => {
904+
905+
const {state, changeSearchContent, changeSearchPriorLevel, feedback} = useAgentReducer(SearchParamsModel);
906+
907+
const todoListModel = useModel(SimpleTodoList);
908+
909+
const {search} = useAgentMethods(todoListModel);
910+
911+
// In fact, only the `todoListModel.changePage` action
912+
// can lead the `feedback` happen.
913+
// So, we can use `useAgentEffect` to listen
914+
// the `todoListModel.changePage` method,
915+
// and call `feedback`.
916+
useAgentEffect<State>((prevState, currentState) => {
917+
feedback(currentState.searchParams);
918+
}, todoListModel, todoListModel.changePage);
919+
920+
const handleSubmit = useCallback(async () => {
921+
search(state);
922+
}, [state]);
923+
924+
return (
925+
<SearchContent>
926+
<label>content: </label>
927+
<ContentInput value={state.content} onChange={changeSearchContent}/>
928+
<label>prior level: </label>
929+
<PriorLevelSelect value={state.priorLevel} onChange={changeSearchPriorLevel}/>
930+
<Button type="primary" onClick={handleSubmit}>submit</Button>
931+
</SearchContent>
932+
);
933+
});
934+
935+
const Search = memo(()=>{
936+
const SearchProvider = useModelProvider(new SearchParamsModel());
937+
return (
938+
<SearchProvider>
939+
<SearchParamComponent/>
940+
</SearchProvider>
941+
);
942+
})
943+
944+
export default function Effect() {
945+
946+
const todoListModel = new SimpleTodoList();
947+
948+
const agent = useAgentReducer(todoListModel);
949+
950+
const TodoListProvider = useModelProvider(todoListModel);
951+
952+
const {
953+
state,
954+
search,
955+
changePage
956+
} = agent;
957+
958+
useEffect(() => {
959+
search();
960+
}, []);
961+
962+
const renderPriorLevel = useCallback((value: PriorLevel) => {
963+
return value === PriorLevel.NORMAL ? 'normal' : 'emergency';
964+
}, []);
965+
966+
return (
967+
<PageContent>
968+
<TodoListProvider>
969+
<Search/>
970+
<Table dataSource={state.dataSource || []} pagination={false} rowKey="id">
971+
<Column title="id" dataIndex="id" width={'20%'}/>
972+
<Column title="content" dataIndex="content" width={'60%'}/>
973+
<Column title="prior level" dataIndex="priorLevel" render={renderPriorLevel} width={'20%'}/>
974+
</Table>
975+
<Pagination
976+
current={state.currentPage}
977+
total={state.total}
978+
pageSize={state.pageSize}
979+
onChange={changePage}
980+
/>
981+
</TodoListProvider>
982+
</PageContent>
983+
);
984+
985+
}
986+
```
987+
988+
useAgentEffect API 是为了解决一些 useEffect 难以解决问题而创造的,例如该接口提供了 `prevState`, `currentState` 对比机制,再如,useAgentEffect 可监听特定方法产生的 state 变化并做出反应。请悬着合适的时机使用该 API,切勿滥用。

docs/zh/api.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,22 @@ export declare function useWeakSharing<
133133

134134
当前 API 是 `agent-reducer` API [weakSharing](https://filefoxper.github.io/agent-reducer/#/api?id=weaksharing) 的一个 hook 应用。
135135

136-
136+
## useAgentEffect
137+
138+
要求 agent-reducer >=4.2.3 ,创建一个模型副作用监听目标模型或代理 state 变化。
139+
140+
```typescript
141+
export declare function useAgentEffect<S, T extends Model<S>=Model<S>>(
142+
callback:EffectCallback<S>,
143+
target:T,
144+
...methods:(((...args:any[])=>any)|string)[]
145+
):void;
146+
```
147+
148+
* callback - 副作用回调函数,可接收 `prevState`, `state`, `methodName` 三个参数:改变前 state,改变后 state,引起改变的方法。
149+
* target - 被监听的模型或代理对象。
150+
* methods - 可选,指定需要监听的方法,不加方法过滤,在第一次使用 useAgentEffect 时,副作用回调函数会被立即触发一次。如添加该参数,当且仅当由指定方法引起 state 变更时才会触发副作用回调函数
151+
152+
该 API 无返回值。
153+
154+
移步至[教程](/zh/tutorial?id=使用副作用),看看如何使用该 API。

docs/zh/changes.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,8 @@
8080

8181
## v4.1.7 2022-02-14
8282

83-
* [bug] 修复关于 `useModel` API 中使用 Model class 时,class 传参错误问题。
83+
* [bug] 修复关于 `useModel` API 中使用 Model class 时,class 传参错误问题。
84+
85+
## v4.2.0 2022-03-19
86+
87+
* [update] 新增 API [useAgentEffect](/zh/api?id=useagenteffect)

docs/zh/tutorial.md

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -814,4 +814,131 @@ export default function NewFeatures() {
814814
}
815815
```
816816

817-
利用好`模型共享`特性可以让我们的代码更简单更清晰。
817+
利用好`模型共享`特性可以让我们的代码更简单更清晰。
818+
819+
## 使用副作用
820+
821+
如果希望直接使用 `agent-reducer` 的副作用 API,可参考[文档](https://filefoxper.github.io/agent-reducer/#/zh/guides?id=effect),其中在 class 模型中通过 [effect decorator](https://filefoxper.github.io/agent-reducer/#/zh/guides?id=副作用-decorator-装饰器用法) 添加副作用,非常方便,但并不常用。这里着重介绍使用 [useAgentEffect](/zh/api?id=useagenteffect) 添加副作用的方案,虽然不及 react useEffect API,但在一些特殊场景中,依然有着不小的自身优势。
822+
823+
添加一个副作用:
824+
825+
```typescript
826+
useAgentEffect((prevState, currentState, methodName)=>{
827+
// `prevState` 当前 state 变更之前的模型 state
828+
// `currentState` 当前模型 state
829+
// `methodName` 引起本次变化的方法名,
830+
// 在直接监听模型 state 变更时,当前 callback 函数
831+
// 会在模型空闲时立即执行一次,这时因为并没有方法引起 state 变更,
832+
// 所以 `methodName` 为 null
833+
......
834+
// return function destroy(){
835+
// ......
836+
// }
837+
// 如果返回 function ,该 function 会在回调再次被调用,
838+
// 或副作用被卸载时调用。
839+
// 可用于销毁副作用回调过程中产生的影响
840+
},model, ...methods);
841+
```
842+
843+
我们可以使用这个新 API 来尝试重置 SearchParamsModelstate
844+
845+
```typescript
846+
import React, {memo, useCallback, useEffect} from 'react';
847+
import {
848+
useAgentReducer,
849+
useMiddleWare,
850+
useAgentMethods,
851+
useAgentSelector,
852+
useModelProvider,
853+
useModel, useAgentEffect
854+
} from "./ar";
855+
import SimpleTodoList, {SearchParamsModel} from "./model";
856+
import {ContentInput, PageContent, PriorLevelSelect, SearchContent} from "@/components";
857+
import {Button, Pagination, Table} from "antd";
858+
import Column from "antd/lib/table/Column";
859+
import {PriorLevel, SearchParams, State} from "@/type";
860+
861+
const SearchParamComponent = memo(() => {
862+
863+
const {state, changeSearchContent, changeSearchPriorLevel, feedback} = useAgentReducer(SearchParamsModel);
864+
865+
const todoListModel = useModel(SimpleTodoList);
866+
867+
const {search} = useAgentMethods(todoListModel);
868+
869+
// 目前需要引起 search 部分 state 重置的条件就是翻页查询,
870+
// 所以这里,我们只要监听是否进行翻页查询即可,
871+
// 我们可以使用 useAgentEffect 并监听 todoListModel.changePage 方法
872+
useAgentEffect<State>((prevState, currentState) => {
873+
feedback(currentState.searchParams);
874+
}, todoListModel, todoListModel.changePage);
875+
876+
const handleSubmit = useCallback(async () => {
877+
search(state);
878+
}, [state]);
879+
880+
return (
881+
<SearchContent>
882+
<label>content: </label>
883+
<ContentInput value={state.content} onChange={changeSearchContent}/>
884+
<label>prior level: </label>
885+
<PriorLevelSelect value={state.priorLevel} onChange={changeSearchPriorLevel}/>
886+
<Button type="primary" onClick={handleSubmit}>submit</Button>
887+
</SearchContent>
888+
);
889+
});
890+
891+
const Search = memo(()=>{
892+
const SearchProvider = useModelProvider(new SearchParamsModel());
893+
return (
894+
<SearchProvider>
895+
<SearchParamComponent/>
896+
</SearchProvider>
897+
);
898+
})
899+
900+
export default function Effect() {
901+
902+
const todoListModel = new SimpleTodoList();
903+
904+
const agent = useAgentReducer(todoListModel);
905+
906+
const TodoListProvider = useModelProvider(todoListModel);
907+
908+
const {
909+
state,
910+
search,
911+
changePage
912+
} = agent;
913+
914+
useEffect(() => {
915+
search();
916+
}, []);
917+
918+
const renderPriorLevel = useCallback((value: PriorLevel) => {
919+
return value === PriorLevel.NORMAL ? 'normal' : 'emergency';
920+
}, []);
921+
922+
return (
923+
<PageContent>
924+
<TodoListProvider>
925+
<Search/>
926+
<Table dataSource={state.dataSource || []} pagination={false} rowKey="id">
927+
<Column title="id" dataIndex="id" width={'20%'}/>
928+
<Column title="content" dataIndex="content" width={'60%'}/>
929+
<Column title="prior level" dataIndex="priorLevel" render={renderPriorLevel} width={'20%'}/>
930+
</Table>
931+
<Pagination
932+
current={state.currentPage}
933+
total={state.total}
934+
pageSize={state.pageSize}
935+
onChange={changePage}
936+
/>
937+
</TodoListProvider>
938+
</PageContent>
939+
);
940+
941+
}
942+
```
943+
944+
useAgentEffect API 是为了解决一些 useEffect 难以解决问题而创造的,例如该接口提供了 `prevState`, `currentState` 对比机制,再如,useAgentEffect 可监听特定方法产生的 state 变化并做出反应。请悬着合适的时机使用该 API,切勿滥用。

example/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@
1313
"@types/classnames": "^2.2.7",
1414
"@types/react": "16.14.21",
1515
"@types/react-dom": "16.9.14",
16-
"agent-reducer": "4.0.4",
16+
"agent-reducer": "4.2.3",
1717
"antd": "^4.16.13",
1818
"babel-polyfill": "^6.26.0",
1919
"classnames": "^2.2.6",
2020
"moment": "^2.24.0",
2121
"react": "^16.8.6",
2222
"react-dom": "^16.8.6",
23-
"use-agent-reducer": "4.1.5"
23+
"use-agent-reducer": "4.2.0"
2424
},
2525
"devDependencies": {
2626
"@babel/core": "7.15.8",

example/src/Layout.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import SplitModel from "@/splitModel";
55
import TakeLatest from "@/takeLatest";
66
import NewFeatures from "@/newFeatures";
77
import ModelProvider from "@/modelProvider";
8+
import Effect from '@/effect';
89

910
const Title=({children})=>{
1011
return (
@@ -29,6 +30,8 @@ export default memo(() => {
2930
<NewFeatures/>
3031
<Title>model provider</Title>
3132
<ModelProvider/>
33+
<Title>effect provider</Title>
34+
<Effect/>
3235
</div>
3336
</div>
3437
);

0 commit comments

Comments
 (0)