-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(react): pauseable subscription (#525)
- Loading branch information
Showing
30 changed files
with
544 additions
and
151 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@reactive-dot/react": minor | ||
--- | ||
|
||
Added the ability to pause subscriptions via `<QueryOptionsProvider active={false} />` context. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
node_modules | ||
coverage | ||
*.tsbuildinfo | ||
.yarn/* | ||
!.yarn/patches | ||
.nx/cache | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
--- | ||
sidebar_position: 3 | ||
--- | ||
|
||
# Query performance | ||
|
||
All query hooks in ReactiveDOT create subscriptions to the underlying data sources. These subscriptions ensure that your components always reflect the latest data changes. However, it's important to manage these subscriptions effectively to avoid performance issues. | ||
|
||
## Subscription lifecycle | ||
|
||
A new subscription is created the first time a query hook is mounted. When the last component that uses the query unmounts, the subscription is automatically cleaned up. Therefore, it's crucial to ensure components unmount when they are no longer needed. | ||
|
||
## Unmounting components | ||
|
||
Always remember to unmount components when they are no longer visible or required. This is the most straightforward way to manage subscriptions and prevent unnecessary data fetching and updates. | ||
|
||
## Controlling subscriptions | ||
|
||
In some scenarios, you might not want to unmount components (e.g., in an infinite scroll implementation where you only want active subscriptions on visible elements). In such cases, you can use the [`QueryOptionsProvider`](/api/react/function/QueryOptionsProvider) component to control subscription behavior. | ||
|
||
The `QueryOptionsProvider` allows you to disable subscriptions for specific parts of your application. This can be useful for optimizing performance when dealing with a large number of components. | ||
|
||
```tsx | ||
import { QueryOptionsProvider } from "@reactive-dot/react"; | ||
|
||
export default function ParentComponent() { | ||
// Hook implementation not included for brevity | ||
const isVisible = useIsVisible(); | ||
|
||
return ( | ||
<QueryOptionsProvider active={isVisible}> | ||
<YourComponent /> | ||
</QueryOptionsProvider> | ||
); | ||
} | ||
``` |
2 changes: 1 addition & 1 deletion
2
apps/docs/react/guides/usinng-papi.md → apps/docs/react/guides/using-papi.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
--- | ||
sidebar_position: 3 | ||
sidebar_position: 4 | ||
--- | ||
|
||
# Using Polkadot-API (PAPI) | ||
|
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { createContext, type PropsWithChildren } from "react"; | ||
|
||
export const SubscriptionContext = createContext({ active: true }); | ||
|
||
type SubscriptionProps = PropsWithChildren<{ | ||
active: boolean; | ||
}>; | ||
|
||
/** | ||
* React context to control subscription options. | ||
* | ||
* @param props - Component props | ||
* @returns React element | ||
*/ | ||
export function QueryOptionsProvider({ active, children }: SubscriptionProps) { | ||
return ( | ||
<SubscriptionContext value={{ active }}>{children}</SubscriptionContext> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { QueryOptionsProvider } from "../contexts/query-options.js"; | ||
import { atomWithObservableAndPromise } from "../utils/jotai/atom-with-observable-and-promise.js"; | ||
import { usePausableAtomValue } from "./use-pausable-atom-value.js"; | ||
import { renderHook } from "@testing-library/react"; | ||
import { useAtomValue } from "jotai"; | ||
import { atomWithObservable } from "jotai/utils"; | ||
import { act, type PropsWithChildren } from "react"; | ||
import { BehaviorSubject } from "rxjs"; | ||
import { expect, it } from "vitest"; | ||
|
||
it("should return observable atom when subscription is set to active", async () => { | ||
const subject = new BehaviorSubject("initial"); | ||
const valueAtom = atomWithObservableAndPromise(() => subject); | ||
|
||
const { result } = await act(() => | ||
renderHook(() => usePausableAtomValue(valueAtom), { | ||
wrapper: ({ children }) => ( | ||
<QueryOptionsProvider active>{children}</QueryOptionsProvider> | ||
), | ||
}), | ||
); | ||
|
||
expect(result.current).toBe("initial"); | ||
|
||
act(() => subject.next("updated")); | ||
|
||
expect(result.current).toBe("updated"); | ||
}); | ||
|
||
it("should return promise atom when subscription is set to inactive", async () => { | ||
const subject = new BehaviorSubject("initial"); | ||
const valueAtom = atomWithObservableAndPromise(() => subject); | ||
|
||
const { result } = await act(() => | ||
renderHook(() => usePausableAtomValue(valueAtom), { | ||
wrapper: ({ children }) => ( | ||
<QueryOptionsProvider active={false}>{children}</QueryOptionsProvider> | ||
), | ||
}), | ||
); | ||
|
||
expect(result.current).toBe("initial"); | ||
|
||
act(() => subject.next("updated")); | ||
|
||
expect(result.current).toBe("initial"); | ||
}); | ||
|
||
it("should change from inactive to active when subscription status change", async () => { | ||
const activeSubject = new BehaviorSubject(false); | ||
const activeAtom = atomWithObservable(() => activeSubject); | ||
|
||
const subject = new BehaviorSubject("initial"); | ||
const valueAtom = atomWithObservableAndPromise(() => subject); | ||
|
||
const Wrapper = ({ children }: PropsWithChildren) => ( | ||
<QueryOptionsProvider active={useAtomValue(activeAtom)}> | ||
{children} | ||
</QueryOptionsProvider> | ||
); | ||
|
||
const { result } = await act(() => | ||
renderHook(() => usePausableAtomValue(valueAtom), { | ||
wrapper: Wrapper, | ||
}), | ||
); | ||
|
||
expect(result.current).toBe("initial"); | ||
|
||
act(() => subject.next("updated")); | ||
|
||
expect(result.current).toBe("initial"); | ||
|
||
act(() => activeSubject.next(true)); | ||
|
||
expect(result.current).toBe("updated"); | ||
}); |
Oops, something went wrong.