Skip to content

Commit dfbb81d

Browse files
authored
fix: Avoid stale state in singleton-handler (#151)
1 parent dca1353 commit dfbb81d

File tree

2 files changed

+21
-8
lines changed

2 files changed

+21
-8
lines changed

src/internal/singleton-handler/__tests__/create-singleton-state.test.tsx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@ import { createSingletonState } from '../index';
77

88
function setup() {
99
const state = {
10+
value: 0,
1011
// eslint-disable-next-line @typescript-eslint/no-unused-vars
1112
handler: (value: number) => {},
1213
};
1314
const useSingletonState = createSingletonState({
14-
initialState: 0,
15+
initialState: () => state.value,
1516
factory: handler => {
16-
state.handler = handler;
17+
state.handler = value => {
18+
handler(value);
19+
state.value = value;
20+
};
1721
return () => {};
1822
},
1923
});
@@ -62,3 +66,16 @@ test('should use updated value for late-rendered components', () => {
6266
expect(container).toHaveTextContent('first: 123');
6367
expect(container).toHaveTextContent('second: 123');
6468
});
69+
70+
test('should use latest value from the global state', () => {
71+
const { Demo, state } = setup();
72+
const { container, rerender } = render(<Demo id="first" />);
73+
expect(container).toHaveTextContent('first: 0');
74+
state.handler(123);
75+
expect(container).toHaveTextContent('first: 123');
76+
rerender(<></>);
77+
// value changes when there are no active listeners
78+
state.value = 456;
79+
rerender(<Demo id="first" />);
80+
expect(container).toHaveTextContent('first: 456');
81+
});

src/internal/singleton-handler/index.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,9 @@ interface SingletonStateOptions<T> {
4646

4747
export function createSingletonState<T>({ factory, initialState }: SingletonStateOptions<T>) {
4848
const useSingleton = createSingletonHandler(factory);
49-
let value = initialState;
5049
return function useSingletonState() {
51-
const [state, setState] = useState<T>(value);
52-
useSingleton(newValue => {
53-
value = newValue;
54-
setState(newValue);
55-
});
50+
const [state, setState] = useState<T>(initialState);
51+
useSingleton(setState);
5652
return state;
5753
};
5854
}

0 commit comments

Comments
 (0)