diff --git a/__tests__/functionComponent.test.no-hook.jsx b/__tests__/functionComponent.test.no-hook.jsx deleted file mode 100644 index 2d5aa14..0000000 --- a/__tests__/functionComponent.test.no-hook.jsx +++ /dev/null @@ -1,52 +0,0 @@ -import React, { Component } from 'react'; -import { render, cleanup } from '@testing-library/react/pure'; -// eslint-disable-next-line import/no-unresolved -import { view, store } from 'react-easy-state'; - -describe('Using an old react version', () => { - afterEach(cleanup); - - test('Using local state in a function component with a version of react that has no hooks should throw an error', () => { - const MyComp = view(() => { - const person = store({ name: 'Bob' }); - return
{person.name}
; - }); - expect(() => render()).toThrow( - '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.', - ); - }); - - test('Using global state in a function component with a version of react that has no hooks should not throw an error', () => { - const person = store({ name: 'Bob' }); - const MyComp = view(() => { - return
{person.name}
; - }); - expect(() => render()).not.toThrow(); - }); - - test('Using global state in a class component with a version of react that has no hooks should not throw an error', () => { - const person = store({ name: 'Bob' }); - const MyComp = view( - class extends Component { - render() { - return
{person.name}
; - } - }, - ); - expect(() => render()).not.toThrow(); - }); - - test('Using local state in a class component with a version of react that has no hooks should not throw an error', () => { - const MyComp = view( - class extends Component { - person = store({ name: 'Bob' }); - - render() { - return
{this.person.name}
; - } - }, - ); - - expect(() => render()).not.toThrow(); - }); -}); diff --git a/__tests__/store.no-hook.test.jsx b/__tests__/store.no-hook.test.jsx new file mode 100644 index 0000000..51929e6 --- /dev/null +++ b/__tests__/store.no-hook.test.jsx @@ -0,0 +1,76 @@ +import React, { Component } from 'react'; +import { render, cleanup } from '@testing-library/react/pure'; +// eslint-disable-next-line import/no-unresolved +import { view, store } from 'react-easy-state'; + +describe('Using an old react version', () => { + afterEach(cleanup); + + test(`Using local state in a function component ${ + process.env.NOHOOK + ? 'with a version of react that has no hooks should' + : 'should not' + } throw an error`, () => { + const MyComp = view(() => { + const person = store({ name: 'Bob' }); + return
{person.name}
; + }); + + if (process.env.NOHOOK) { + expect(() => render()).toThrow( + '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.', + ); + } else { + expect(() => render()).not.toThrow(); + } + }); + + test('Using global state in a function component should not throw an error', () => { + const person = store({ name: 'Bob' }); + const MyComp = view(() => { + return
{person.name}
; + }); + expect(() => render()).not.toThrow(); + }); + + test('Using global state in a class component should not throw an error', () => { + const person = store({ name: 'Bob' }); + const MyComp = view( + class extends Component { + render() { + return
{person.name}
; + } + }, + ); + expect(() => render()).not.toThrow(); + }); + + test('Using local state in a class component should not throw an error', () => { + const MyComp = view( + class extends Component { + person = store({ name: 'Bob' }); + + render() { + return
{this.person.name}
; + } + }, + ); + + expect(() => render()).not.toThrow(); + }); + + test('Using local state inside a render of a class component should throw an error', () => { + const MyComp = view( + class extends Component { + render() { + const person = store({ name: 'Bob' }); + return
{person.name}
; + } + }, + ); + + expect(() => render()).toThrow( + 'You cannot use state inside a render of a class component. Please create your store outside of the render function.', + ); + }); +}); diff --git a/jest.no-hook.json b/jest.no-hook.json index 08032b2..bac4ac6 100644 --- a/jest.no-hook.json +++ b/jest.no-hook.json @@ -1,6 +1,6 @@ { "setupFilesAfterEnv": ["./scripts/testSetup.js"], - "testRegex": "\\.test\\.no-hook\\.jsx?$", + "testRegex": "\\.no-hook\\.test\\.jsx?$", "collectCoverage": true, "coverageReporters": ["text"], "collectCoverageFrom": ["src/**/*.{js,jsx}"], diff --git a/src/store.js b/src/store.js index 56c6f30..3810ea9 100644 --- a/src/store.js +++ b/src/store.js @@ -3,9 +3,9 @@ import { observable } from '@nx-js/observer-util'; import { isInsideFunctionComponent, - isInsideClassComponent, + isInsideClassComponentRender, + isInsideFunctionComponentWithoutHooks, } from './view'; -import { hasHooks } from './utils'; export function store(obj) { // do not create new versions of the store on every render @@ -14,10 +14,15 @@ export function store(obj) { if (isInsideFunctionComponent) { return useMemo(() => observable(obj), []); } - if (!hasHooks && isInsideClassComponent) { + if (isInsideFunctionComponentWithoutHooks) { throw new Error( '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.', ); } + if (isInsideClassComponentRender) { + throw new Error( + 'You cannot use state inside a render of a class component. Please create your store outside of the render function.', + ); + } return observable(obj); } diff --git a/src/view.js b/src/view.js index 5063749..6052773 100644 --- a/src/view.js +++ b/src/view.js @@ -17,7 +17,8 @@ import { hasHooks } from './utils'; import { queue } from './queue'; export let isInsideFunctionComponent = false; -export let isInsideClassComponent = false; +export let isInsideClassComponentRender = false; +export let isInsideFunctionComponentWithoutHooks = false; const COMPONENT = Symbol('owner component'); const TRIGGERRENDER = Symbol('trigger render'); @@ -96,13 +97,15 @@ export function view(Comp) { }; render() { - isInsideClassComponent = true; + isInsideClassComponentRender = !isStatelessComp; + isInsideFunctionComponentWithoutHooks = isStatelessComp; try { return isStatelessComp ? Comp(this.props, this.context) : super.render(); } finally { - isInsideClassComponent = false; + isInsideClassComponentRender = false; + isInsideFunctionComponentWithoutHooks = false; } }