Skip to content

Commit

Permalink
Merge pull request #395 from timolins/fix-subscription-race-condition
Browse files Browse the repository at this point in the history
Fix subscription race condition
  • Loading branch information
timolins authored Feb 15, 2025
2 parents 51d78b5 + e5819fb commit 40abbf8
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 5 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/size.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ jobs:
CI_JOB_NUMBER: 1
steps:
- uses: actions/checkout@v2
- uses: pnpm/action-setup@v2.2.2
- uses: pnpm/action-setup@v4
with:
version: 7
version: 9
- uses: andresz1/size-limit-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
10 changes: 8 additions & 2 deletions src/core/store.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react';
import { useEffect, useState, useRef } from 'react';
import { DefaultToastOptions, Toast, ToastType } from './types';

const TOAST_LIMIT = 20;
Expand Down Expand Up @@ -143,15 +143,21 @@ export const defaultTimeouts: {

export const useStore = (toastOptions: DefaultToastOptions = {}): State => {
const [state, setState] = useState<State>(memoryState);
const initial = useRef(memoryState);

// TODO: Switch to useSyncExternalStore when targeting React 18+
useEffect(() => {
if (initial.current !== memoryState) {
setState(memoryState);
}
listeners.push(setState);
return () => {
const index = listeners.indexOf(setState);
if (index > -1) {
listeners.splice(index, 1);
}
};
}, [state]);
}, []);

const mergedToasts = state.toasts.map((t) => ({
...toastOptions,
Expand Down
24 changes: 23 additions & 1 deletion test/toast.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import {
render,
screen,
Expand Down Expand Up @@ -319,3 +319,25 @@ test('pause toast', async () => {

expect(toastElement).not.toBeInTheDocument();
});

test('"toast" can be called from useEffect hook', async () => {
const MyComponent = () => {
const [success, setSuccess] = useState(false);
useEffect(() => {
toast.success('Success toast');
setSuccess(true);
}, []);

return success ? <div>MyComponent finished</div> : null;
};

render(
<>
<MyComponent />
<Toaster />
</>
);

await screen.findByText(/MyComponent finished/i);
expect(screen.queryByText(/Success toast/i)).toBeInTheDocument();
});

0 comments on commit 40abbf8

Please sign in to comment.