Skip to content

Commit aa4b5b8

Browse files
authored
refactor: Use channel to optimize render (#32)
* chore: init effect state * refactor: useLayoutEffect for refresh * chore: tmp of key cache * refactor: merge effect at once * chore: clean up * chore: support if channel not support
1 parent 478db39 commit aa4b5b8

File tree

5 files changed

+94
-56
lines changed

5 files changed

+94
-56
lines changed

src/Overflow.tsx

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import classNames from 'classnames';
44
import ResizeObserver from 'rc-resize-observer';
55
import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
66
import Item from './Item';
7-
import { useBatchFrameState } from './hooks/useBatchFrameState';
7+
import useEffectState, { useBatcher } from './hooks/useEffectState';
88
import RawItem from './RawItem';
99

1010
export const OverflowContext = React.createContext<{
@@ -89,24 +89,37 @@ function Overflow<ItemType = any>(
8989
...restProps
9090
} = props;
9191

92-
const createUseState = useBatchFrameState();
93-
9492
const fullySSR = ssr === 'full';
9593

96-
const [containerWidth, setContainerWidth] = createUseState<number>(null);
94+
const notifyEffectUpdate = useBatcher();
95+
96+
const [containerWidth, setContainerWidth] = useEffectState<number>(
97+
notifyEffectUpdate,
98+
null,
99+
);
97100
const mergedContainerWidth = containerWidth || 0;
98101

99-
const [itemWidths, setItemWidths] = createUseState(
102+
const [itemWidths, setItemWidths] = useEffectState(
103+
notifyEffectUpdate,
100104
new Map<React.Key, number>(),
101105
);
102106

103-
const [prevRestWidth, setPrevRestWidth] = createUseState(0);
104-
const [restWidth, setRestWidth] = createUseState(0);
107+
const [prevRestWidth, setPrevRestWidth] = useEffectState<number>(
108+
notifyEffectUpdate,
109+
0,
110+
);
111+
const [restWidth, setRestWidth] = useEffectState<number>(
112+
notifyEffectUpdate,
113+
0,
114+
);
105115

106-
const [suffixWidth, setSuffixWidth] = createUseState(0);
116+
const [suffixWidth, setSuffixWidth] = useEffectState<number>(
117+
notifyEffectUpdate,
118+
0,
119+
);
107120
const [suffixFixedStart, setSuffixFixedStart] = useState<number>(null);
108121

109-
const [displayCount, setDisplayCount] = useState(null);
122+
const [displayCount, setDisplayCount] = useState<number>(null);
110123
const mergedDisplayCount = React.useMemo(() => {
111124
if (displayCount === null && fullySSR) {
112125
return Number.MAX_SAFE_INTEGER;

src/hooks/channelUpdate.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import raf from 'rc-util/lib/raf';
2+
3+
export default function channelUpdate(callback: VoidFunction) {
4+
if (typeof MessageChannel === 'undefined') {
5+
raf(callback);
6+
} else {
7+
const channel = new MessageChannel();
8+
channel.port1.onmessage = () => callback();
9+
channel.port2.postMessage(undefined);
10+
}
11+
}

src/hooks/useBatchFrameState.tsx

Lines changed: 0 additions & 45 deletions
This file was deleted.

src/hooks/useEffectState.tsx

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import useEvent from 'rc-util/lib/hooks/useEvent';
2+
import * as React from 'react';
3+
import { unstable_batchedUpdates } from 'react-dom';
4+
import channelUpdate from './channelUpdate';
5+
6+
type Updater<T> = T | ((origin: T) => T);
7+
8+
type UpdateCallbackFunc = VoidFunction;
9+
10+
type NotifyEffectUpdate = (callback: UpdateCallbackFunc) => void;
11+
12+
/**
13+
* Batcher for record any `useEffectState` need update.
14+
*/
15+
export function useBatcher() {
16+
// Updater Trigger
17+
const updateFuncRef = React.useRef<UpdateCallbackFunc[]>(null);
18+
19+
// Notify update
20+
const notifyEffectUpdate: NotifyEffectUpdate = callback => {
21+
if (!updateFuncRef.current) {
22+
updateFuncRef.current = [];
23+
24+
channelUpdate(() => {
25+
unstable_batchedUpdates(() => {
26+
updateFuncRef.current.forEach(fn => {
27+
fn();
28+
});
29+
updateFuncRef.current = null;
30+
});
31+
});
32+
}
33+
34+
updateFuncRef.current.push(callback);
35+
};
36+
37+
return notifyEffectUpdate;
38+
}
39+
40+
/**
41+
* Trigger state update by `useLayoutEffect` to save perf.
42+
*/
43+
export default function useEffectState<T extends string | number | object>(
44+
notifyEffectUpdate: NotifyEffectUpdate,
45+
defaultValue?: T,
46+
): [T, (value: Updater<T>) => void] {
47+
// Value
48+
const [stateValue, setStateValue] = React.useState(defaultValue);
49+
50+
// Set State
51+
const setEffectVal = useEvent((nextValue: Updater<T>) => {
52+
notifyEffectUpdate(() => {
53+
setStateValue(nextValue);
54+
});
55+
});
56+
57+
return [stateValue, setEffectVal];
58+
}

src/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import Overflow, { OverflowProps } from './Overflow';
1+
import Overflow from './Overflow';
2+
import type { OverflowProps } from './Overflow';
23

3-
export { OverflowProps };
4+
export type { OverflowProps };
45

56
export default Overflow;

0 commit comments

Comments
 (0)