Skip to content

Commit e6a680a

Browse files
rolandszokesolkimicreb
authored andcommitted
fix(store): throw error if store used inside render
1 parent ad47b30 commit e6a680a

File tree

3 files changed

+32
-9
lines changed

3 files changed

+32
-9
lines changed

__tests__/functionComponent.test.no-hook.jsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ describe('Using an old react version', () => {
1616
);
1717
});
1818

19-
test('Using global state in a function component with a version of react that has no hooks should not throw an error', () => {
19+
test('Using global state in a function component should not throw an error', () => {
2020
const person = store({ name: 'Bob' });
2121
const MyComp = view(() => {
2222
return <div>{person.name}</div>;
2323
});
2424
expect(() => render(<MyComp />)).not.toThrow();
2525
});
2626

27-
test('Using global state in a class component with a version of react that has no hooks should not throw an error', () => {
27+
test('Using global state in a class component should not throw an error', () => {
2828
const person = store({ name: 'Bob' });
2929
const MyComp = view(
3030
class extends Component {
@@ -36,7 +36,7 @@ describe('Using an old react version', () => {
3636
expect(() => render(<MyComp />)).not.toThrow();
3737
});
3838

39-
test('Using local state in a class component with a version of react that has no hooks should not throw an error', () => {
39+
test('Using local state in a class component should not throw an error', () => {
4040
const MyComp = view(
4141
class extends Component {
4242
person = store({ name: 'Bob' });
@@ -49,4 +49,19 @@ describe('Using an old react version', () => {
4949

5050
expect(() => render(<MyComp />)).not.toThrow();
5151
});
52+
53+
test('Using local state inside a render of a class component should throw an error', () => {
54+
const MyComp = view(
55+
class extends Component {
56+
render() {
57+
const person = store({ name: 'Bob' });
58+
return <div>{person.name}</div>;
59+
}
60+
},
61+
);
62+
63+
expect(() => render(<MyComp />)).toThrow(
64+
'You cannot use state inside a render of a class component. Please create your store outside of the render function.',
65+
);
66+
});
5267
});

src/store.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import { observable } from '@nx-js/observer-util';
33

44
import {
55
isInsideFunctionComponent,
6-
isInsideClassComponent,
6+
isInsideClassComponentRender,
7+
isInsideFunctionComponentWithoutHooks,
78
} from './view';
8-
import { hasHooks } from './utils';
99

1010
export function store(obj) {
1111
// do not create new versions of the store on every render
@@ -14,10 +14,15 @@ export function store(obj) {
1414
if (isInsideFunctionComponent) {
1515
return useMemo(() => observable(obj), []);
1616
}
17-
if (!hasHooks && isInsideClassComponent) {
17+
if (isInsideFunctionComponentWithoutHooks) {
1818
throw new Error(
1919
'You cannot use state inside a function component with a pre-hooks version of React. Please update your React version to at least v16.8.0 to use this feature.',
2020
);
2121
}
22+
if (isInsideClassComponentRender) {
23+
throw new Error(
24+
'You cannot use state inside a render of a class component. Please create your store outside of the render function.',
25+
);
26+
}
2227
return observable(obj);
2328
}

src/view.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ import { hasHooks } from './utils';
1717
import { queue } from './queue';
1818

1919
export let isInsideFunctionComponent = false;
20-
export let isInsideClassComponent = false;
20+
export let isInsideClassComponentRender = false;
21+
export let isInsideFunctionComponentWithoutHooks = false;
2122
const COMPONENT = Symbol('owner component');
2223
const TRIGGERRENDER = Symbol('trigger render');
2324

@@ -96,13 +97,15 @@ export function view(Comp) {
9697
};
9798

9899
render() {
99-
isInsideClassComponent = true;
100+
isInsideClassComponentRender = !isStatelessComp;
101+
isInsideFunctionComponentWithoutHooks = isStatelessComp;
100102
try {
101103
return isStatelessComp
102104
? Comp(this.props, this.context)
103105
: super.render();
104106
} finally {
105-
isInsideClassComponent = false;
107+
isInsideClassComponentRender = false;
108+
isInsideFunctionComponentWithoutHooks = false;
106109
}
107110
}
108111

0 commit comments

Comments
 (0)