refactor:优化函数组件渲染发生setState不触发整个应用重新更新,改为组件本身马上重运行
This commit is contained in:
parent
a33ba0ebf3
commit
85e20114ca
|
@ -14,6 +14,7 @@
|
|||
*/
|
||||
|
||||
import * as Inula from '../../../src/index';
|
||||
const { useState } = Inula;
|
||||
describe('FunctionComponent Test', () => {
|
||||
it('渲染无状态组件', () => {
|
||||
const App = props => {
|
||||
|
@ -89,4 +90,39 @@ describe('FunctionComponent Test', () => {
|
|||
Inula.render(<App />, container);
|
||||
expect(container.querySelector('div').style['_values']['--max-segment-num']).toBe(10);
|
||||
});
|
||||
|
||||
it('函数组件渲染中重新发生setState不触发整个应用重新更新', () => {
|
||||
function CountLabel({ count }) {
|
||||
const [prevCount, setPrevCount] = useState(count);
|
||||
const [trend, setTrend] = useState(null);
|
||||
if (prevCount !== count) {
|
||||
setPrevCount(count);
|
||||
setTrend(count > prevCount ? 'increasing' : 'decreasing');
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<h1>{count}</h1>
|
||||
<Child trend={trend} />
|
||||
{trend && <p>The count is {trend}</p>}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
function Child({ trend }) {
|
||||
count++;
|
||||
return <div>{trend}</div>;
|
||||
}
|
||||
|
||||
let update;
|
||||
function App() {
|
||||
const [count, setCount] = useState(0);
|
||||
update = setCount;
|
||||
return <CountLabel count={count} />;
|
||||
}
|
||||
|
||||
Inula.render(<App />, container);
|
||||
update(1);
|
||||
expect(count).toBe(2);
|
||||
})
|
||||
});
|
||||
|
|
|
@ -18,12 +18,19 @@ import type { VNode } from '../Types';
|
|||
import { getLastTimeHook, setLastTimeHook, setCurrentHook, getNextHook } from './BaseHook';
|
||||
import { HookStage, setHookStage } from './HookStage';
|
||||
|
||||
const NESTED_UPDATE_LIMIT = 50;
|
||||
// state updated in render phrase
|
||||
let hasUpdatedInRender = false;
|
||||
function resetGlobalVariable() {
|
||||
setHookStage(null);
|
||||
setLastTimeHook(null);
|
||||
setCurrentHook(null);
|
||||
}
|
||||
|
||||
export function markUpdatedInRender() {
|
||||
hasUpdatedInRender = true;
|
||||
}
|
||||
|
||||
// hook对外入口
|
||||
export function runFunctionWithHooks<Props extends Record<string, any>, Arg>(
|
||||
funcComp: (props: Props, arg: Arg) => any,
|
||||
|
@ -45,8 +52,14 @@ export function runFunctionWithHooks<Props extends Record<string, any>, Arg>(
|
|||
setHookStage(HookStage.Update);
|
||||
}
|
||||
|
||||
const comp = funcComp(props, arg);
|
||||
let comp = funcComp(props, arg);
|
||||
|
||||
if (hasUpdatedInRender) {
|
||||
resetGlobalVariable();
|
||||
processing.oldHooks = processing.hooks;
|
||||
setHookStage(HookStage.Update);
|
||||
comp = runFunctionAgain(funcComp, props, arg);
|
||||
}
|
||||
// 设置hook阶段为null,用于判断hook是否在函数组件中调用
|
||||
setHookStage(null);
|
||||
|
||||
|
@ -63,3 +76,22 @@ export function runFunctionWithHooks<Props extends Record<string, any>, Arg>(
|
|||
|
||||
return comp;
|
||||
}
|
||||
|
||||
function runFunctionAgain<Props extends Record<string, any>, Arg>(
|
||||
funcComp: (props: Props, arg: Arg) => any,
|
||||
props: Props,
|
||||
arg: Arg
|
||||
) {
|
||||
let reRenderTimes = 0;
|
||||
let childElements;
|
||||
while (hasUpdatedInRender) {
|
||||
reRenderTimes++;
|
||||
if (reRenderTimes > NESTED_UPDATE_LIMIT) {
|
||||
throw new Error('Too many setState called in function component');
|
||||
}
|
||||
hasUpdatedInRender = false;
|
||||
childElements = funcComp(props, arg);
|
||||
}
|
||||
|
||||
return childElements;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import { setStateChange } from '../render/FunctionComponent';
|
|||
import { getHookStage, HookStage } from './HookStage';
|
||||
import type { VNode } from '../Types';
|
||||
import { getProcessingVNode } from '../GlobalVar';
|
||||
import { markUpdatedInRender } from "./HookMain";
|
||||
|
||||
// 构造新的Update数组
|
||||
function insertUpdate<S, A>(action: A, hook: Hook<S, A>): Update<S, A> {
|
||||
|
@ -64,8 +65,13 @@ export function TriggerAction<S, A>(vNode: VNode, hook: Hook<S, A>, isUseState:
|
|||
}
|
||||
}
|
||||
|
||||
// 执行vNode节点渲染
|
||||
launchUpdateFromVNode(vNode);
|
||||
if (vNode === getProcessingVNode()) {
|
||||
// 绑定的VNode就是当前渲染的VNode时,就是在函数组件体内触发setState
|
||||
markUpdatedInRender();
|
||||
} else {
|
||||
// 执行vNode节点渲染
|
||||
launchUpdateFromVNode(vNode);
|
||||
}
|
||||
}
|
||||
|
||||
export function useReducerForInit<S, A>(reducer, initArg, init, isUseState?: boolean): [S, Trigger<A>] {
|
||||
|
|
Loading…
Reference in New Issue