Skip to content

Commit c323476

Browse files
committed
fix: stop unnecessary re-rendering
1 parent 047be68 commit c323476

File tree

5 files changed

+62
-33
lines changed

5 files changed

+62
-33
lines changed

__tests__/FixedSizeTree.spec.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ describe('FixedSizeTree', () => {
4141

4242
function* treeWalker(
4343
refresh: boolean,
44-
): IterableIterator<FixedSizeNodeData<ExtendedData> | string | symbol> {
44+
): Generator<
45+
FixedSizeNodeData<ExtendedData> | string | symbol,
46+
void,
47+
boolean
48+
> {
4549
const stack: StackElement[] = [];
4650

4751
stack.push({
@@ -161,15 +165,26 @@ describe('FixedSizeTree', () => {
161165
expect(component.find(FixedSizeList).prop('children')).toBe(rowComponent);
162166
});
163167

164-
it('recomputes on new props', () => {
168+
it('recomputes on new treeWalker', () => {
165169
treeWalkerSpy = jest.fn(treeWalker);
170+
166171
component.setProps({
167172
treeWalker: treeWalkerSpy,
168173
});
169174

170175
expect(treeWalkerSpy).toHaveBeenCalledWith(true);
171176
});
172177

178+
it('does not recompute if treeWalker is the same', () => {
179+
treeWalkerSpy.mockClear();
180+
181+
component.setProps({
182+
treeWalker: treeWalkerSpy,
183+
});
184+
185+
expect(treeWalkerSpy).not.toHaveBeenCalled();
186+
});
187+
173188
describe('component instance', () => {
174189
let treeInstance: FixedSizeTree<ExtendedData>;
175190

__tests__/VariableSizeTree.spec.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ describe('VariableSizeTree', () => {
4242

4343
function* treeWalker(
4444
refresh: boolean,
45-
): IterableIterator<VariableSizeNodeData<ExtendedData> | string | symbol> {
45+
): Generator<
46+
VariableSizeNodeData<ExtendedData> | string | symbol,
47+
void,
48+
boolean
49+
> {
4650
const stack: StackElement[] = [];
4751

4852
stack.push({
@@ -171,15 +175,26 @@ describe('VariableSizeTree', () => {
171175
);
172176
});
173177

174-
it('recomputes on new props', () => {
178+
it('recomputes on new treeWalker', () => {
175179
treeWalkerSpy = jest.fn(treeWalker);
180+
176181
component.setProps({
177182
treeWalker: treeWalkerSpy,
178183
});
179184

180185
expect(treeWalkerSpy).toHaveBeenCalledWith(true);
181186
});
182187

188+
it('does not recompute if treeWalker is the same', () => {
189+
treeWalkerSpy.mockClear();
190+
191+
component.setProps({
192+
treeWalker: treeWalkerSpy,
193+
});
194+
195+
expect(treeWalkerSpy).not.toHaveBeenCalled();
196+
});
197+
183198
describe('component instance', () => {
184199
let treeInstance: VariableSizeTree<ExtendedData>;
185200

@@ -599,7 +614,7 @@ describe('VariableSizeTree', () => {
599614
.instance() as VariableSizeList;
600615

601616
const resetAfterIndexSpy = spyOn(listInstance, 'resetAfterIndex');
602-
const order = component.state('order');
617+
const order = component.state('order')!;
603618
const foo3 = component.state('records')['foo-3'];
604619

605620
foo3.resize(100, true);

src/FixedSizeTree.tsx

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,15 @@ export default class FixedSizeTree<T> extends React.PureComponent<
112112
props: FixedSizeTreeProps<{}>,
113113
state: FixedSizeTreeState<{}>,
114114
): Partial<FixedSizeTreeState<{}>> {
115-
const {children: component, itemData: treeData} = props;
115+
const {children: component, itemData: treeData, treeWalker} = props;
116+
const {treeWalker: oldTreeWalker, order} = state;
116117

117118
return {
118119
component,
119120
treeData,
120-
...computeTree({refreshNodes: true}, props, state),
121+
...(treeWalker !== oldTreeWalker || !order
122+
? computeTree({refreshNodes: true}, props, state)
123+
: null),
121124
};
122125
}
123126

@@ -126,16 +129,11 @@ export default class FixedSizeTree<T> extends React.PureComponent<
126129
public constructor(props: FixedSizeTreeProps<T>, context: any) {
127130
super(props, context);
128131

129-
const initialState: FixedSizeTreeState<T> = {
132+
this.state = {
130133
component: props.children,
131-
order: [],
132134
recomputeTree: this.recomputeTree.bind(this),
133135
records: {},
134-
};
135-
136-
this.state = {
137-
...initialState,
138-
...computeTree({refreshNodes: true}, props, initialState),
136+
treeWalker: props.treeWalker,
139137
};
140138
}
141139

@@ -153,7 +151,7 @@ export default class FixedSizeTree<T> extends React.PureComponent<
153151
}
154152

155153
public scrollToItem(id: string | symbol, align?: Align): void {
156-
this.list.current?.scrollToItem(this.state.order.indexOf(id) || 0, align);
154+
this.list.current?.scrollToItem(this.state.order!.indexOf(id) || 0, align);
157155
}
158156

159157
public render(): React.ReactNode {
@@ -163,7 +161,7 @@ export default class FixedSizeTree<T> extends React.PureComponent<
163161
<FixedSizeList
164162
{...rest}
165163
itemData={this.state}
166-
itemCount={this.state.order.length}
164+
itemCount={this.state.order!.length}
167165
ref={this.list}
168166
>
169167
{rowComponent!}

src/VariableSizeTree.tsx

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,15 @@ export default class VariableSizeTree<T> extends React.PureComponent<
163163
props: VariableSizeTreeProps<{}>,
164164
state: VariableSizeTreeState<{}>,
165165
): Partial<VariableSizeTreeState<{}>> {
166-
const {children: component, itemData: treeData} = props;
166+
const {children: component, itemData: treeData, treeWalker} = props;
167+
const {treeWalker: oldTreeWalker, order} = state;
167168

168169
return {
169170
component,
170171
treeData,
171-
...computeTree({refreshNodes: true}, props, state),
172+
...(treeWalker !== oldTreeWalker || !order
173+
? computeTree({refreshNodes: true}, props, state)
174+
: null),
172175
};
173176
}
174177

@@ -179,17 +182,12 @@ export default class VariableSizeTree<T> extends React.PureComponent<
179182

180183
this.getItemSize = this.getItemSize.bind(this);
181184

182-
const initialState: VariableSizeTreeState<T> = {
185+
this.state = {
183186
component: props.children,
184-
order: [],
185187
recomputeTree: this.recomputeTree.bind(this),
186188
records: {},
187189
resetAfterId: this.resetAfterId.bind(this),
188-
};
189-
190-
this.state = {
191-
...initialState,
192-
...computeTree({refreshNodes: true}, props, initialState),
190+
treeWalker: props.treeWalker,
193191
};
194192
}
195193

@@ -215,7 +213,7 @@ export default class VariableSizeTree<T> extends React.PureComponent<
215213
shouldForceUpdate: boolean = false,
216214
): void {
217215
this.list.current?.resetAfterIndex(
218-
this.state.order.indexOf(id),
216+
this.state.order!.indexOf(id),
219217
shouldForceUpdate,
220218
);
221219
}
@@ -225,7 +223,7 @@ export default class VariableSizeTree<T> extends React.PureComponent<
225223
}
226224

227225
public scrollToItem(id: string | symbol, align?: Align): void {
228-
this.list.current?.scrollToItem(this.state.order.indexOf(id) || 0, align);
226+
this.list.current?.scrollToItem(this.state.order!.indexOf(id) || 0, align);
229227
}
230228

231229
public render(): React.ReactNode {
@@ -235,7 +233,7 @@ export default class VariableSizeTree<T> extends React.PureComponent<
235233
<VariableSizeList
236234
{...rest}
237235
itemData={this.state}
238-
itemCount={this.state.order.length}
236+
itemCount={this.state.order!.length}
239237
// tslint:disable-next-line:no-unbound-method
240238
itemSize={itemSize || this.getItemSize}
241239
ref={this.list}
@@ -248,6 +246,6 @@ export default class VariableSizeTree<T> extends React.PureComponent<
248246
private getItemSize(index: number): number {
249247
const {order, records} = this.state;
250248

251-
return records[order[index] as string].height;
249+
return records[order![index] as string].height;
252250
}
253251
}

src/utils.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
import * as React from 'react';
33
import {ListChildComponentProps} from 'react-window';
44

5+
export type TreeWalker<T> = (
6+
refresh: boolean,
7+
) => Generator<T | string | symbol, void, boolean>;
8+
59
export type CommonNodeData<T> = {
610
/**
711
* Unique ID of the current node. Will be used to identify the node to change
@@ -42,9 +46,7 @@ export type CommonNodeComponentProps<TData extends CommonNodeData<T>, T> = Omit<
4246

4347
export type TreeProps<TData extends CommonNodeData<T>, T> = {
4448
readonly rowComponent?: React.ComponentType<ListChildComponentProps>;
45-
readonly treeWalker: (
46-
refresh: boolean,
47-
) => Generator<TData | string | symbol, void, boolean>;
49+
readonly treeWalker: TreeWalker<TData>;
4850
};
4951

5052
export type TreeState<
@@ -55,10 +57,11 @@ export type TreeState<
5557
T
5658
> = {
5759
readonly component: React.ComponentType<TNodeComponentProps>;
58-
readonly order: ReadonlyArray<string | symbol>;
60+
readonly order?: ReadonlyArray<string | symbol>;
5961
readonly records: Record<string, TNodeRecord>;
6062
readonly treeData?: any;
6163
readonly recomputeTree: (options?: TUpdateOptions) => Promise<void>;
64+
readonly treeWalker: TreeWalker<TData>;
6265
};
6366

6467
export const Row: React.FunctionComponent<ListChildComponentProps> = ({

0 commit comments

Comments
 (0)