Skip to content

Commit 282fa1c

Browse files
fix: computed props w/ dependencies on store state (#874)
1 parent 11903f8 commit 282fa1c

File tree

3 files changed

+66
-8
lines changed

3 files changed

+66
-8
lines changed

src/computed-properties.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import equal from 'fast-deep-equal/es6';
22
import { areInputsEqual } from './lib';
33

4-
export function createComputedPropertyBinder(parentPath, key, def, _r) {
5-
let runOnce = false;
4+
export function createComputedPropertyBinder(key, def, _r) {
5+
let hasRunOnce = false;
66
let prevInputs = [];
77
let prevValue;
88
let prevStoreState;
@@ -28,10 +28,11 @@ export function createComputedPropertyBinder(parentPath, key, def, _r) {
2828
const inputs = def.stateResolvers.map((resolver) =>
2929
resolver(parentState, storeState),
3030
);
31+
3132
if (
32-
runOnce &&
33-
(storeState === prevStoreState ||
34-
areInputsEqual(inputs, prevInputs) ||
33+
hasRunOnce &&
34+
((storeState === prevStoreState &&
35+
areInputsEqual(inputs, prevInputs)) ||
3536
// We don't want computed properties resolved every time an action
3637
// is handled by the reducer. They need to remain lazy, only being
3738
// computed when used by a component or getState call;
@@ -49,7 +50,7 @@ export function createComputedPropertyBinder(parentPath, key, def, _r) {
4950

5051
prevInputs = inputs;
5152
prevStoreState = storeState;
52-
runOnce = true;
53+
hasRunOnce = true;
5354
return prevValue;
5455
},
5556
});

src/extract-data-from-model.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,15 +150,19 @@ export default function extractDataFromModel(
150150
} else if (value[computedSymbol]) {
151151
const parent = get(parentPath, _dS);
152152
const bindComputedProperty = createComputedPropertyBinder(
153-
parentPath,
154153
key,
155154
value,
156155
_r,
157156
);
158157
bindComputedProperty(parent, _dS);
159158
_cP.push({ key, parentPath, bindComputedProperty });
160159
} else if (value[reducerSymbol]) {
161-
_cR.push({ key, parentPath, reducer: value.fn, config: value.config });
160+
_cR.push({
161+
key,
162+
parentPath,
163+
reducer: value.fn,
164+
config: value.config,
165+
});
162166
} else if (value[effectOnSymbol]) {
163167
const def = { ...value };
164168

tests/computed.test.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,3 +619,56 @@ test('computed properties operate against their original store state', () => {
619619
expect(stateAtAPointInTime.count).toBe(1);
620620
expect(store.getState().count).toBe(2);
621621
});
622+
623+
test('issue #873: computed properties without state resolvers update when they have dependencies that depend on store state', () => {
624+
// ARRANGE
625+
const store = createStore({
626+
foo: {
627+
status: 'Ok',
628+
setStatus: action((state, payload) => {
629+
state.status = payload;
630+
})
631+
},
632+
bar: {
633+
status: 'Ok',
634+
setStatus: action((state, payload) => {
635+
state.status = payload;
636+
})
637+
},
638+
baz: {
639+
errorMessage: computed(
640+
[
641+
(_, storeState) => storeState.foo.status,
642+
(_, storeState) => storeState.bar.status
643+
],
644+
(fooStatus, barStatus) => {
645+
if (fooStatus === 'Error' || barStatus === 'Error') {
646+
return 'Uh oh, error in app!';
647+
}
648+
649+
return '';
650+
}
651+
),
652+
hasError: computed((state) => Boolean(state.errorMessage))
653+
}
654+
});
655+
656+
let state = store.getState();
657+
658+
// ASSERT
659+
expect(state.foo.status).toBe('Ok');
660+
expect(state.bar.status).toBe('Ok');
661+
expect(state.baz.errorMessage).toBe('');
662+
expect(state.baz.hasError).toBe(false);
663+
664+
// ACT
665+
store.getActions().foo.setStatus('Error');
666+
667+
state = store.getState();
668+
669+
// ASSERT
670+
expect(state.foo.status).toBe('Error');
671+
expect(state.bar.status).toBe('Ok');
672+
expect(state.baz.errorMessage).toBe('Uh oh, error in app!');
673+
expect(state.baz.hasError).toBe(true); // would have failed before #874
674+
});

0 commit comments

Comments
 (0)