nemoet
Version:
Modal package with different TON wallets for React JS
226 lines (172 loc) • 12.6 kB
Markdown
in a React environment.
It consists of the `<Connect />` component, through which a connection to an organization is described, and a series of hooks: `useApp()`, `useApps()`, `useOrganization()`, `usePermissions()`.
To get started, add the `@aragon/connect-react` package to your project. It contains all the exports of the `@aragon/connect`, so you don’t have to install both.
```jsx
import {
Connect,
useApps,
useOrganization,
usePermissions,
} from '@aragon/connect-react'
function App() {
const [org, orgStatus] = useOrganization()
const [apps, appsStatus] = useApps()
const [permissions, permissionsStatus] = usePermissions()
const loading =
orgStatus.loading || appsStatus.loading || permissionsStatus.loading
const error = orgStatus.error || appsStatus.error || permissionsStatus.error
if (loading) {
return <p>Loading…</p>
}
if (error) {
return <p>Error: {error.message}</p>
}
return (
<>
<h1>{org.name}</h1>
<h2>Apps</h2>
<ul>
{apps.map((app, i) => (
<li key={i}>{app.name}</li>
))}
</ul>
<h2>Permissions</h2>
<ul>
{permissions.map((permission, i) => (
<li key={i}>{String(permission)}</li>
))}
</ul>
</>
)
}
ReactDOM.render(
<Connect location="myorg.aragonid.eth" connector="thegraph">
<App />
</Connect>,
document.querySelector('main')
)
```
This component is required in order to use the provided hooks.
| Props | Type | Description |
| ------------------ | ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `location` | `String` | The Ethereum address or ENS domain of an Aragon organization. |
| `connector` | `Connector` or `[String, Object]` or `String` | Accepts a `Connector` instance, and either a string or a tuple for embedded connectors and their config. |
| `options` | `Object` | The optional configuration object. |
| `options.ethereum` | `EthereumProvider` | An [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) compatible object. |
| `options.network` | [`Networkish`](../api-reference/types.md
| Props | Type | Description |
| ------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| returns | `[Organization \| null, { loading: boolean, error: null \| Error, retry: Function }]` | An array containing the [organization](../api-reference/organization.md) and a loading status object. |
| Name | Type | Description |
| ------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `appFilter` | `String` or `Object` \(optional\) | When a string is passed, the app will get searched by address if it starts by `0x`, and by `appName` otherwise. See `appFilter.address` and `appFilter.appName` to set them explicitly. For the time being, only one type of filter can get passed at a time. |
| `appFilter.address` | `String` | Same as `appFilter`, but makes the selection by `address` explicit. |
| `appFilter.appName` | `String` | Same as `appFilter`, but makes the selection by `appName` explicit. |
| returns | `[App \| null, { loading: boolean, error: null \| Error, retry: Function }]` | An array containing a single [app](../api-reference/app.md) from the organization and a loading status object. |
| Name | Type | Description |
| ------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `appFilter` | `String` or `String[]` or object (optional) | When a string is passed, apps will get filtered by address if it starts by `0x`, and by `appName` otherwise. When an array is passed, the first entry determines the type of filter. See `appFilter.address` and `appFilter.appName` to set them explicitly. For the time being, only one type of filter can get passed at a time. |
| `appFilter.address` | `String` or `String[]` | Same as `appFilter`, but makes the selection by `address` explicit. |
| `appFilter.appName` | `String` or `String[]` | Same as `appFilter`, but makes the selection by `appName` explicit. |
| returns | `[App[], { loading: boolean, error: null \| Error, retry: Function }]` | An array containing the organization [apps](../api-reference/app.md) and a loading status object. |
| Name | Type | Description |
| ------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| returns | `[Permission[], { loading: boolean, error: null \| Error, retry: Function }]` | An array containing the organization [permissions](../api-reference/permission.md) and a loading status object. |
This utility function makes app connectors available in your React app.
This is how it works at the most basic level:
```jsx
import { createAppHook, useApp } from '@aragon/connect-react'
import connectVoting from '@aragon/connect-voting'
// We create a hook corresponding to the app connector. This is usually enough,
// since the app connector will inherit from the connection set on <Connect />.
const useVoting = createAppHook(connectVoting)
function Votes() {
const [voting] = useApp('voting')
// And this is how we can use it, by passing the app instance and a callback.
const [votes] = useVoting(voting, (app) => app.votes())
return (
<ul>
{votes ? (
votes.map((vote) => <li key={vote.id}>{formatVote(vote)}</li>)
) : (
<li>Loading votes…</li>
)}
</ul>
)
}
```
By default, the callback will be called once, and never update afterwards. This can be a problem if you want to reload data depending on the current state. This is why the hook also accept a dependency parameter. It behaves in a very similar way to the `useEffect()` or `useMemo()` hooks, except that it doesn’t update the callback when omitted.
This is how you can use it:
```jsx
import connect from '@aragon/connect'
import connectVoting from '@aragon/connect-voting'
import { createAppHook, useApp } from '@aragon/connect-react'
const useVoting = createAppHook(connectVoting)
function App() {
const [page, setPage] = useState(0)
const [voting] = useApp('voting')
const [votes] = useVoting(
voting,
(app) => app.votes({ first: 10, skip: 10 * page }),
[ ]
)
return (
<div>
<ul>
{votes ? (
votes.map((vote) => <li key={vote.id}>{formatVote(vote)}</li>)
) : (
<li>Loading votes…</li>
)}
</ul>
<button onClick={() => setPage(page + 1)}>prev</button>
<button onClick={() => setPage(page - 1)}>next</button>
</div>
)
}
```
An issue with the previous examples is that we only fetch the data once, instead of receiving updates from it. For example, someone might create a new vote, and it is reasonable to expect an app to reflect that. With the app connectors API, you generally have `onX` equivalents of the async methods, like `votes(filters)` and `onVotes(filters, callback)`.
Using them with `createAppHook()` hooks requires to call the `onX` equivalent of the async method you want to use, but without passing a callback. App connectors return a partially applied function when the callback is omitted, which `createAppHook()` takes advantage of by entirely managing the subscription.
```jsx
import connect from '@aragon/connect'
import connectVoting from '@aragon/connect-voting'
import { createAppHook, useApp } from '@aragon/connect-react'
const useVoting = createAppHook(connectVoting)
function App() {
const [page, setPage] = useState(0)
const [voting] = useApp('voting')
// votes will now get updated automatically
const [votes] = useVoting(
voting,
// Note that we are now using onVotes() rather than votes()
(app) => app.onVotes({ first: 10, skip: 10 * page }),
// When page changes, a new subscription will replace the previous one
[ ]
)
return (
<div>
<ul>
{votes ? (
votes.map((vote) => <li key={vote.id}>{formatVote(vote)}</li>)
) : (
<li>Loading votes…</li>
)}
</ul>
<button onClick={() => setPage(page + 1)}>prev</button>
<button onClick={() => setPage(page - 1)}>next</button>
</div>
)
}
```
Aragon Connect provides a series of utilities that simplify the usage of Aragon Connect