Skip to content

Commit d963af0

Browse files
committed
Avoids duplicate deref calculations in useStoreDependency
See #95 Avoids duplicate deref calculations in `useStoreDependency`. Only calculate once; use this result to initialize state (if necessary) and as the next state. When the value changes, two deref calculations will still run: 1. the initial render/hook call 2. the re-render/hook call triggered by setting the new value in state
1 parent 5569034 commit d963af0

File tree

2 files changed

+38
-4
lines changed

2 files changed

+38
-4
lines changed

src/dependencies/__tests__/useStoreDependency-test.js

+36
Original file line numberDiff line numberDiff line change
@@ -214,13 +214,16 @@ describe('useStoreDependency', () => {
214214
describe('with props', () => {
215215
let store;
216216
let multiplyDependency;
217+
let derefCallCount;
217218

218219
beforeEach(() => {
219220
store = BaseStoreFactory.register(dispatcher);
221+
derefCallCount = 0;
220222
multiplyDependency = {
221223
stores: [store],
222224
deref: ({ factor }) => {
223225
if (!factor) throw new Error('no factor provided');
226+
derefCallCount += 1;
224227
return store.get() * factor;
225228
},
226229
};
@@ -287,5 +290,38 @@ describe('useStoreDependency', () => {
287290
rendered.update();
288291
expect(rendered.find('div').prop('data-factor')).toBe(16);
289292
});
293+
294+
describe('deref call count', () => {
295+
it('called once on initial render', () => {
296+
const Component = ({ factor }) => {
297+
const value = useStoreDependency(multiplyDependency, { factor });
298+
return <div data-value={value} />;
299+
};
300+
mount(<Component factor={4} />);
301+
expect(derefCallCount).toBe(1);
302+
});
303+
304+
it('called once on re-render', () => {
305+
const Component = ({ factor }) => {
306+
const value = useStoreDependency(multiplyDependency, { factor });
307+
return <div data-value={value} />;
308+
};
309+
const rendered = mount(<Component factor={4} />);
310+
rendered.setProps({});
311+
rendered.update();
312+
expect(derefCallCount).toBe(2);
313+
});
314+
315+
it('called twice on prop change (once from the initial re-render, again in the second re-render after the internal setState call)', () => {
316+
const Component = ({ factor }) => {
317+
const value = useStoreDependency(multiplyDependency, { factor });
318+
return <div data-value={value} />;
319+
};
320+
const rendered = mount(<Component factor={4} />);
321+
rendered.setProps({ factor: 8 });
322+
rendered.update();
323+
expect(derefCallCount).toBe(3);
324+
});
325+
});
290326
});
291327
});

src/dependencies/useStoreDependency.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,8 @@ function useStoreDependency<Props, DepType>(
8383
): DepType {
8484
enforceDispatcher(dispatcher);
8585

86-
const [dependencyValue, setDependencyValue] = useState({
87-
dependency: calculate(dependency, props),
88-
});
86+
const newValue = { dependency: calculate(dependency, props) };
87+
const [dependencyValue, setDependencyValue] = useState(newValue);
8988

9089
const currProps = useCurrent(props);
9190

@@ -99,7 +98,6 @@ function useStoreDependency<Props, DepType>(
9998
setDependencyValue
10099
);
101100

102-
const newValue = { dependency: calculate(dependency, props) };
103101
if (!shallowEqual(newValue.dependency, dependencyValue.dependency)) {
104102
setDependencyValue(newValue);
105103
return newValue.dependency;

0 commit comments

Comments
 (0)