Skip to content

Commit

Permalink
feat(core): support react 18 strict mode (algolia/react-instantsearch…
Browse files Browse the repository at this point in the history
  • Loading branch information
dhayab authored Oct 13, 2022
1 parent cf5761a commit 2fa3768
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import createConnector, {
createConnectorWithoutContext,
} from '../createConnector';
import { InstantSearchProvider } from '../context';
import { wait } from '../../../../../test/utils';

Enzyme.configure({ adapter: new Adapter() });

Expand Down Expand Up @@ -415,7 +416,7 @@ describe('createConnector', () => {
expect(subscribe).toHaveBeenCalledTimes(1);
});

it('unsubscribes from the store on unmount', () => {
it('unsubscribes from the store on unmount', async () => {
const Connected = createConnectorWithoutContext({
displayName: 'Connector',
getProvidedProps: () => {},
Expand All @@ -435,6 +436,8 @@ describe('createConnector', () => {

wrapper.unmount();

await wait(0);

expect(unsubscribe).toHaveBeenCalledTimes(1);
});

Expand Down Expand Up @@ -787,7 +790,7 @@ describe('createConnector', () => {
expect(onSearchStateChange).not.toHaveBeenCalled();
});

it('unregisters itself on unmount', () => {
it('unregisters itself on unmount', async () => {
const Connected = createConnectorWithoutContext({
displayName: 'Connector',
getProvidedProps: () => {},
Expand All @@ -808,10 +811,12 @@ describe('createConnector', () => {

wrapper.unmount();

await wait(0);

expect(unregisterWidget).toHaveBeenCalledTimes(1);
});

it('calls onSearchStateChange with cleanUp on unmount', () => {
it('calls onSearchStateChange with cleanUp on unmount', async () => {
const cleanUp = jest.fn(function (props, searchState) {
return {
instanceProps: this.props,
Expand Down Expand Up @@ -858,6 +863,8 @@ describe('createConnector', () => {

wrapper.unmount();

await wait(0);

expect(cleanUp).toHaveBeenCalledTimes(1);
expect(onSearchStateChange).toHaveBeenCalledTimes(1);
expect(onSearchStateChange).toHaveBeenCalledWith({
Expand All @@ -875,7 +882,7 @@ describe('createConnector', () => {
});
});

it('calls onSearchStateChange with cleanUp without empty keys on unmount', () => {
it('calls onSearchStateChange with cleanUp without empty keys on unmount', async () => {
const cleanUp = jest.fn((_, searchState) => searchState);

const Connected = createConnectorWithoutContext({
Expand Down Expand Up @@ -910,6 +917,8 @@ describe('createConnector', () => {

wrapper.unmount();

await wait(0);

expect(onSearchStateChange).toHaveBeenCalledWith({
query: 'hello',
});
Expand Down
48 changes: 28 additions & 20 deletions packages/react-instantsearch-core/src/core/createConnector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export function createConnectorWithoutContext(
unsubscribe?: () => void;
unregisterWidget?: () => void;

cleanupTimerRef: ReturnType<typeof setTimeout> | null = null;
isUnmounting = false;

state: ConnectorState = {
Expand All @@ -126,6 +127,11 @@ export function createConnectorWithoutContext(
}

componentDidMount() {
if (this.cleanupTimerRef) {
clearTimeout(this.cleanupTimerRef);
this.cleanupTimerRef = null;
}

this.unsubscribe = this.props.contextValue.store.subscribe(() => {
if (!this.isUnmounting) {
this.setState({
Expand Down Expand Up @@ -193,32 +199,34 @@ export function createConnectorWithoutContext(
}

componentWillUnmount() {
this.isUnmounting = true;
this.cleanupTimerRef = setTimeout(() => {
this.isUnmounting = true;

if (this.unsubscribe) {
this.unsubscribe();
}
if (this.unsubscribe) {
this.unsubscribe();
}

if (this.unregisterWidget) {
this.unregisterWidget();
if (this.unregisterWidget) {
this.unregisterWidget();

if (typeof connectorDesc.cleanUp === 'function') {
const nextState = connectorDesc.cleanUp.call(
this,
this.props,
this.props.contextValue.store.getState().widgets
);
if (typeof connectorDesc.cleanUp === 'function') {
const nextState = connectorDesc.cleanUp.call(
this,
this.props,
this.props.contextValue.store.getState().widgets
);

this.props.contextValue.store.setState({
...this.props.contextValue.store.getState(),
widgets: nextState,
});
this.props.contextValue.store.setState({
...this.props.contextValue.store.getState(),
widgets: nextState,
});

this.props.contextValue.onSearchStateChange(
removeEmptyKey(nextState)
);
this.props.contextValue.onSearchStateChange(
removeEmptyKey(nextState)
);
}
}
}
});
}

getProvidedProps(props) {
Expand Down
12 changes: 10 additions & 2 deletions packages/react-instantsearch-core/src/widgets/InstantSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ class InstantSearch extends Component<Props, State> {
};
}

cleanupTimerRef: ReturnType<typeof setTimeout> | null = null;
isUnmounting: boolean = false;

constructor(props: Props) {
Expand Down Expand Up @@ -235,6 +236,11 @@ class InstantSearch extends Component<Props, State> {
}

componentDidMount() {
if (this.cleanupTimerRef) {
clearTimeout(this.cleanupTimerRef);
this.cleanupTimerRef = null;
}

if (isMetadataEnabled()) {
injectMetadata(
this.state.instantSearchManager.widgetsManager.getWidgets(),
Expand All @@ -244,8 +250,10 @@ class InstantSearch extends Component<Props, State> {
}

componentWillUnmount() {
this.isUnmounting = true;
this.state.instantSearchManager.skipSearch();
this.cleanupTimerRef = setTimeout(() => {
this.isUnmounting = true;
this.state.instantSearchManager.skipSearch();
});
}

createHrefForState(searchState: SearchState) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
import createInstantSearchManager from '../../core/createInstantSearchManager';
import InstantSearch from '../InstantSearch';
import { InstantSearchConsumer } from '../../core/context';
import { wait } from '../../../../../test/utils';

Enzyme.configure({ adapter: new Adapter() });

Expand Down Expand Up @@ -330,7 +331,7 @@ describe('InstantSearch', () => {
expect(childContext.widgetsManager).toBe(ism.widgetsManager);
});

it('onSearchStateChange should not be called and search should be skipped if the widget is unmounted', () => {
it('onSearchStateChange should not be called and search should be skipped if the widget is unmounted', async () => {
const ism = createFakeInstantSearchManager();
let childContext;
createInstantSearchManager.mockImplementation(() => ism);
Expand All @@ -350,6 +351,9 @@ describe('InstantSearch', () => {
);

wrapper.unmount();

await wait(0);

childContext.onSearchStateChange({});

expect(onSearchStateChangeMock).toHaveBeenCalledTimes(0);
Expand Down

0 comments on commit 2fa3768

Please sign in to comment.