Skip to content

Commit

Permalink
build(3.4.15): del notify redundant excludedInsKeyDict logic
Browse files Browse the repository at this point in the history
  • Loading branch information
fantasticsoul committed Dec 2, 2023
1 parent 6bf8ba3 commit fd5727b
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 7 deletions.
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": "3.4.13",
"version": "3.4.15",
"description": "A state library core that integrates atom, signal, collection dep, derive and watch, it supports all react like frameworks( including react 18 ).",
"keywords": [],
"author": {
Expand Down
2 changes: 1 addition & 1 deletion packages/helux-core/src/consts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createSymbol, HAS_SYMBOL } from '../helpers/sym';
export { EVENT_NAME, FROM, LOADING_MODE } from './user';
export { HAS_SYMBOL };

export const VER = '3.4.14';
export const VER = '3.4.15';

export const PROD_FLAG = true;

Expand Down
7 changes: 2 additions & 5 deletions packages/helux-core/src/factory/creator/notify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,9 @@ export function execDepFns(opts: ICommitStateOptions) {

const insKeys = key2InsKeys[key] || [];
const validInsKeys: number[] = [];
const excludedInsKeyDict: Dict = {};
for (const insKey of insKeys) {
// 已包含或已排除,都跳过当次循环
if (allInsKeys.includes(insKey) || excludedInsKeyDict[insKey]) {
if (allInsKeys.includes(insKey)) {
continue;
}
const insCtx = insCtxMap.get(insKey);
Expand All @@ -69,8 +68,6 @@ export function execDepFns(opts: ICommitStateOptions) {

if (hasChangedNode(internal, depKeys, key)) {
validInsKeys.push(insKey);
} else {
excludedInsKeyDict[insKey] = 1;
}
}

Expand All @@ -85,7 +82,7 @@ export function execDepFns(opts: ICommitStateOptions) {
// 2 子串更新时,还能够查出只读取了父路径的组件并触发更新
// 例如,comp1: a.b.c.d , comp2: a.b
// 更新 draft.a.b.c.d = 1000, 导致 a.b 也变了,按 a.b.c.d 去查组件实例是查不到 comp2 的
// 这里通过 rootValKey 可找到 comp2 并触发其实例更新
// 这里通过 rootValKey 依靠 depKey&insKey 不对称记录机制可找到 comp2 并触发其实例更新
if (!depKeys.includes(rootValKey)) {
analyzeDepKey(rootValKey);
}
Expand Down
154 changes: 154 additions & 0 deletions packages/helux/__tests__/hook-options/_makeArrDepTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { renderHook } from '@testing-library/react';
import { describe, expect, test } from 'vitest';
import { atom, useAtom } from '../helux';
import { dictFictory, getDepKey, noop } from '../util';

interface IOptionsBase {
arrIndexDep?: boolean;
arrDep?: boolean;
}
interface IOptions extends IOptionsBase {
getMatchObj: (rootValKey: string) => any;
}

export function makeTest(options: { label: string; atom: typeof atom; useAtom: typeof useAtom }) {
const { label, atom, useAtom } = options;

async function testNotReadList(options: IOptionsBase) {
const { arrIndexDep, arrDep } = options;
const [dictAtom, setDictAtom, { rootValKey }] = atom(dictFictory);
const { result } = renderHook(() => {
const [state, , info] = useAtom(dictAtom, { arrIndexDep, arrDep });
return info.getDeps();
});

expect(result.current).toMatchObject([rootValKey]);
}

/** arrIndexDep=true by default */
async function testReadList(options: IOptions) {
const { arrIndexDep, arrDep, getMatchObj } = options;
const [dictAtom, setDictAtom, { rootValKey }] = atom(dictFictory);
const { result } = renderHook(() => {
const [state, , info] = useAtom(dictAtom, { arrIndexDep, arrDep });
noop(state.extra.list);
noop(state.extra.list[0]);
noop(state.extra.list[1]);
return info.getDeps();
});

expect(result.current).toMatchObject(getMatchObj(rootValKey));
}

const getArrDepAndIndexDep = (rootValKey) => [
getDepKey(rootValKey, 'extra.list'),
getDepKey(rootValKey, 'extra.list.0'),
getDepKey(rootValKey, 'extra.list.1'),
];

const getArrDep = (rootValKey) => [getDepKey(rootValKey, 'extra.list')];

const getIndexDep = (rootValKey) => [getDepKey(rootValKey, 'extra.list.0'), getDepKey(rootValKey, 'extra.list.1')];

describe(`${label} arrDep=undefined`, () => {
test('arrIndexDep=undefined, not read list', async () => {
await testNotReadList({ arrDep: undefined, arrIndexDep: undefined });
});

test('arrIndexDep=true, not read list', async () => {
await testNotReadList({ arrDep: undefined, arrIndexDep: true });
});

test('arrIndexDep=false, not read list', async () => {
await testNotReadList({ arrDep: undefined, arrIndexDep: false });
});

test('arrIndexDep=undefined, read list', async () => {
await testReadList({
arrDep: undefined,
arrIndexDep: undefined,
getMatchObj: getArrDepAndIndexDep,
});
});

test('arrIndexDep=true, read list', async () => {
await testReadList({
arrDep: undefined,
arrIndexDep: true,
getMatchObj: getArrDepAndIndexDep,
});
});

test('arrIndexDep=false, read list', async () => {
await testReadList({
arrDep: undefined,
arrIndexDep: false,
getMatchObj: getArrDep,
});
});
});

describe(`${label} arrDep=true`, () => {
test('arrIndexDep=undefined, not read list', async () => {
await testNotReadList({ arrDep: true, arrIndexDep: undefined });
});

test('arrIndexDep=true, not read list', async () => {
await testNotReadList({ arrDep: true, arrIndexDep: true });
});

test('arrIndexDep=false, not read list', async () => {
await testNotReadList({ arrDep: true, arrIndexDep: false });
});

test('arrIndexDep=undefined, read list', async () => {
await testReadList({
arrDep: true,
arrIndexDep: undefined,
getMatchObj: getArrDepAndIndexDep,
});
});

test('arrIndexDep=true, read list', async () => {
await testReadList({
arrDep: true,
arrIndexDep: true,
getMatchObj: getArrDepAndIndexDep,
});
});

test('arrIndexDep=true, read list', async () => {
await testReadList({
arrDep: true,
arrIndexDep: false,
getMatchObj: getArrDep,
});
});
});

describe(`${label} arrDep=false`, () => {
test('arrIndexDep=true, read list', async () => {
await testReadList({
arrDep: false,
arrIndexDep: true,
getMatchObj: getIndexDep,
});
});

test('arrIndexDep=false, read list', async () => {
await testReadList({
arrDep: false,
arrIndexDep: false,
getMatchObj: getIndexDep,
});
});

test('arrIndexDep=undefined, read list', async () => {
await testReadList({
arrDep: false,
arrIndexDep: undefined,
getMatchObj: getIndexDep,
});
});
});
}
6 changes: 6 additions & 0 deletions packages/helux/__tests__/hook-options/arrDep.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { atom, share, useAtom, useShared } from '../helux';
import { makeTest } from './_makeArrDepTest';

makeTest({ label: 'useAtom', atom, useAtom });

makeTest({ label: 'useShared', atom: share as any, useAtom: useShared as any });
67 changes: 67 additions & 0 deletions packages/helux/__tests__/hook-options/pure.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { renderHook } from '@testing-library/react';
import { describe, expect, test } from 'vitest';
import { atom, useAtom } from '../helux';
import { dictFictory, getDepKey, noop } from '../util';

describe('useAtom options.pure', () => {
test('pure=undefined', async () => {
const [dictAtom, setDictAtom, { rootValKey }] = atom(dictFictory);

let renderCount = 0;
const { result } = renderHook(() => {
renderCount += 1;
const [state, , info] = useAtom(dictAtom);
return info.getDeps();
});

expect(result.current).toMatchObject([rootValKey]);
expect(renderCount).toBe(1);

setDictAtom((draft) => {
draft.a.b.c = 100;
});
expect(renderCount).toBe(1);
});

test('pure=undefined, read state.a, update a.b.c', async () => {
const [dictAtom, setDictAtom, { rootValKey }] = atom(dictFictory);

let renderCount = 0;
const { result } = renderHook(() => {
renderCount += 1;
const [state, , info] = useAtom(dictAtom);
noop(state.a);
return info.getDeps();
});

expect(result.current).toMatchObject([getDepKey(rootValKey, 'a')]);
expect(renderCount).toBe(1);

setDictAtom((draft) => {
draft.a.b.c = 100;
});
// 更新 a.b.c ,通过 rootValKey 配合不对称记录机制,能查到依赖 a 的示例并触发更新
expect(renderCount).toBe(2);
});

test('pure=undefined, read state.a.b.c, update a', async () => {
const [dictAtom, setDictAtom, { rootValKey }] = atom(dictFictory);

let renderCount = 0;
const { result } = renderHook(() => {
renderCount += 1;
const [state, , info] = useAtom(dictAtom);
noop(state.a.b.c);
return info.getDeps();
});

expect(result.current).toMatchObject([getDepKey(rootValKey, 'a.b.c')]);
expect(renderCount).toBe(1);

setDictAtom((draft) => {
draft.a = { ...draft.a };
});
// 更新 a,但 a.b.c 无变化,故实例不会更新
expect(renderCount).toBe(1);
});
});

0 comments on commit fd5727b

Please sign in to comment.