Skip to content

Commit

Permalink
Merge pull request #138 from heluxjs/doc
Browse files Browse the repository at this point in the history
Doc
  • Loading branch information
fantasticsoul authored Jan 16, 2024
2 parents a93f02a + 3513419 commit 8a67cc6
Show file tree
Hide file tree
Showing 30 changed files with 189 additions and 40 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

click helux-core [change log](./packages/helux-core/CHANGELOG.md) to see more details

[released] - 2024-01-16

- fix issue [136](https://github.com/heluxjs/helux/issues/136)

- fix issue [125](https://github.com/heluxjs/helux/issues/125)

- fix issue [137](https://github.com/heluxjs/helux/issues/125) 新增`fnScope.delPathAoa`来记录需要移除的依赖项,让 mutate 回调的 draft 读依赖记录更精确,避免误判为死循环

- 优化`defineFullDerive`类型,自动推导出`params.state`累心

[released] - 2023-11-24

- 接入 `vitest`
Expand Down
13 changes: 7 additions & 6 deletions docs/docs/playground/demos/Playground/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import Console from './Console';
import * as codes from './codes';
import './index.less';

function getCode(name: string, subName: string) {
const codeDict: any = codes;
return codeDict[name]?.[subName] || '';
}

const scope = { helux, React, ...helux };
const subNames: Record<string, string> = {
atom: 'primitive',
Expand All @@ -19,14 +24,10 @@ const cachedSubNames: Record<string, string> = {};
const obj = qs.parse(window.location.search, { ignoreQueryPrefix: true });
const name = obj.n || 'atom';
const subName = obj.s || 'primitive';

function getCode(name: string, subName: string) {
const codeDict: any = codes;
return codeDict[name]?.[subName] || '';
}
const code = getCode(name, subName);

export default () => {
const [info, setInfo] = React.useState({ name, subName, code: getCode('atom', 'primitive'), });
const [info, setInfo] = React.useState({ name, subName, code });
const changeCode = (name: string) => {
const subName = cachedSubNames[name] || subNames[name] || 'primitive';
setInfo({ name, subName, code: getCode(name, subName) });
Expand Down
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"animated-scroll-to": "^2.3.0",
"classnames": "^2.5.0",
"console-feed": "^3.5.0",
"helux": "4.1.6",
"helux": "4.2.0",
"lodash": "^4.17.21",
"lodash.throttle": "^4.1.1",
"prism-react-renderer": "^2.3.1",
Expand Down
13 changes: 13 additions & 0 deletions packages/helux-core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# @helux/core

## 4.2.0

### Minor Changes

- e63fbea: build(4.2.0): see change log 2024-01-16

### Patch Changes

- Updated dependencies [e63fbea]
- @helux/hooks-impl@4.2.0
- @helux/types@4.2.0
- @helux/utils@4.2.0

## 4.1.6

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/helux-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@helux/core",
"version": "4.1.6",
"version": "4.2.0",
"description": "A reactive atomic state engine for React like.",
"bugs": {
"url": "https://github.com/heluxjs/helux/issues"
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.1.6';
export const VER = '4.2.0';

export const LIMU_VER = limuVer;

Expand Down
4 changes: 2 additions & 2 deletions packages/helux-core/src/factory/common/fnScope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ export function getFnCtxByObj<T = Dict>(obj: T) {
}

export function getRunningFn() {
const { runningFnKey, depKeys, runningSharedKey, isIgnore } = getFnScope();
const { runningFnKey, depKeys, runningSharedKey, isIgnore, delPathAoa } = getFnScope();
const fnCtx = !runningFnKey ? null : getFnCtx(runningFnKey);
return { fnCtx, depKeys, isIgnore, runningSharedKey };
return { fnCtx, depKeys, delPathAoa, isIgnore, runningSharedKey };
}

export function hasRunningFn() {
Expand Down
21 changes: 16 additions & 5 deletions packages/helux-core/src/factory/creator/notify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ export function execDepFns(opts: ICommitOpts) {
FN_DEP_KEYS.set(depKeys);
}

const findDirtyFnKeys = (key: string, forSharedKey = false) => {
// 通过根值去查询 fnCtx,内部再根据自己的依赖比较后得出需要执行的函数
const { firstLevelFnKeys, asyncFnKeys } = getDepFnStats(internal, key, runCountStats, forSharedKey);
dirtyFnKeys = dirtyFnKeys.concat(firstLevelFnKeys);
dirtyAsyncFnKeys = dirtyAsyncFnKeys.concat(asyncFnKeys);
};

const analyzeDepKey = (key: string) => {
// 值相等就忽略
if (!diffVal(internal, key)) {
Expand Down Expand Up @@ -72,11 +79,8 @@ export function execDepFns(opts: ICommitOpts) {
validInsKeys.push(insKey);
}
}

dirtyInsKeys = dirtyInsKeys.concat(validInsKeys);
const { firstLevelFnKeys, asyncFnKeys } = getDepFnStats(internal, key, runCountStats);
dirtyFnKeys = dirtyFnKeys.concat(firstLevelFnKeys);
dirtyAsyncFnKeys = dirtyAsyncFnKeys.concat(asyncFnKeys);
findDirtyFnKeys(key);
};
depKeys.forEach((k) => analyzeDepKey(k));
// 分析 rootValKey 结果刻意放 depKeys.forEach 之后执行,是需要复用 sharedScope.isStateChanged 结果,有以下2个作用
Expand All @@ -88,6 +92,9 @@ export function execDepFns(opts: ICommitOpts) {
if (!depKeys.includes(rootValKey)) {
analyzeDepKey(rootValKey);
}
// fix issue 136
findDirtyFnKeys(rootValKey, true);

// clear cached diff result
clearDiff();
// find id's ins keys
Expand All @@ -106,7 +113,11 @@ export function execDepFns(opts: ICommitOpts) {
// start mark async derive fn computing
dirtyAsyncFnKeys.forEach((fnKey) => markComputing(fnKey, runCountStats[fnKey]));
// start execute derive/watch fns
dirtyFnKeys.forEach((fnKey) => runFn(fnKey, { depKeys, sn, from, triggerReasons, internal, desc, isFirstCall, fromFnKey }));
const watchFnKeys: string[] = [];
const runOptions = { depKeys, sn, from, triggerReasons, watchFnKeys, skipWatch: true, internal, desc, isFirstCall, fromFnKey };
dirtyFnKeys.forEach((fnKey) => runFn(fnKey, runOptions));
const runOptionsOfWatch = { depKeys, sn, from, triggerReasons, internal, desc, isFirstCall, fromFnKey };
watchFnKeys.forEach((fnKey) => runFn(fnKey, runOptionsOfWatch));

// start trigger rerender
dirtyInsKeys.forEach((insKey) => updateIns(insCtxMap, insKey, sn));
Expand Down
8 changes: 7 additions & 1 deletion packages/helux-core/src/factory/creator/operateState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function putId(keyIds: KeyIdsDict, options: { writeKey: string; ids: NumStrSymbo
export function handleOperate(opParams: IOperateParams, opts: { internal: TInternal; mutateCtx: IMutateCtx }) {
const { isChanged, fullKeyPath, keyPath, parentType, value } = opParams;
const { internal, mutateCtx } = opts;
const { arrKeyDict, isReactive, readKeys } = mutateCtx;
const { arrKeyDict, isReactive, readKeys, from } = mutateCtx;
const { sharedKey } = internal;
const arrLike = isArrLike(parentType);
const currReactive = REACTIVE_META.current();
Expand Down Expand Up @@ -75,6 +75,12 @@ export function handleOperate(opParams: IOperateParams, opts: { internal: TInter
return;
}

// 来自于 mutate 回调里的 draft 读写
if (MUTATE === from) {
const { delPathAoa, fnCtx } = getRunningFn();
fnCtx && delPathAoa.push(keyPath);
}

// 无任何变化的写操作
if (!isChanged) {
return;
Expand Down
11 changes: 11 additions & 0 deletions packages/helux-core/src/factory/root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,22 @@ function buildFnScope() {
isIgnore: false,
/** 函数运行结束收集到的读依赖 depKeys */
depKeys: [] as string[],
/**
* del path array of array
* 需要移除的 depKeys,解决 mutate 回调里 draft 里深层次读取修改的依赖收集不正确问题
* ```
* // 这里 get 收集到了 a,这个 a 需要移除,否则会造成死循环依赖误判
* draft.a.val = state.someKey + 1;
* ```
*/
delPathAoa: [] as string[][],
/** globalId to Array<insKey> */
GID_INSKEYS_MAP: new Map<NumStrSymbol, number[]>(),
FNKEY_STATIC_CTX_MAP: new Map<string, IFnCtx>(),
FNKEY_HOOK_CTX_MAP: new Map<string, IFnCtx>(),
DEPKEY_FNKEYS_MAP: new Map<string, string[]>(),
/** sharedKeyStr to fnKeys */
SKEY_FNKEYS_MAP: new Map<string, string[]>(),
UNMOUNT_INFO_MAP: new Map<string, IUnmountInfo>(),
/** 记录第一次运行的各个函数,辅助推导出计算状态 */
DEPKEY_COMPUTING_FNKEYS_MAP: new Map<string, string[]>(),
Expand Down
15 changes: 13 additions & 2 deletions packages/helux-core/src/helpers/fnCtx.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { includeOne, matchDictKey, nodupPush } from '@helux/utils';
import { delListItem, includeOne, matchDictKey, nodupPush } from '@helux/utils';
import { newFnCtx } from '../factory/common/ctor';
import { getCtxMap, getFnCtx, getFnKey, markFnKey } from '../factory/common/fnScope';
import { getFnScope } from '../factory/common/speedup';
import { getDepKeyByPath } from '../factory/common/util';
import type { Dict, Fn, IFnCtx, ScopeType } from '../types/base';
import { delFnDep, delHistoryUnmoutFnCtx } from './fnDep';

Expand Down Expand Up @@ -29,7 +30,7 @@ export function markFnEnd() {
const fnCtx = getFnCtx(runningFnKey);
let targetKeys: string[] = [];
if (fnCtx) {
const { depKeys: afterRunDepKeys } = fnScope;
const { depKeys: afterRunDepKeys, delPathAoa, runningSharedKey } = fnScope;
const { depKeys } = fnCtx;

const dict: Dict<number> = {};
Expand All @@ -44,11 +45,21 @@ export function markFnEnd() {
});
const validDepKeys = Object.keys(dict);
validDepKeys.forEach((depKey) => nodupPush(depKeys, depKey));

// 移除认为是冗余的依赖
delPathAoa.forEach((pathArr) => {
const len = pathArr.length;
for (let i = 1; i <= len; i++) {
const toDel = getDepKeyByPath(pathArr.slice(0, i), runningSharedKey);
delListItem(depKeys, toDel);
}
});
targetKeys = depKeys.slice(); // 返回收集到依赖,辅助判断死循环之用
}

fnScope.runningFnKey = '';
fnScope.depKeys = [];
fnScope.delPathAoa = [];
fnScope.runningSharedKey = 0;
return targetKeys;
}
Expand Down
23 changes: 16 additions & 7 deletions packages/helux-core/src/helpers/fnDep.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { nodupPush, safeMapGet } from '@helux/utils';
import { EXPIRE_MS, NOT_MOUNT, PROTO_KEY, SIZE_LIMIT, UNMOUNT } from '../consts';
import { DERIVE, EXPIRE_MS, NOT_MOUNT, PROTO_KEY, SIZE_LIMIT, UNMOUNT } from '../consts';
import { delFnDepData, getFnCtx, getRunningFn, opUpstreamFnKey } from '../factory/common/fnScope';
import { hasChangedNode } from '../factory/common/sharedScope';
import { getFnScope } from '../factory/common/speedup';
Expand All @@ -24,7 +24,7 @@ export function recordFnDepKeys(inputDepKeys: string[], options: { sharedKey?: n
DEPS_CB.current()(inputDepKeys);
return;
}
const { DEPKEY_FNKEYS_MAP } = getFnScope();
const { DEPKEY_FNKEYS_MAP, SKEY_FNKEYS_MAP } = getFnScope();
const { belongCtx, sharedKey } = options;

if (sharedKey) {
Expand Down Expand Up @@ -55,6 +55,9 @@ export function recordFnDepKeys(inputDepKeys: string[], options: { sharedKey?: n

const fnKeys = safeMapGet(DEPKEY_FNKEYS_MAP, depKey, []);
nodupPush(fnKeys, fnKey);
const [sKey] = depKey.split('/');
const fnKeysOfSkey = safeMapGet(SKEY_FNKEYS_MAP, sKey, []);
nodupPush(fnKeysOfSkey, fnKey);
});
}

Expand Down Expand Up @@ -99,9 +102,10 @@ export function getDepSharedStateFeature(fn: IFnCtx) {
/**
* 获得依赖的第一层函数、异步函数
*/
export function getDepFnStats(internal: TInternal, depKey: string, runCountStats: Dict<number>) {
const { DEPKEY_FNKEYS_MAP } = getFnScope();
const fnKeys = DEPKEY_FNKEYS_MAP.get(depKey) || [];
export function getDepFnStats(internal: TInternal, depKey: string, runCountStats: Dict<number>, isSharedKey = false) {
const { DEPKEY_FNKEYS_MAP, SKEY_FNKEYS_MAP } = getFnScope();
const map = isSharedKey ? SKEY_FNKEYS_MAP : DEPKEY_FNKEYS_MAP;
const fnKeys = map.get(depKey) || [];
const firstLevelFnKeys: string[] = [];
const asyncFnKeys: string[] = [];

Expand All @@ -113,11 +117,16 @@ export function getDepFnStats(internal: TInternal, depKey: string, runCountStats
if (fnCtx.isFirstLevel) {
firstLevelFnKeys.push(fnKey);
}
if (fnCtx.isAsync) {
if (fnCtx.isAsync && fnCtx.fnType === DERIVE) {
asyncFnKeys.push(fnKey);
}

const count = runCountStats[fnKey]; // 每个函数将要运行的次数统计
runCountStats[fnKey] = count === undefined ? 1 : count + 1;
if (count === undefined) {
runCountStats[fnKey] = 1;
} else if (!isSharedKey) {
runCountStats[fnKey] = count + 1;
}
}
});

Expand Down
13 changes: 11 additions & 2 deletions packages/helux-core/src/helpers/fnRunner.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { enureReturnArr, isPromise, noopVoid, tryAlert } from '@helux/utils';
import { enureReturnArr, isPromise, nodupPush, noopVoid, tryAlert } from '@helux/utils';
import { ASYNC_TYPE, FROM, WATCH } from '../consts';
import { fakeFnCtx, fakeInternal } from '../factory/common/fake';
import { delComputingFnKey, getFnCtx, getFnCtxByObj, putComputingFnKey } from '../factory/common/fnScope';
Expand All @@ -20,6 +20,8 @@ interface IRunFnOpt {
force?: boolean;
isFirstCall?: boolean;
triggerReasons?: TriggerReason[];
watchFnKeys?: string[];
skipWatch?: boolean;
err?: any;
internal?: TInternal;
desc?: any;
Expand Down Expand Up @@ -131,6 +133,8 @@ export function runFn(fnKey: string, options: IRunFnOpt = {}) {
forceTask = false,
throwErr = false,
triggerReasons = [],
watchFnKeys = [],
skipWatch = false,
sn = 0,
err,
unbox = false,
Expand All @@ -146,6 +150,10 @@ export function runFn(fnKey: string, options: IRunFnOpt = {}) {
return resultTuple(new Error(`not a valid watch or derive cb for key ${fnKey}`));
}
if (fnCtx.fnType === WATCH) {
// watch 需要合并后再外部独立执行
if (skipWatch) {
return nodupPush(watchFnKeys, fnCtx.fnKey);
}
return runWatch(fnCtx, options);
}

Expand Down Expand Up @@ -184,8 +192,9 @@ export function runFn(fnKey: string, options: IRunFnOpt = {}) {
fnCtx.setLoading(false, err);
}
triggerUpdate();
const runOptions = { isFirstCall, sn, triggerReasons, err, watchFnKeys, skipWatch };
fnCtx.nextLevelFnKeys.forEach((key) => {
runFn(key, { isFirstCall, sn, triggerReasons, err });
runFn(key, runOptions);
});
};

Expand Down
2 changes: 1 addition & 1 deletion packages/helux-core/src/hooks/useLocalForceUpdate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { CoreApiCtx } from '../types/api-ctx';
/**
* 强制更新当前组件
*/
export function useLocalForceUpdate<T = any>(apiCtx: CoreApiCtx): () => void {
export function useLocalForceUpdate(apiCtx: CoreApiCtx): () => void {
const updater = apiCtx.hookImpl.useForceUpdate();
return updater;
}
4 changes: 2 additions & 2 deletions packages/helux-core/src/types/api.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
|------------------------------------------------------------------------------------------------
| helux-core@4.1.6
| helux-core@4.2.0
| A state library core that integrates atom, signal, collection dep, derive and watch,
| it supports all react like frameworks ( including react 18 ).
|------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -68,7 +68,7 @@ import type {
} from './base';

export declare const cst: {
VER: '4.1.6';
VER: '4.2.0';
LIMU_VER: string;
EVENT_NAME: {
/** 共享状态创建时的事件 */
Expand Down
7 changes: 5 additions & 2 deletions packages/helux-core/src/types/base.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -768,10 +768,13 @@ type DefineMutateDerive<T extends SharedState = SharedState> = <I = SharedDict>(
type DefineFullDerive<T extends SharedState = SharedState> = <DR extends DepsResultDict | undefined = undefined>(
throwErr?: boolean,
) => <
D extends /**
/**
* 如果透传了 DR 约束返回结果类型和 deps 返回类型,则使用 DR 来约束
* 加上 & Dict 是为了支持用户配置 DR 之外的其他结果,不严格要求所有结果 key 都需要在 DR 里定义类型
*/ DR extends DepsResultDict ? MultiDeriveFn<DR> & Dict<DeriveFn | IDeriveFnItem> : Dict<DeriveFn | IDeriveFnItem>,
*/
D extends DR extends DepsResultDict
? MultiDeriveFn<DR> & Dict<DeriveFn<any, any, T> | IDeriveFnItem<any, any, T>>
: Dict<DeriveFn<any, any, T> | IDeriveFnItem<any, any, T>>,
>(
deriveFnDict: D | ((boundStateInfo: IBoundStateInfo<T>) => D),
) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/helux-demo-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"dependencies": {
"@types/react": ">=16.0.0",
"@types/react-dom": ">=16.0.0",
"helux": "^4.1.6",
"helux": "^4.2.0",
"react": ">=16.10.2",
"react-dom": ">=16.10.2"
},
Expand Down
Loading

0 comments on commit 8a67cc6

Please sign in to comment.