Skip to content

Commit

Permalink
build(4.0.3): see 4.0.3 changelog
Browse files Browse the repository at this point in the history
  • Loading branch information
fantasticsoul committed Jan 6, 2024
1 parent 433f8d2 commit 19b582b
Show file tree
Hide file tree
Showing 22 changed files with 289 additions and 53 deletions.
1 change: 1 addition & 0 deletions docs/docs/api/base/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ nav:
- [runMutateTask](/api/base/run-mutate-task) 人工触发可变派生函数异步任务
- [action](/api/base/action) 创建修改状态的 action 同步或异步函数
- [watch](/api/base/watch) 创建观察数据变化的监听函数
- [watchEffect](/api/base/watch-effect) 创建观察数据变化的监听函数,立即运行并在首次运行时收集到依赖
- [syncer](/api/base/syncer) 浅层次对象的同步函数生成器,辅助双向绑定
- [sync](/api/base/sync) 深层次对象的同步函数生成器,辅助双向绑定
- [emit](/api/base/emit) 发射事件
Expand Down
28 changes: 28 additions & 0 deletions docs/docs/api/base/watch-effect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
group:
title: 帮助
order: 4
order: 0
---

# watchEffect

`watchEffect`用于观察数据变化,并做对应的处理逻辑,观察的粒度可以任意定制。

区别于`watch``watchEffect`回调会立即执行,自动对首次运行时函数内读取到的值完成变化监听。

:::info
本章节展示基础用法,更多用法查阅[指南/观察](/guide/watch)[基础api/观察](/api/base/watch)
:::

## 基础用法

```ts
import { watchEffect, getSnap } from ' helux ';
const [priceState, setPrice] = share({ a: 1 });

// 观察 priceState.a 的变化
watchEffect(() => {
console.log(`found price.a changed from ${getSnap(priceState).a} to ${priceState.a}`);
});
```
8 changes: 6 additions & 2 deletions docs/docs/api/base/watch.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ watch 可观察共享状态跟对象的变化,第二位参数可写为`()=>[]`

## 死循环

设置`immediate`为 true 时,watch 回调首次执行会自动收集依赖,此时如果存在读取自己修改自己的新闻,则会造成死循环
设置`immediate`为 true 时,watch 回调首次执行会自动收集依赖,此时如果存在读取自己修改自己的行为,会造成死循环。

:::tip
死循环产生后,框架会定位到具体的函数位置并告知原因,用户可打开控制台查看
:::

```ts
import { watch, atom } from 'helux';
Expand All @@ -36,7 +40,7 @@ const [state, setAtom] = atom({ a: 1 });

watch(
() => {
//单独 a 修改 a,触发死循环
//读取 a 修改 a,触发死循环
setAtom((draft) => {
draft.a += 1;
});
Expand Down
1 change: 1 addition & 0 deletions docs/docs/api/hooks/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ order: 0
- [useDerived](/api/hooks/use-derived) 使用全量派生结果
- [useOnEvent](/api/hooks/use-on-event) 使用事件监听
- [useWatch](/api/hooks/use-watch) 使用观察
- [useWatchEffect](/api/hooks/use-watch-effect) 使用观察,立即运行并在首次运行时收集到依赖
- [useGlobalId](/api/hooks/use-global-id) 使用`globalId`
- [useMutate](/api/hooks/use-mutate) 使用可变本地状态
- [useService](/api/hooks/use-service) 使用服务
Expand Down
49 changes: 49 additions & 0 deletions docs/docs/api/hooks/use-watch-effect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
order: 4
---

# useWatchEffect

`useWatchEffect` 功能同 `watchEffect``一样,区别在于 `useWatchEffect` 会立即执行回调,自动对首次运行时函数内读取到的值完成变化监听。

:::info
其他使用方式可参考[watchEffect](/api/hooks/use-effect)
:::


## 基础用法

:::info
`useWatchEffect`回调的首次运行的执行时机是在组件挂载完毕后才执行
:::

```tsx
/**
* defaultShowCode: true
*/
import { share, useMutable, useWatchEffect, getSnap } from 'helux';

const [priceState, setState, ctx] = share({ a: 1, b: { b1: { b2: 200 } } });

function changeA() {
setState((draft) => {
draft.a += 1;
});
}

export default function Comp(props: any) {
const [ state, setState] = useMutable({tip:'1'})
useWatchEffect(() => {
setState(draft=>{
draft.tip = `priceState.a changed from ${getSnap(priceState).a} to ${priceState.a}`;
});
});

return (
<div>
<button onClick={changeA}>changeA</button>
<h1>tip: {state.tip}</h1>
</div>
);
}
```
4 changes: 4 additions & 0 deletions docs/docs/api/hooks/use-watch.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ interface IWatchOptions {
type WatchFnDeps = () => any[] | undefined;
```

:::info
`useWatch`回调的首次运行的执行时机是在组件挂载完毕后才执行
:::

## 基础用法

### 观察原始类型 atom
Expand Down
2 changes: 2 additions & 0 deletions docs/docs/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ nav:
> - [runMutateTask](/api/base/run-mutate-task) 人工触发可变派生函数异步任务
> - [action](/api/base/action) 创建修改状态的 action 同步或异步函数
> - [watch](/api/base/watch) 创建观察数据变化的监听函数
> - [watchEffect](/api/base/watch-effect) 创建观察数据变化的监听函数,立即运行并在首次运行时收集到依赖
> - [syncer](/api/base/syncer) 浅层次对象的同步函数生成器,辅助双向绑定
> - [sync](/api/base/sync) 深层次对象的同步函数生成器,辅助双向绑定
> - [emit](/api/base/emit) 发射事件
Expand Down Expand Up @@ -56,6 +57,7 @@ ctx.aciton()(/** action 函数定义 */)
> - [useDerived](/api/hooks/use-derived) 使用全量派生结果
> - [useOnEvent](/api/hooks/use-on-event) 使用事件监听
> - [useWatch](/api/hooks/use-watch) 使用观察
> - [useWatchEffect](/api/hooks/use-watch-effect) 使用观察,立即运行并在首次运行时收集到依赖
> - [useGlobalId](/api/hooks/use-global-id) 使用`globalId`
> - [useMutate](/api/hooks/use-mutate) 使用可变本地状态
> - [useService](/api/hooks/use-service) 使用服务
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/guide/atom.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const { state: numAtom, setState: setAtom } = atomCtx;

钩子 `useAtom` 返回一个元组,使用方式大体对齐 `react.useState` 接口,唯一的区别是`setter`提供的回调参数是一个草稿对象,可基于草稿对象直接修改,这个差异点下面会再次提到。

:::info{title=推荐通过 actions 修改}
:::info{title='通过 actions 修改'}
推荐了解和使用[模块化/defineAtcions](/guide/modular#defineactions)定义修改方法,有利于维护或扩展
:::

Expand Down
55 changes: 55 additions & 0 deletions docs/docs/guide/watch.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ order: 8

## 组件外观察变化

### watch

使用`watch`可观察 atom 对象自身变化或任意多个子节点的变化。

观察函数立即执行,首次执行时收集到相关依赖
Expand Down Expand Up @@ -86,8 +88,24 @@ watch(
);
```

### watchEffect

`watchEffect`回调会立即执行,自动对首次运行时函数内读取到的值完成变化监听

```ts
import { watchEffect, getSnap } from ' helux ';
const [priceState, setPrice] = share({ a: 1 });

// 观察 priceState.a 的变化
watchEffect(() => {
console.log(`found price.a changed from ${getSnap(priceState).a} to ${priceState.a}`);
});
```

## 组件内观察变化

### useWatch

提供`useWatch`让客户在组件内部观察变化

:::success{title=自动销毁观察监听}
Expand Down Expand Up @@ -176,3 +194,40 @@ export default () => (
</div>
);
```

### useWatchEffect

在组件中使用 `useWatchEffect` 来完成状态变化监听,会在组件销毁时自动取消监听。

`useWatchEffect` 功能同 `watchEffect``一样,区别在于 `useWatchEffect` 会立即执行回调,自动对首次运行时函数内读取到的值完成变化监听。

```tsx
/**
* defaultShowCode: true
*/
import { share, useMutable, useWatchEffect, getSnap } from 'helux';

const [priceState, setState, ctx] = share({ a: 1, b: { b1: { b2: 200 } } });

function changeA() {
setState((draft) => {
draft.a += 1;
});
}

export default function Comp(props: any) {
const [ state, setState] = useMutable({tip:'1'})
useWatchEffect(() => {
setState(draft=>{
draft.tip = `priceState.a changed from ${getSnap(priceState).a} to ${priceState.a}`;
});
});

return (
<div>
<button onClick={changeA}>changeA</button>
<h1>tip: {state.tip}</h1>
</div>
);
}
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "helux",
"version": "4.0.2",
"version": "4.0.3",
"description": "A state engine integrates atom, signal, derive, watch and dep tracking, supports fine-grained responsive updates, it is compatible with all react like libs (including React 18).",
"keywords": [],
"author": {
Expand Down
2 changes: 2 additions & 0 deletions packages-legacy/helux-preact/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ export const {
runDeriveTask,
// watch api
watch,
watchEffect,
// hooks api
useAtom,
useAtomX,
useReactive,
useReactiveX,
useDerived,
useWatch,
useWatchEffect,
useGlobalId,
useService,
useOnEvent,
Expand Down
5 changes: 4 additions & 1 deletion packages/helux-core/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { defineDeriveFnItem, defineDeriveTask, derive, deriveDict } from './fact
import { mutate, mutateDict, runMutate, runMutateTask } from './factory/createMutate';
import { atom, atomx, share, sharex } from './factory/createShared';
import { sync, syncer } from './factory/createSync';
import { watch } from './factory/createWatch';
import { watch, watchEffect } from './factory/createWatch';
import { currentDraftRoot } from './factory/creator/current';
import { flush, reactiveDesc } from './factory/creator/reactive';
import { init } from './factory/root';
Expand All @@ -33,6 +33,7 @@ import {
useReactiveX,
useService,
useWatch,
useWatchEffect,
} from './hooks';
import { block, dynamicBlock, signal } from './signal';

Expand Down Expand Up @@ -63,13 +64,15 @@ export {
runDeriveTask,
// watch api
watch,
watchEffect,
// hooks api
useAtom,
useAtomX,
useReactive,
useReactiveX,
useDerived,
useWatch,
useWatchEffect,
useGlobalId,
useService,
useOnEvent,
Expand Down
2 changes: 1 addition & 1 deletion packages/helux-core/src/consts/user.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { VER as limuVer } from 'limu';

export const VER = '4.0.1';
export const VER = '4.0.3';

export const LIMU_VER = limuVer;

Expand Down
28 changes: 21 additions & 7 deletions packages/helux-core/src/factory/createWatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { markFnEnd, markFnStart, registerFn } from '../helpers/fnCtx';
import { delFnDep, recordFnDepKeys } from '../helpers/fnDep';
import { runFn } from '../helpers/fnRunner';
import { getInternal, getSharedKey } from '../helpers/state';
import type { Fn, IFnCtx, IWatchFnParams, ScopeType, SharedState, WatchOptionsType } from '../types/base';
import type { Fn, IFnCtx, IWatchFnParams, ScopeType, SharedState, WatchEffectOptionsType, WatchOptionsType } from '../types/base';
import { INS_CTX } from './creator/current';
import { parseWatchOptions } from './creator/parse';
interface ICreateWatchLogicOpts<T = SharedState> {
Expand Down Expand Up @@ -38,6 +38,15 @@ function putSharedToDep(list: any[]) {
}
}

function innerWatch(forEffect: boolean, watchFn: (fnParams: IWatchFnParams) => void, options?: WatchOptionsType) {
const { deps, immediate } = parseWatchOptions(forEffect, options);
const fnCtx = createWatchLogic(watchFn, { scopeType: SCOPE_TYPE.STATIC, deps, immediate });
return {
run: (throwErr?: boolean) => runFn(fnCtx.fnKey, { throwErr }),
unwatch: () => delFnDep(fnCtx),
};
}

export function createWatchLogic<T = SharedState>(watchFn: (fnParams: IWatchFnParams) => any, options: ICreateWatchLogicOpts<T>) {
const { scopeType, fnCtxBase, immediate, deps = noop, label = 'watch', sharedState, isSimpleWatch } = options;
if (!isFn(watchFn)) {
Expand All @@ -61,11 +70,16 @@ export function createWatchLogic<T = SharedState>(watchFn: (fnParams: IWatchFnPa
return fnCtx;
}

/**
* watch 回调默认不立即执行,需要设置 immediate=true 才立即执行
*/
export function watch(watchFn: (fnParams: IWatchFnParams) => void, options?: WatchOptionsType) {
const { deps, immediate } = parseWatchOptions(options);
const fnCtx = createWatchLogic(watchFn, { scopeType: SCOPE_TYPE.STATIC, deps, immediate });
return {
run: (throwErr?: boolean) => runFn(fnCtx.fnKey, { throwErr }),
unwatch: () => delFnDep(fnCtx),
};
return innerWatch(false, watchFn, options);
}

/**
* watch 回调立即执行
*/
export function watchEffect(watchFn: (fnParams: IWatchFnParams) => void, options?: WatchEffectOptionsType) {
return innerWatch(true, watchFn, options);
}
5 changes: 4 additions & 1 deletion packages/helux-core/src/factory/creator/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ export function parseCreateMutateOpt(descOrOptions?: string | IRunMutateOptions)
return { desc, strict, ...descOrOptions, throwErr };
}

export function parseWatchOptions(options?: WatchOptionsType) {
export function parseWatchOptions(forEffect: boolean, options?: WatchOptionsType) {
let deps: WatchFnDeps = noop;
let immediate: boolean = false;

Expand All @@ -327,6 +327,9 @@ export function parseWatchOptions(options?: WatchOptionsType) {
deps = options.deps || noop;
immediate = options.immediate ?? false;
}
// 如果是为 watchEffect 准备参数,则 immediate 一定为 true
immediate = forEffect ? true : immediate;

return { immediate, deps };
}

Expand Down
Loading

0 comments on commit 19b582b

Please sign in to comment.