Skip to content

Commit

Permalink
98 react strict (#99)
Browse files Browse the repository at this point in the history
Fixes #98
  • Loading branch information
davidgilbertson authored Mar 19, 2020
1 parent 110a8e5 commit 928b194
Show file tree
Hide file tree
Showing 22 changed files with 98 additions and 66 deletions.
5 changes: 3 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,11 @@
"error",
{
"allow": [
"_name",
"__RR__",
"_isMounted",
"_isMounting",
"__RR__"
"_isRendering",
"_name"
]
}
],
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-recollect",
"version": "5.1.0",
"version": "5.1.1",
"description": "Simple state management for react",
"keywords": [
"flux",
Expand Down
11 changes: 10 additions & 1 deletion src/collect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ const collect = <C extends React.ComponentType<any>>(

private _isMounting = true;

// <React.StrictMode> will trigger multiple renders,
// we must disregard these
private _isRendering = false;

_name = componentName;

static displayName = `Collected(${componentName})`;
Expand All @@ -114,11 +118,13 @@ const collect = <C extends React.ComponentType<any>>(

// Stop recording. For first render()
stopRecordingGetsForComponent();
this._isRendering = false;
}

componentDidUpdate() {
// Stop recording. For not-first render()
stopRecordingGetsForComponent();
this._isRendering = false;
}

componentWillUnmount() {
Expand All @@ -137,7 +143,10 @@ const collect = <C extends React.ComponentType<any>>(
}

render() {
startRecordingGetsForComponent(this);
if (!this._isRendering) {
startRecordingGetsForComponent(this);
this._isRendering = true;
}

const props = {
...this.props,
Expand Down
11 changes: 7 additions & 4 deletions tests/integration/TaskListTest/TaskList.test.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import React from 'react';
import { render } from '@testing-library/react';
import TaskList from './TaskList';
import { store } from '../../../src';
import * as testUtils from '../../testUtils';

it('TaskList', async () => {
const { findByText, getByText, queryByText, getByLabelText } = render(
<TaskList />
);
const {
findByText,
getByText,
queryByText,
getByLabelText,
} = testUtils.renderStrict(<TaskList />);

// it should render a loading indicator
getByText('Loading...');
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/TaskListTest/isolation.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/

import React from 'react';
import { render } from '@testing-library/react';
import * as testUtils from '../../testUtils';
import App from './App';
import { store } from '../../../src';

Expand All @@ -29,7 +29,7 @@ const props = {
};

it('should handle isolation', () => {
const { getByText } = render(<App {...props} />);
const { getByText } = testUtils.renderStrict(<App {...props} />);

// should render the title
getByText('The task list site');
Expand Down
6 changes: 3 additions & 3 deletions tests/integration/basicClassComponent.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { render } from '@testing-library/react';
import { collect, store, WithStoreProp } from '../../src';
import * as testUtils from '../testUtils';

// eslint-disable-next-line react/prefer-stateless-function
class RawClassComponent extends Component<WithStoreProp> {
Expand All @@ -27,7 +27,7 @@ store.title = 'The initial title';
store.clickCount = 3;

it('should render and update the title', () => {
const { getByText } = render(<ClassComponent />);
const { getByText } = testUtils.renderStrict(<ClassComponent />);

expect(getByText('The initial title'));

Expand All @@ -38,7 +38,7 @@ it('should render and update the title', () => {
});

it('should render and update the click count', () => {
const { getByText } = render(<ClassComponent />);
const { getByText } = testUtils.renderStrict(<ClassComponent />);

expect(getByText('Button was pressed 3 times'));

Expand Down
6 changes: 4 additions & 2 deletions tests/integration/basicFunctionalComponent.test.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React from 'react';
import { store, WithStoreProp } from '../../src';
import { collectAndRender } from '../testUtils';
import * as testUtils from '../testUtils';

it('should render the title', () => {
store.title = 'The initial title';

const { getByText } = collectAndRender((props: WithStoreProp) => (
const {
getByText,
} = testUtils.collectAndRenderStrict((props: WithStoreProp) => (
<h1>{props.store.title}</h1>
));

Expand Down
9 changes: 4 additions & 5 deletions tests/integration/componentDidMount.test.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
/* eslint-disable max-classes-per-file */
import React, { Component } from 'react';
import { render } from '@testing-library/react';
import { expectToLogError } from '../testUtils';
import { collect, WithStoreProp } from '../../src';
import * as testUtils from '../testUtils';

const TestComponentBad = collect(
class extends Component<WithStoreProp> {
Expand Down Expand Up @@ -43,13 +42,13 @@ const TestComponentGood = collect(
);

it('should fail if setting the state during mounting', () => {
expectToLogError(() => {
render(<TestComponentBad />);
testUtils.expectToLogError(() => {
testUtils.renderStrict(<TestComponentBad />);
});
});

it('should set loading state after mounting', async () => {
const { findByText } = render(<TestComponentGood />);
const { findByText } = testUtils.renderStrict(<TestComponentGood />);

await findByText('Loading...');

Expand Down
6 changes: 3 additions & 3 deletions tests/integration/componentDidUpdate.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable max-classes-per-file */
import React, { Component, useEffect } from 'react';
import { render, waitFor } from '@testing-library/react';
import { waitFor } from '@testing-library/react';
import { collect, store as globalStore, WithStoreProp } from '../../src';
import * as testUtils from '../testUtils';

Expand Down Expand Up @@ -100,7 +100,7 @@ const reportUserChange = jest.fn();
it('should handle a change in a value', () => {
globalStore.userId = 1;

const { getByText } = render(
const { getByText } = testUtils.renderStrict(
<ParentComponent reportUserChange={reportUserChange} />
);

Expand All @@ -125,7 +125,7 @@ it('should re-render on a hidden prop read (FAILS)', () => {
const sideEffectMock = jest.fn();
globalStore.loaded = false;

testUtils.collectAndRender(
testUtils.collectAndRenderStrict(
class extends Component<WithStoreProp> {
componentDidUpdate(prevProps: Readonly<WithStoreProp>) {
if (!prevProps.store.loaded && this.props.store.loaded) {
Expand Down
6 changes: 4 additions & 2 deletions tests/integration/forwardRefClass.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable max-classes-per-file */
import React, { Component } from 'react';
import { render } from '@testing-library/react';
import { collect, WithStoreProp } from '../../src';
import * as testUtils from '../testUtils';

interface Props extends WithStoreProp {
defaultValue: string;
Expand Down Expand Up @@ -44,7 +44,9 @@ class ComponentWithRef extends Component {
}
}

const { getByText, getByLabelText } = render(<ComponentWithRef />);
const { getByText, getByLabelText } = testUtils.renderStrict(
<ComponentWithRef />
);

it('should empty the input when the button is clicked', () => {
const getInputByLabelText = (text: string) =>
Expand Down
6 changes: 4 additions & 2 deletions tests/integration/forwardRefFc.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { render } from '@testing-library/react';
import { collect, WithStoreProp } from '../../src';
import * as testUtils from '../testUtils';

type Props = WithStoreProp & {
defaultValue: string;
Expand Down Expand Up @@ -34,7 +34,9 @@ class ComponentWithRef extends Component {
}
}

const { getByText, getByLabelText } = render(<ComponentWithRef />);
const { getByText, getByLabelText } = testUtils.renderStrict(
<ComponentWithRef />
);

const getInputByLabelText = (text: string) =>
getByLabelText(text) as HTMLInputElement;
Expand Down
12 changes: 7 additions & 5 deletions tests/integration/hooks.test.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React, { useEffect, useLayoutEffect, useState } from 'react';
import { waitFor } from '@testing-library/react';
import { waitFor, act } from '@testing-library/react';
import { store as globalStore, WithStoreProp } from '../../src';
import * as testUtils from '../testUtils';

it('should work with useState hook', () => {
globalStore.counter = 0;

const { getByText } = testUtils.collectAndRender(
const { getByText } = testUtils.collectAndRenderStrict(
({ store }: WithStoreProp) => {
const [counter, setCounter] = useState(0);

Expand Down Expand Up @@ -53,7 +53,7 @@ it('should work with useEffect hook', async () => {
const onMountMock = jest.fn();
const onCountChangeMock = jest.fn();

const { getByText } = testUtils.collectAndRender(
const { getByText } = testUtils.collectAndRenderStrict(
({ store }: WithStoreProp) => {
useEffect(() => {
onMountMock();
Expand Down Expand Up @@ -83,7 +83,9 @@ it('should work with useEffect hook', async () => {
expect(onCountChangeMock).toHaveBeenCalledTimes(1);

getByText('Store count: 0');
getByText('Increment store').click();
act(() => {
getByText('Increment store').click();
});
getByText('Store count: 1');

await waitFor(() => {}); // useEffect is async
Expand Down Expand Up @@ -119,7 +121,7 @@ it('changing store in useLayoutEffect should error', async () => {
globalStore.loaded = false;

testUtils.expectToLogError(() => {
testUtils.collectAndRender(({ store }: WithStoreProp) => {
testUtils.collectAndRenderStrict(({ store }: WithStoreProp) => {
useLayoutEffect(() => {
// Oh no, useLayoutEffect is synchronous, so will fire during render
store.loaded = true;
Expand Down
8 changes: 4 additions & 4 deletions tests/integration/listening.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ it('should register the correct listeners', () => {
},
});

testUtils.collectAndRender(({ store }: WithStoreProp) => {
testUtils.collectAndRenderStrict(({ store }: WithStoreProp) => {
return (
<div>
<h1>Object</h1>
Expand Down Expand Up @@ -86,7 +86,7 @@ it('should register the correct listeners', () => {
it('should register a listener on the store object itself', () => {
const {
getByText,
} = testUtils.collectAndRender(({ store }: WithStoreProp) => (
} = testUtils.collectAndRenderStrict(({ store }: WithStoreProp) => (
<div>
{Object.keys(store).length ? (
<div>The store has stuff in it</div>
Expand All @@ -108,7 +108,7 @@ it('should register a listener on the store object itself', () => {
it('should register a listener on the store object with values()', () => {
const {
getByText,
} = testUtils.collectAndRender(({ store }: WithStoreProp) => (
} = testUtils.collectAndRenderStrict(({ store }: WithStoreProp) => (
<div>
{Object.values(store).includes('test') ? (
<div>Has test</div>
Expand All @@ -132,7 +132,7 @@ it('should register a listener on the store object with values()', () => {
it('should register a listener on the store object with is', () => {
const {
getByText,
} = testUtils.collectAndRender(({ store }: WithStoreProp) => (
} = testUtils.collectAndRenderStrict(({ store }: WithStoreProp) => (
<div>
{'anything' in store ? (
<div>Has test</div>
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/newComponentsGetNewStore.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { render } from '@testing-library/react';
import { collect, store as globalStore, WithStoreProp } from '../../src';
import * as testUtils from '../testUtils';

globalStore.hiddenMessage = '';

Expand All @@ -21,7 +21,7 @@ const TestParentComponent = collect(({ store }: WithStoreProp) => (
</div>
));

const { getByText } = render(<TestParentComponent />);
const { getByText } = testUtils.renderStrict(<TestParentComponent />);

it('should give the new version of the store to a newly mounting component', () => {
getByText('Details are hidden');
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/propTypes.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ it('should not listen to props read from prop types', () => {
}).isRequired,
};

const { getByText } = testUtils.collectAndRender(MyComponent);
const { getByText } = testUtils.collectAndRenderStrict(MyComponent);

expect(testUtils.getAllListeners()).toEqual([
'prop1',
Expand Down Expand Up @@ -56,7 +56,7 @@ it('should warn for failed prop types', () => {
};

const consoleError = testUtils.expectToLogError(() => {
testUtils.collectAndRender(MyComponent);
testUtils.collectAndRenderStrict(MyComponent);
});

expect(consoleError).toMatch(
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/propsInheritance.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { render } from '@testing-library/react';
import { collect, store as globalStore, WithStoreProp } from '../../src';
import * as testUtils from '../testUtils';

type Props = {
visibility: string;
Expand Down Expand Up @@ -38,7 +38,7 @@ const ClassComponent = collect(RawClassComponent);
it('should update a child component not wrapped in collect()', () => {
globalStore.clickCount = 0;

const { getByText } = render(<ClassComponent />);
const { getByText } = testUtils.renderStrict(<ClassComponent />);

expect(getByText('This component should be hidden'));

Expand Down
Loading

0 comments on commit 928b194

Please sign in to comment.