Skip to content

Commit 74d38d3

Browse files
authored
Merge pull request #41 from Houfeng/v8
V8
2 parents 3fadf1e + faed43a commit 74d38d3

18 files changed

+164
-91
lines changed

.eslintrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"rules": {
1919
"@typescript-eslint/no-explicit-any": 0,
2020
"@typescript-eslint/explicit-function-return-type": 0,
21+
"@typescript-eslint/no-non-null-assertion": 0,
2122
"prettier/prettier": "error"
2223
}
2324
}

.npmignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ server.yml
1717
.dawn/
1818
*.js.map
1919
docs/
20-
webpack.config.js
20+
rollup.config.js
21+
rollup.config.dev.js
2122
design/
23+
scripts/
2224
markdowns/
2325
.coveralls.yml
2426
.travis.yml

.vscode/launch.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
// 使用 IntelliSense 了解相关属性。
3+
// 悬停以查看现有属性的描述。
4+
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": "debug/test",
9+
"type": "node",
10+
"request": "launch",
11+
"program": "${workspaceRoot}/node_modules/.bin/mocha",
12+
"args": [
13+
"-r",
14+
"${workspaceRoot}/node_modules/dn-middleware-unit/lib/setups/browser_typescript",
15+
"${relativeFile}"
16+
],
17+
"runtimeArgs": [
18+
"--nolazy"
19+
],
20+
"cwd": "${workspaceRoot}",
21+
"sourceMaps": true,
22+
}
23+
]
24+
}

examples/benchmark-mota.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
import { ObserveConfig, observable, observer } from "../src";
12
import React, { StrictMode } from 'react';
2-
import { observable, observer } from "../src";
33

44
import ReactDOM from 'react-dom';
55
import { list } from './data';
66
import { useEffect } from 'react';
77

8+
ObserveConfig.maxDependencies = Number.MAX_SAFE_INTEGER;
9+
ObserveConfig.maxHandlers = Number.MAX_SAFE_INTEGER;
10+
811
let renderCount = 0;
912

1013
const markRender = () => {
@@ -29,7 +32,7 @@ const test = () => {
2932
const itemStyle = {
3033
padding: 4, margin: 2, background: '#eee', display: 'inline-block',
3134
}
32-
35+
3336
const Item = observer(function Item() {
3437
const { count } = model;
3538
useEffect(() => markRender(), [count]);

examples/data.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export const list = new Array(10000).fill({});
1+
export const list = new Array(5000).fill({});

examples/develop.tsx

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import React, { StrictMode, memo, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';
2-
import { observable, observer, useWatch } from "../src";
1+
import React, { Fragment, memo, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';
2+
import { computed, observable, observer, useObservable, useWatch } from "../src";
33

44
import ReactDOM from 'react-dom';
55

@@ -9,10 +9,31 @@ const model = observable({
99
name: 'test',
1010
num: 1,
1111
add() {
12-
this.num += 1;
12+
this.num += 1;
1313
}
1414
})
1515

16+
@observable
17+
class User {
18+
firstName = "Feng";
19+
lastName = "Hou";
20+
age = 1;
21+
@computed
22+
get fullName() {
23+
return `${this.firstName} ${this.lastName}: ${this.age}`;
24+
}
25+
}
26+
27+
const UserView = observer(function () {
28+
const user = useObservable(() => new User());
29+
return (
30+
<div>
31+
<strong>{user.fullName}</strong>
32+
<div><button onClick={() => user.age++}>click</button></div>
33+
</div>
34+
)
35+
});
36+
1637
export const Demo1 = observer(function Demo1() {
1738
useWatch(() => model.num > 100, () => {
1839
console.log("num:", model.num);
@@ -110,12 +131,13 @@ export const Demo3_1 = memo(observer(function Demo3_1() {
110131

111132
const App = () => {
112133
return (
113-
<StrictMode>
134+
<Fragment>
135+
<UserView />
114136
<Demo1 />
115137
<Demo2 />
116138
<Demo3 />
117139
<Demo4 />
118-
</StrictMode>
140+
</Fragment>
119141
)
120142
}
121143

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mota",
3-
"version": "8.0.2",
3+
"version": "8.1.1",
44
"description": "An extremely lightweight and responsive state management library",
55
"module": "./dist/mota-es.js",
66
"main": "./dist/mota-cjs.js",
@@ -21,7 +21,7 @@
2121
},
2222
"license": "MIT",
2323
"dependencies": {
24-
"ober": "8.0.1",
24+
"ober": "8.2.6",
2525
"tslib": "*"
2626
},
2727
"devDependencies": {

rollup.config.dev.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { ObserveMode } from 'ober';
21
import commonjs from 'rollup-plugin-commonjs';
32
import injectProcessEnv from 'rollup-plugin-inject-process-env';
43
import path from 'path';
@@ -37,7 +36,7 @@ const createConf = (page) => {
3736
sourcemaps(),
3837
injectProcessEnv({
3938
NODE_ENV: 'production',
40-
OBER_CONFIG: { mode: ObserveMode.property },
39+
OBER_CONFIG: { mode: 'property' },
4140
}),
4241
]
4342
};

src/hooks.ts

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,57 @@
44
* @author Houfeng <houzhanfeng@gmail.com>
55
*/
66

7-
import { DependencyList, useLayoutEffect, useMemo } from "react";
8-
import { autorun, observable, watch } from "ober";
7+
import { DependencyList, useCallback, useLayoutEffect, useMemo } from "react";
8+
import { autorun, computable, observable, watch } from "ober";
99

10+
/**
11+
* 在函数组件中即时创建一个可观察对象
12+
* @param value 对象或创建对象的函数
13+
* @param deps 指定的依赖发生变化时,将重新创建
14+
* @returns 可观察对象
15+
*/
16+
export function useObservable<T extends object>(
17+
value: T | (() => T),
18+
deps: DependencyList = []
19+
): T {
20+
return useMemo<T>(() => {
21+
return observable(value instanceof Function ? value() : value);
22+
}, deps);
23+
}
24+
25+
/**
26+
* 创建一个观察器,每当用到的数据发生变化时,将重新计算
27+
* @param selector 计算函数,需返回一个值,将对新旧值进行浅对比,决定是否调用执行函数
28+
* @param handler 执行函数,由 selector 的计算结果决定是否重新执行
29+
* @param immed 是否立即执行一次 handler
30+
*/
1031
export function useWatch(
1132
selector: () => any,
12-
handler: () => void,
33+
handler: (newValue?: any, oldValue?: any) => void,
1334
immed = false
1435
) {
1536
return useLayoutEffect(() => watch(selector, handler, immed), [immed]);
1637
}
1738

39+
/**
40+
* 启动一个自执行函数,当函数中用到的数据发生变化时它将自动重新执行
41+
* @param handler 将执行的函数
42+
*/
1843
export function useAutoRun(handler: () => void) {
1944
return useLayoutEffect(() => autorun(handler), []);
2045
}
2146

22-
export function useObservable<T extends object>(
23-
value: T | (() => T),
24-
deps: DependencyList = []
25-
): T {
26-
return useMemo<T>(() => {
27-
return observable(value instanceof Function ? value() : value);
28-
}, deps);
47+
/**
48+
* 创建一个具备缓存能力的计算结果,
49+
* 计算函数用到的数据发生变化时将重新计算并驱动组件更新,否则将使用缓存
50+
* @param fn 计算函数
51+
* @returns 计算结果
52+
*/
53+
export function useComputed<T>(fn: () => T): T {
54+
const compute = useCallback(computable(fn, { bind: false }), []);
55+
useLayoutEffect(() => {
56+
compute.subscribe!();
57+
return () => compute.unsubscribe!();
58+
});
59+
return compute();
2960
}

src/index.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,24 @@ import { ObserveConfig, nextTick } from "ober";
99
import { ReactDOMUtil } from "./util";
1010
import { name } from "./info";
1111

12-
export { version } from "./info";
13-
export { observer } from "./observer";
14-
export { useWatch, useAutoRun, useObservable } from "./hooks";
15-
1612
export {
1713
observable,
1814
action,
1915
bind,
20-
ObserveInspector,
21-
ObserveConfig,
22-
ObserveMode,
16+
computed,
2317
track,
2418
untrack,
2519
autorun,
2620
watch,
2721
nextTick,
22+
ObserveInspector,
23+
ObserveConfig,
24+
type ObserveMode,
2825
} from "ober";
2926

27+
export { version } from "./info";
28+
export { observer } from "./observer";
29+
export { useObservable, useWatch, useAutoRun, useComputed } from "./hooks";
30+
3031
nextTick.batch = ReactDOMUtil.unstable_batchedUpdates;
3132
ObserveConfig.logPrefix = name.toLocaleUpperCase();

src/info.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
*/
66

77
export const name = "mota";
8-
export const version = "8.0.2";
8+
export const version = "8.1.1";

src/observer.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ import { isSyncRequired } from "./input";
1818
function createReactiver(
1919
render: (...args: any[]) => ReactNode,
2020
requestUpdate: () => void,
21-
lazySubscribe = false
21+
bind = true
2222
) {
23-
const trigger = (info: ObserveData) =>
24-
isSyncRequired(info.value)
23+
const update = (info?: ObserveData) =>
24+
isSyncRequired(info?.value)
2525
? requestUpdate()
26-
: nextTick(requestUpdate, false);
27-
return reactivable(render, trigger, lazySubscribe);
26+
: nextTick(requestUpdate, true);
27+
return reactivable(render, { bind, update, batch: false });
2828
}
2929

3030
function getDisplayName(
@@ -37,7 +37,7 @@ function getDisplayName(
3737
function wrapClassComponent<T extends ComponentClass>(Component: T): T {
3838
const Wrapper = class extends Component {
3939
static displayName = getDisplayName(Component, "Component");
40-
private __reactiver__: ReactiveFunction;
40+
private __reactiver__!: ReactiveFunction;
4141
render(): ReactNode {
4242
if (this.constructor !== Wrapper) return super.render();
4343
if (!this.__reactiver__) {
@@ -49,7 +49,7 @@ function wrapClassComponent<T extends ComponentClass>(Component: T): T {
4949
return this.__reactiver__();
5050
}
5151
componentWillUnmount(): void {
52-
this.__reactiver__.unsubscribe();
52+
this.__reactiver__!.unsubscribe!();
5353
super.componentWillUnmount?.();
5454
}
5555
};
@@ -60,10 +60,10 @@ function wrapFunctionComponent<T extends FunctionComponent>(FC: T): T {
6060
const Wrapper = (...args: any[]) => {
6161
const setState = useState({})[1];
6262
const reactiver = useMemo(() => {
63-
return createReactiver(FC, () => setState({}), true);
63+
return createReactiver(FC, () => setState({}), false);
6464
}, []);
6565
useLayoutEffect(() => {
66-
reactiver.subscribe();
66+
reactiver.subscribe!();
6767
return reactiver.unsubscribe;
6868
}, [reactiver]);
6969
return reactiver(...args);
@@ -72,6 +72,11 @@ function wrapFunctionComponent<T extends FunctionComponent>(FC: T): T {
7272
return Wrapper as T;
7373
}
7474

75+
/**
76+
* 将一个组件转换为可响应组件
77+
* @param target 原类组件或函数组件
78+
* @returns 具有响应能力的组件
79+
*/
7580
export function observer<T extends ComponentType>(target: T) {
7681
if (!target) return target;
7782
const Wrapper = isClassComponent(target)

test/helpers/dom.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const root = document.querySelector('.root');
2+
export const $ = <T extends HTMLElement>(selector: string): T => {
3+
return root?.querySelector<T>(selector) as T;
4+
}

0 commit comments

Comments
 (0)