Skip to content

Commit

Permalink
feat: reactive chains (#3580)
Browse files Browse the repository at this point in the history
* feat: runtime set chains

* feat: update chains

* test: add

* docs: add

* chore: snaps

* chore: changeset
  • Loading branch information
tmm authored Feb 8, 2024
1 parent ed70e03 commit c677dcd
Show file tree
Hide file tree
Showing 28 changed files with 460 additions and 39 deletions.
5 changes: 5 additions & 0 deletions .changeset/friendly-mails-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wagmi": patch
---

Made `useSwitchChain().chains` reactive.
6 changes: 6 additions & 0 deletions .changeset/scary-whales-tiptoe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@wagmi/core": patch
---

Updated internals.

2 changes: 2 additions & 0 deletions docs/.vitepress/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ export function getSidebar() {
},
{ text: 'useCall', link: '/react/api/hooks/useCall' },
{ text: 'useChainId', link: '/react/api/hooks/useChainId' },
{ text: 'useChains', link: '/react/api/hooks/useChains' },
{ text: 'useClient', link: '/react/api/hooks/useClient' },
{ text: 'useConfig', link: '/react/api/hooks/useConfig' },
{ text: 'useConnect', link: '/react/api/hooks/useConnect' },
Expand Down Expand Up @@ -493,6 +494,7 @@ export function getSidebar() {
link: '/core/api/actions/getBytecode',
},
{ text: 'getChainId', link: '/core/api/actions/getChainId' },
{ text: 'getChains', link: '/core/api/actions/getChains' },
{
text: 'getClient',
link: '/core/api/actions/getClient',
Expand Down
31 changes: 31 additions & 0 deletions docs/core/api/actions/getChains.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# getChains

Action for getting configured chains.

## Import

```ts
import { getChains } from '@wagmi/core'
```

## Usage

::: code-group
```ts [index.ts]
import { getChains } from '@wagmi/core'
import { config } from './config'

const chains = getChains(config)
```
<<< @/snippets/core/config.ts[config.ts]
:::

## Return Type

```ts
import { type GetChainsReturnType } from '@wagmi/core'
```

`readonly [Chain, ...Chain[]]`

Chains from [`config.chains`](/core/api/createConfig#chains).
67 changes: 67 additions & 0 deletions docs/react/api/hooks/useChains.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
title: useChains
description: Hook for getting configured chains
---

# useChains

Hook for getting configured chains

## Import

```ts
import { useChains } from 'wagmi'
```

## Usage

::: code-group
```tsx [index.tsx]
import { useChains } from 'wagmi'

function App() {
const chains = useChains()
}
```
<<< @/snippets/react/config.ts[config.ts]
:::

## Parameters

```ts
import { type UseChainsParameters } from 'wagmi'
```

### config

`Config | undefined`

[`Config`](/react/api/createConfig#config) to use instead of retrieving from the from nearest [`WagmiProvider`](/react/api/WagmiProvider).

::: code-group
```tsx [index.tsx]
import { useChains } from 'wagmi'
import { config } from './config' // [!code focus]
function App() {
const chains = useChains({
config, // [!code focus]
})
}
```
<<< @/snippets/react/config.ts[config.ts]
:::

## Return Type

```ts
import { type UseChainsReturnType } from 'wagmi'
```

`readonly [Chain, ...Chain[]]`

Chains from [`config.chains`](/react/api/createConfig#chains).

## Action

- [`getChains`](/core/api/actions/getChains)
12 changes: 12 additions & 0 deletions packages/core/src/actions/getChains.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { chain, config } from '@wagmi/test'
import { type Chain } from 'viem'
import { expectTypeOf, test } from 'vitest'

import { getChains } from './getChains.js'

test('default', async () => {
const chains = getChains(config)
expectTypeOf(chains[0]).toEqualTypeOf<Chain | typeof chain.mainnet>()
expectTypeOf(chains[2]).toEqualTypeOf<Chain | typeof chain.optimism>()
expectTypeOf(chains[3]).toEqualTypeOf<Chain | undefined>()
})
14 changes: 14 additions & 0 deletions packages/core/src/actions/getChains.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { chain, config } from '@wagmi/test'
import { expect, test } from 'vitest'

import { getChains } from './getChains.js'

test('default', async () => {
expect(getChains(config)).toEqual([
chain.mainnet,
chain.mainnet2,
chain.optimism,
])
config._internal.chains.setState([chain.mainnet, chain.mainnet2])
expect(getChains(config)).toEqual([chain.mainnet, chain.mainnet2])
})
20 changes: 20 additions & 0 deletions packages/core/src/actions/getChains.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { type Chain } from 'viem'
import { type Config } from '../createConfig.js'
import { deepEqual } from '../utils/deepEqual.js'

export type GetChainsReturnType<config extends Config = Config> =
| config['chains']
| readonly [Chain, ...Chain[]]

let previousChains: readonly Chain[] = []

/** https://wagmi.sh/core/api/actions/getChains */
export function getChains<config extends Config>(
config: config,
): GetChainsReturnType<config> {
const chains = config.chains
if (deepEqual(previousChains, chains))
return previousChains as GetChainsReturnType
previousChains = chains
return chains
}
37 changes: 37 additions & 0 deletions packages/core/src/actions/watchChains.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { chain, config } from '@wagmi/test'
import { type Chain } from 'viem'
import { expect, test } from 'vitest'

import { watchChains } from './watchChains.js'

test('default', async () => {
let chains: readonly [Chain, ...Chain[]] = config.chains
const unwatch = watchChains(config, {
onChange(nextChains) {
chains = nextChains
},
})

config._internal.chains.setState([chain.mainnet, chain.mainnet2])
expect(chains.map((x) => x.id)).toMatchInlineSnapshot(`
[
1,
456,
]
`)

config._internal.chains.setState([
chain.mainnet,
chain.mainnet2,
chain.optimism,
])
expect(chains.map((x) => x.id)).toMatchInlineSnapshot(`
[
1,
456,
10,
]
`)

unwatch()
})
26 changes: 26 additions & 0 deletions packages/core/src/actions/watchChains.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { type Config } from '../createConfig.js'
import { type GetChainsReturnType } from './getChains.js'

export type WatchChainsParameters<config extends Config = Config> = {
onChange(
chains: GetChainsReturnType<config>,
prevChains: GetChainsReturnType<config>,
): void
}

export type WatchChainsReturnType = () => void

/**
* @internal
* We don't expose this because as far as consumers know, you can't chainge (lol) `config.chains` at runtime.
* Setting `config.chains` via `config._internal.chains.setState(...)` is an extremely advanced use case that's not worth documenting or supporting in the public API at this time.
*/
export function watchChains<config extends Config>(
config: config,
parameters: WatchChainsParameters<config>,
): WatchChainsReturnType {
const { onChange } = parameters
return config._internal.chains.subscribe((chains, prevChains) => {
onChange(chains, prevChains)
})
}
Loading

0 comments on commit c677dcd

Please sign in to comment.