Skip to content

Commit

Permalink
feat: react 19 support
Browse files Browse the repository at this point in the history
  • Loading branch information
tien committed Nov 24, 2024
1 parent a41f3f1 commit 3df6b70
Show file tree
Hide file tree
Showing 15 changed files with 301 additions and 224 deletions.
107 changes: 61 additions & 46 deletions apps/docs/react/getting-started/query.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ sidebar_position: 2

# Query

The [`useLazyLoadQuery`](/api/react/function/useLazyLoadQuery) hook allow you to read any data from chain, while maintaining updates, concurrency, caching & deduplication behind the scene for you.
The [`useQuery`](/api/react/function/useQuery) hook allow you to read any data from chain, while maintaining updates, concurrency, caching & deduplication behind the scene for you.

## Async handling

[`useLazyLoadQuery`](/api/react/function/useLazyLoadQuery) utilize React's Suspense API for data fetching & error handling.
[`useQuery`](/api/react/function/useQuery) utilize React's Suspense API for data fetching & error handling.

```tsx
function ActiveEra() {
const activeEra = useLazyLoadQuery((builder) =>
builder.readStorage("Staking", "ActiveEra", []),
const activeEra = use(
useQuery((builder) => builder.readStorage("Staking", "ActiveEra", [])),
);

return <div>Active era: {activeEra}</div>;
Expand All @@ -32,16 +32,17 @@ function App() {

## Fetching multiple data

Fetching multiple data can be done by chaining queries together, [`useLazyLoadQuery`](/api/react/function/useLazyLoadQuery) (with TypeScript) will automatically infer that you want to fetch multiple data concurrently & will return an array of data instead.
Fetching multiple data can be done by chaining queries together, [`useQuery`](/api/react/function/useQuery) (with TypeScript) will automatically infer that you want to fetch multiple data concurrently & will return an array of data instead.

```tsx
function MultiQuery() {
const [expectedBlockTime, epochDuration, proposalCount] = useLazyLoadQuery(
(builder) =>
const [expectedBlockTime, epochDuration, proposalCount] = use(
useQuery((builder) =>
builder
.getConstant("Babe", "ExpectedBlockTime")
.getConstant("Babe", "EpochDuration")
.readStorage("Treasury", "ProposalCount", []),
),
);

return (
Expand All @@ -62,18 +63,20 @@ function MultiQuery() {
Multiple queries of the same type can also be fetched using [`callApis`](/api/core/class/Query#callApis) & [`readStorages`](/api/core/class/Query#readStorages).

```tsx
const [rewards, metadatum] = useLazyLoadQuery((builder) =>
builder
.callApis("NominationPoolsApi", "pending_rewards", [
[ADDRESS_1],
[ADDRESS_2],
[ADDRESS_3],
])
.readStorages("NominationPools", "Metadata", [
[POOL_ID_1],
[POOL_ID_2],
[POOL_ID_3],
]),
const [rewards, metadatum] = use(
useQuery((builder) =>
builder
.callApis("NominationPoolsApi", "pending_rewards", [
[ADDRESS_1],
[ADDRESS_2],
[ADDRESS_3],
])
.readStorages("NominationPools", "Metadata", [
[POOL_ID_1],
[POOL_ID_2],
[POOL_ID_3],
]),
),
);
```

Expand All @@ -83,15 +86,19 @@ Result of a query can be used as variables in subsequent queries.

```tsx
function Query() {
const pools = useLazyLoadQuery((builder) =>
builder.readStorageEntries("NominationPools", "BondedPools", []),
const pools = use(
useQuery((builder) =>
builder.readStorageEntries("NominationPools", "BondedPools", []),
),
);

const poolMetadatum = useLazyLoadQuery((builder) =>
builder.readStorages(
"NominationPools",
"Metadata",
pools.map(({ keyArgs: [poolId] }) => [poolId] as const),
const poolMetadatum = use(
useQuery((builder) =>
builder.readStorages(
"NominationPools",
"Metadata",
pools.map(({ keyArgs: [poolId] }) => [poolId] as const),
),
),
);

Expand All @@ -115,23 +122,27 @@ Use a falsy value (`undefined`, `null` or `false`) to conditionally fetch data.
```ts
import { idle } from "@reactive-dot/core";

const conditionalReturn = useLazyLoadQuery((builder) =>
account === undefined
? undefined
: builder.callApi("NominationPoolsApi", "pending_rewards", [
account.address,
]),
const conditionalReturn = use(
useQuery((builder) =>
account === undefined
? undefined
: builder.callApi("NominationPoolsApi", "pending_rewards", [
account.address,
]),
),
);

// Or

const conditionalFunction = useLazyLoadQuery(
account === undefined
? undefined
: (builder) =>
builder.callApi("NominationPoolsApi", "pending_rewards", [
account.address,
]),
const conditionalFunction = use(
useQuery(
account === undefined
? undefined
: (builder) =>
builder.callApi("NominationPoolsApi", "pending_rewards", [
account.address,
]),
),
);

// Result will be `idle` if the query hasn't been executed
Expand All @@ -142,20 +153,22 @@ if (conditionalReturn === idle || conditionalFunction === idle) {

## Refreshing queries

Certain query, like runtime API calls & reading of storage entries doesn't create any subscriptions. In order to get the latest data, they must be manually refreshed with the [`useLazyLoadQueryWithRefresh`](/api/react/function/useLazyLoadQueryWithRefresh) hook.
Certain query, like runtime API calls & reading of storage entries doesn't create any subscriptions. In order to get the latest data, they must be manually refreshed with the [`useQueryWithRefresh`](/api/react/function/useQueryWithRefresh) hook.

```tsx
import { useTransition } from "react";
import { use, useTransition } from "react";

function QueryWithRefresh() {
const [isPending, startTransition] = useTransition();
const [pendingRewards, refreshPendingRewards] = useLazyLoadQueryWithRefresh(
const [pendingRewardsPromise, refreshPendingRewards] = useQueryWithRefresh(
(builder) =>
builder.callApi("NominationPoolsApi", "pending_rewards", [
ACCOUNT_ADDRESS,
]),
);

const pendingRewards = use(pendingRewardsPromise);

return (
<div>
<p>{pendingRewards.toLocaleString()}</p>
Expand All @@ -175,10 +188,12 @@ The above will refresh all refreshable data in the query. If you want to target
```tsx
function QueryWithRefresh() {
const [isPending, startTransition] = useTransition();
const [account1Rewards, account2Rewards] = useLazyLoadQuery((builder) =>
builder
.callApi("NominationPoolsApi", "pending_rewards", [ACCOUNT_ADDRESS_1])
.callApi("NominationPoolsApi", "pending_rewards", [ACCOUNT_ADDRESS_2]),
const [account1Rewards, account2Rewards] = use(
useQuery((builder) =>
builder
.callApi("NominationPoolsApi", "pending_rewards", [ACCOUNT_ADDRESS_1])
.callApi("NominationPoolsApi", "pending_rewards", [ACCOUNT_ADDRESS_2]),
),
);
const refreshAccount2Rewards = useQueryRefresher((builder) =>
builder.callApi("NominationPoolsApi", "pending_rewards", [
Expand Down
12 changes: 7 additions & 5 deletions apps/docs/react/getting-started/setup.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -145,14 +145,16 @@ export function App() {

```tsx title="my-component.tsx"
import { config } from "./config";
import { useAccounts, useLazyLoadQuery } from "@reactive-dot/react";
import { useAccounts, useQuery } from "@reactive-dot/react";

export function MyComponent() {
const accounts = useAccounts();
const [timestamp, totalIssuance] = useLazyLoadQuery((builder) =>
builder
.readStorage("Timestamp", "Now", [])
.readStorage("Balances", "TotalIssuance", []),
const [timestamp, totalIssuance] = use(
useQuery((builder) =>
builder
.readStorage("Timestamp", "Now", [])
.readStorage("Balances", "TotalIssuance", []),
),
);

return (
Expand Down
19 changes: 10 additions & 9 deletions apps/docs/react/guides/multichain.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ All hooks provide an option to specify which chain to target.
import { useBlock } from "@reactive-dot/react";

function Component() {
const polkadotBlock = useBlock({ chainId: "polkadot" });
const kusamaBlock = useBlock({ chainId: "kusama" });
const westendBlock = useBlock({ chainId: "westend" });
const polkadotBlock = use(useBlock({ chainId: "polkadot" }));
const kusamaBlock = use(useBlock({ chainId: "kusama" }));
const westendBlock = use(useBlock({ chainId: "westend" }));
}
```

Expand All @@ -126,8 +126,8 @@ function Component() {
// Since the `Bounties` pallet doesn't exist on Westend, this will:
// 1. Trigger a TypeScript error
// 2. Cause a runtime error if Westend is selected
const bountyCount = useLazyLoadQuery((builder) =>
builder.readStorage("Bounties", "BountyCount", []),
const bountyCount = use(
useQuery((builder) => builder.readStorage("Bounties", "BountyCount", [])),
);

// ...
Expand All @@ -138,9 +138,10 @@ To resolve this, you can explicitly specify the chain to query, which will overr

```tsx
function Component() {
const bountyCount = useLazyLoadQuery(
(builder) => builder.readStorage("Bounties", "BountyCount", []),
{ chainId: "polkadot" },
const bountyCount = use(
useQuery((builder) => builder.readStorage("Bounties", "BountyCount", []), {
chainId: "polkadot",
}),
);

// ...
Expand All @@ -165,7 +166,7 @@ function useBountiesChainId() {
}

function BountiesPalletRequiredComponent() {
const bountyCount = useLazyLoadQuery(
const bountyCount = useQuery(
(builder) => builder.readStorage("Bounties", "BountyCount", []),
{
// This will:
Expand Down
20 changes: 11 additions & 9 deletions apps/docs/react/guides/number-utility.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,15 @@ import {
useNativeTokenAmountFromPlanck,
} from "@reactive-dot/react";

let amount = useNativeTokenAmountFromPlanck(10_000_000_000n);
let amount = use(useNativeTokenAmountFromPlanck(10_000_000_000n));
// Or
amount = useNativeTokenAmountFromNumber(1);
amount = use(useNativeTokenAmountFromNumber(1));

console.log(amount.toLocaleString("en-NZ")); // DOT 1.00

// Partial application is also supported by omitting the planck/number value.
// Here, a conversion function will be returned instead.
const amountFromPlanck = useNativeTokenAmountFromPlanck();
const amountFromPlanck = use(useNativeTokenAmountFromPlanck());

[10_000_000_000n, 20_000_000_000n, 30_000_000_000n]
.map(amountFromPlanck)
Expand All @@ -85,15 +85,17 @@ The [`useSpendableBalance`](/api/react/function/useSpendableBalance) hook can be
```ts
import { useSpendableBalance } from "@reactive-dot/react";

const spendableBalance = useSpendableBalance(ACCOUNT_ADDRESS);
const spendableBalance = use(useSpendableBalance(ACCOUNT_ADDRESS));

console.log(spendableBalance.toLocaleString("en-NZ")); // DOT 10.00

const spendableBalances = useSpendableBalance([
ACCOUNT_ADDRESS_1,
ACCOUNT_ADDRESS_2,
ACCOUNT_ADDRESS_3,
]);
const spendableBalances = use(
useSpendableBalance([
ACCOUNT_ADDRESS_1,
ACCOUNT_ADDRESS_2,
ACCOUNT_ADDRESS_3,
]),
);

console.log(
spendableBalances.map((balance) => balance.toLocaleString("en-NZ")),
Expand Down
4 changes: 2 additions & 2 deletions examples/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
"date-fns": "^4.1.0",
"jotai-devtools": "^0.10.1",
"polkadot-api": "^1.7.6",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react": "19.0.0-rc.1",
"react-dom": "19.0.0-rc.1",
"react-error-boundary": "^4.1.1",
"react-hot-toast": "^2.4.1"
},
Expand Down
Loading

0 comments on commit 3df6b70

Please sign in to comment.