Skip to content

Instantly share code, notes, and snippets.

@faiwer
Created September 16, 2025 17:16
Show Gist options
  • Select an option

  • Save faiwer/2d2590f0fbdaf50a1bc88581fa535bb3 to your computer and use it in GitHub Desktop.

Select an option

Save faiwer/2d2590f0fbdaf50a1bc88581fa535bb3 to your computer and use it in GitHub Desktop.
/* eslint-disable @typescript-eslint/no-non-null-assertion -- ignore it. */
/* eslint-disable @typescript-eslint/no-unused-vars -- ignore it. */
/* eslint-disable simple-import-sort/imports -- ignore it. */
import { act, renderHook, render } from '@testing-library/react';
import { useEffect, useState, useSyncExternalStore } from 'react';
it('test', () => {
const store = new Store<State>({
id: '1',
articles: { 1: { title: '1' } },
});
const subscribe = (callback: () => void) => store.subscribe(callback);
const log = globalThis.console.log.bind(console);
const Parent = () => {
const id = useSyncExternalStore(subscribe, () =>
store.get((state) => {
log('selectInParent', state);
selectInChild(state);
return state.id;
}),
);
return id ? <Child id={id} /> : null;
};
const unmountChild = jest.fn();
const selectInChild = jest.fn();
const Child = ({ id }: { id: string }) => {
const title = useSyncExternalStore(subscribe, () =>
store.get((state) => {
log(
'selectInChild',
state,
'id=',
id,
'title=',
state.articles?.[id]?.title,
);
if (!state.articles?.[id]?.title) {
console.trace();
}
selectInChild(state);
return state.articles[id].title;
}),
);
useEffect(
() => () => {
log('unmountChild');
unmountChild();
},
[],
);
return <h1>{title}</h1>;
};
const result = render(<Parent />);
expect(result.container.innerHTML).toBe('<h1>1</h1>');
expect(unmountChild).not.toHaveBeenCalled();
const selectCallCount = selectInChild.mock.calls.length;
expect(selectInChild).toHaveBeenLastCalledWith(store.get((root) => root));
act(() => store.set({ id: null, articles: {} }));
expect(result.container.innerHTML).toBe('');
expect(unmountChild).toHaveBeenCalled();
// expect(selectInChild).toHaveBeenLastCalledWith({
// renderChild: true,
// value: 1,
// });
expect(selectInChild).toHaveBeenCalledTimes(selectCallCount);
});
interface State {
id: string | null;
articles: Record<string, { title: string }>;
}
export class Store<State> {
private subscribers = new Set<() => void>();
constructor(private data: State) {}
subscribe(onStoreChange: () => void): () => void {
this.subscribers.add(onStoreChange);
return () => this.subscribers.delete(onStoreChange);
}
get<T>(selector: (data: State) => T): T {
return selector(this.data);
}
set(data: State) {
if (this.data === data) {
return;
}
this.data = data;
for (const subscriber of this.subscribers) {
subscriber();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment