Skip to content

Commit 9183126

Browse files
committed
fix(autoeffect): fix batching for auto effects
1 parent 88b8563 commit 9183126

File tree

2 files changed

+74
-5
lines changed

2 files changed

+74
-5
lines changed

__tests__/batching.test.jsx

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
import React, { Component } from 'react';
22
import { render, cleanup, act } from '@testing-library/react/pure';
3-
// eslint-disable-next-line import/no-unresolved
4-
import { view, store, batch } from 'react-easy-state';
3+
import {
4+
view,
5+
store,
6+
autoEffect,
7+
clearEffect,
8+
batch,
9+
// eslint-disable-next-line import/no-unresolved
10+
} from 'react-easy-state';
511

612
describe('batching', () => {
713
afterEach(cleanup);
814

9-
test('should batch state changes inside a batch() wrapper', () => {
15+
test('should batch state changes inside a batch() wrapper for views', () => {
1016
let renderCount = 0;
1117
const person = store({ name: 'Bob' });
1218
const MyComp = view(() => {
@@ -27,6 +33,62 @@ describe('batching', () => {
2733
expect(renderCount).toBe(2);
2834
});
2935

36+
test('should batch state changes inside a batch() wrapper for global autoEffects', () => {
37+
let numOfRuns = 0;
38+
let name = '';
39+
40+
const person = store({ name: 'Bob' });
41+
const effect = autoEffect(() => {
42+
numOfRuns += 1;
43+
name = person.name;
44+
});
45+
46+
expect(name).toBe('Bob');
47+
expect(numOfRuns).toBe(1);
48+
49+
batch(() => {
50+
person.name = 'Ann';
51+
person.name = 'Rick';
52+
});
53+
54+
expect(name).toBe('Rick');
55+
expect(numOfRuns).toBe(2);
56+
57+
clearEffect(effect);
58+
});
59+
60+
test('should batch state changes inside a batch() wrapper for local autoEffects', () => {
61+
let effectCount = 0;
62+
let renderCount = 0;
63+
let name = '';
64+
65+
const person = store({ name: 'Bob' });
66+
const MyComp = view(() => {
67+
renderCount += 1;
68+
autoEffect(() => {
69+
effectCount += 1;
70+
name = person.name;
71+
});
72+
return <div>{person.name}</div>;
73+
});
74+
75+
const { container } = render(<MyComp />);
76+
expect(container).toHaveTextContent('Bob');
77+
expect(name).toBe('Bob');
78+
expect(effectCount).toBe(1);
79+
expect(renderCount).toBe(1);
80+
81+
batch(() => {
82+
person.name = 'Ann';
83+
person.name = 'Rick';
84+
});
85+
86+
expect(name).toBe('Rick');
87+
expect(container).toHaveTextContent('Rick');
88+
expect(effectCount).toBe(2);
89+
expect(renderCount).toBe(2);
90+
});
91+
3092
test('should batch state changes inside a batch() wrapper in a class component', () => {
3193
let renderCount = 0;
3294
const person = store({ name: 'Bob' });

src/autoEffect.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useEffect } from 'react';
22
import { observe, unobserve } from '@nx-js/observer-util';
3+
import { queue } from './queue';
34

45
import {
56
isInsideFunctionComponent,
@@ -10,7 +11,9 @@ import {
1011
export function autoEffect(fn, deps = []) {
1112
if (isInsideFunctionComponent) {
1213
return useEffect(() => {
13-
const observer = observe(fn);
14+
const observer = observe(fn, {
15+
scheduler: () => queue.add(observer),
16+
});
1417
return () => unobserve(observer);
1518
}, deps);
1619
}
@@ -24,7 +27,11 @@ export function autoEffect(fn, deps = []) {
2427
'You cannot use autoEffect inside a render of a class component. Please use it in the constructor or lifecycle methods instead.',
2528
);
2629
}
27-
return observe(fn);
30+
31+
const observer = observe(fn, {
32+
scheduler: () => queue.add(observer),
33+
});
34+
return observer;
2835
}
2936

3037
export { unobserve as clearEffect };

0 commit comments

Comments
 (0)