Skip to content

Commit

Permalink
Simplify useImmediateEffect and add additional test
Browse files Browse the repository at this point in the history
  • Loading branch information
kitten committed Jun 4, 2019
1 parent e1bd3f3 commit e371cb4
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 10 deletions.
33 changes: 28 additions & 5 deletions src/hooks/useImmediateEffect.test.ts
@@ -1,5 +1,5 @@
import React from 'react';
import { renderHook } from 'react-hooks-testing-library';
import { renderHook, act } from 'react-hooks-testing-library';
import { useImmediateEffect } from './useImmediateEffect';

it('calls effects immediately on mount', () => {
Expand All @@ -8,14 +8,37 @@ it('calls effects immediately on mount', () => {
const effect = jest.fn();

spy.mockImplementation(useEffect);
renderHook(() => useImmediateEffect(effect, [effect]));

expect(effect).toHaveBeenCalledTimes(1);
expect(effect).toHaveBeenCalledTimes(1);
renderHook(() => {
useImmediateEffect(effect, [effect]);
expect(effect).toHaveBeenCalledTimes(1);
});

expect(effect).toHaveBeenCalledTimes(1);
expect(useEffect).toHaveBeenCalledWith(expect.any(Function), [
expect.any(Function),
]);

useEffect.mockRestore();
spy.mockRestore();
});

it('behaves like useEffect otherwise', () => {
const spy = jest.spyOn(React, 'useEffect');
const effect = jest.fn();

const { result } = renderHook(() => {
const [ref, setState] = React.useState({});
useImmediateEffect(effect, [ref]);

expect(effect).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalled();

const forceUpdate = () => setState({});
return forceUpdate;
});

act(() => result.current()); // forceUpdate
expect(effect).toHaveBeenCalledTimes(2);

spy.mockRestore();
});
11 changes: 6 additions & 5 deletions src/hooks/useImmediateEffect.ts
@@ -1,4 +1,6 @@
import { useRef, useEffect, useCallback } from 'react';
/* eslint-disable react-hooks/exhaustive-deps */

import { useRef, useEffect } from 'react';
import { noop } from '../utils';

enum LifecycleState {
Expand All @@ -16,22 +18,21 @@ export const useImmediateEffect = (
) => {
const teardown = useRef(noop);
const state = useRef(LifecycleState.WillMount);
const execute = useCallback(effect, changes);

// On initial render we just execute the effect
if (state.current === LifecycleState.WillMount) {
state.current = LifecycleState.DidMount;
teardown.current = execute();
teardown.current = effect();
}

useEffect(() => {
// Initially we skip executing the effect since we've already done so on
// initial render, then we execute it as usual
if (state.current === LifecycleState.Update) {
return (teardown.current = execute());
return (teardown.current = effect());
} else {
state.current = LifecycleState.Update;
return teardown.current;
}
}, [execute]);
}, changes);
};

0 comments on commit e371cb4

Please sign in to comment.