// For whatever reason, we need to mock this *and* use RTR._Scheduler below. Why? Who knows.
jest.mock('scheduler', () => require.requireActual('scheduler/unstable_mock'));
const React = require('react');
const ReactTestRenderer = require('react-test-renderer');
const {useReducerWithEmitEffect, emitEffect} = require('./useReducerWithEmitEffect.js');
let _state;
let _dispatch;
let _log;
beforeEach(() => {
_state = _dispatch = undefined;
_log = [];
});
function Foo() {
React.useLayoutEffect(() => {
_log.push('commit');
});
let [state, dispatch] = useReducerWithEmitEffect(function(state, action) {
let calculation = `${state} + ${action} = ${state + action}`;
_log.push(`reduce: ${calculation}`);
emitEffect(() => {
_log.push(`effect: ${calculation}`);
});
return state + action;
}, 0);
_state = state;
_dispatch = dispatch;
return state;
}
it('initializes', () => {
const root = ReactTestRenderer.create(
,
{unstable_isConcurrent: true},
);
ReactTestRenderer._Scheduler.unstable_flushWithoutYielding();
expect(_state).toBe(0);
expect(_log).toEqual(['commit']);
_log.length = 0;
});
it('dispatches', () => {
const root = ReactTestRenderer.create(
,
{unstable_isConcurrent: true},
);
ReactTestRenderer._Scheduler.unstable_flushWithoutYielding();
expect(_state).toBe(0);
expect(_log).toEqual(['commit']);
_log.length = 0;
ReactTestRenderer.act(() => {
_dispatch(1);
// Initial effect run eagerly
expect(_log).toEqual([
'reduce: 0 + 1 = 1',
]);
_log.length = 0;
});
ReactTestRenderer._Scheduler.unstable_flushWithoutYielding();
expect(_state).toBe(1);
expect(_log).toEqual([
'reduce: 0 + 1 = 1',
'commit',
'effect: 0 + 1 = 1',
]);
});
it('does two in series', () => {
const root = ReactTestRenderer.create(
,
{unstable_isConcurrent: true},
);
ReactTestRenderer._Scheduler.unstable_flushWithoutYielding();
expect(_state).toBe(0);
expect(_log).toEqual(['commit']);
_log.length = 0;
ReactTestRenderer.act(() => {
_dispatch(1);
// Initial effect run eagerly
expect(_log).toEqual([
'reduce: 0 + 1 = 1',
]);
_log.length = 0;
});
ReactTestRenderer._Scheduler.unstable_flushWithoutYielding();
expect(_state).toBe(1);
expect(_log).toEqual([
'reduce: 0 + 1 = 1',
'commit',
'effect: 0 + 1 = 1',
]);
_log.length = 0;
ReactTestRenderer.act(() => {
_dispatch(2);
// Why doesn't this one also run eagerly? I might've screwed up the
// scheduler mock somehow.
expect(_log).toEqual([
// 'reduce: 1 + 2 = 3',
]);
_log.length = 0;
});
ReactTestRenderer._Scheduler.unstable_flushWithoutYielding();
expect(_state).toBe(3);
expect(_log).toEqual([
'reduce: 1 + 2 = 3',
'commit',
'effect: 1 + 2 = 3',
]);
});
it('does two at once', () => {
const root = ReactTestRenderer.create(
,
{unstable_isConcurrent: true},
);
ReactTestRenderer._Scheduler.unstable_flushWithoutYielding();
expect(_state).toBe(0);
expect(_log).toEqual(['commit']);
_log.length = 0;
ReactTestRenderer.act(() => {
_dispatch(1);
_dispatch(2);
// Initial effect run eagerly
expect(_log).toEqual([
'reduce: 0 + 1 = 1',
]);
_log.length = 0;
});
ReactTestRenderer._Scheduler.unstable_flushWithoutYielding();
expect(_state).toBe(3);
expect(_log).toEqual([
'reduce: 0 + 1 = 1',
'reduce: 1 + 2 = 3',
'commit',
'effect: 0 + 1 = 1',
'effect: 1 + 2 = 3',
]);
});
it('does low and hi pri', () => {
const root = ReactTestRenderer.create(
,
{unstable_isConcurrent: true},
);
ReactTestRenderer._Scheduler.unstable_flushWithoutYielding();
expect(_state).toBe(0);
expect(_log).toEqual(['commit']);
_log.length = 0;
ReactTestRenderer.act(() => {
_dispatch(1);
// Initial effect run eagerly
expect(_log).toEqual([
'reduce: 0 + 1 = 1',
]);
_log.length = 0;
});
root.unstable_flushSync(() => {
_dispatch(2);
});
// Only the hi-pri update runs, and no effects happen
expect(_log).toEqual([
'reduce: 0 + 2 = 2',
'commit',
]);
_log.length = 0;
ReactTestRenderer._Scheduler.unstable_flushWithoutYielding();
expect(_state).toBe(3);
expect(_log).toEqual([
'reduce: 0 + 1 = 1',
'reduce: 1 + 2 = 3',
'commit',
'effect: 0 + 1 = 1',
'effect: 1 + 2 = 3',
]);
});