A reactive library for building Substrate front-end
**NOTE**: Polkassembly doesn't do code highlighting, so a much easier to read version can be found [here](https://hackmd.io/wUbQpWBqTFaPD-I6D\_O6Mw?view) instead. # Proposal for `reactive-dot`: A reactive library for building Substrate front-end ## Overview I'm [Tien](https://github.com/tien) and was a lead front-end developer working in the Polkadot space for the past 1.5 years. Now I'm going independent and I'd like to solve some developer experience issues I encountered along the way. Prior to my involvement with Polkadot, I've worked on various projects within other ecosystems, namely Ethereum & Cosmos. This library came about based on my experience building out front-ends with Polkadot.js, and comparing it with the developer experience available in Ethereum, realizing the same wasn't available for Polkadot. This proposal outlines the creation of `reactive-dot`, a comprehensive React library designed to simplify and streamline the integration of Polkadot network functionalities into React applications. Inspired by the popular Wagmi library for Ethereum, `reactive-dot` aims to provide developers with an easy-to-use, modular, and flexible toolkit for interacting with the Polkadot ecosystem. ## Objectives 1. **Simplify Development**: Provide a set of intuitive React hooks to facilitate Polkadot network interactions, making it accessible for developers of all skill levels. 2. **Enhance Developer Experience**: Reduce the boilerplate code and complexity involved in integrating Polkadot, allowing developers to focus on building robust applications. 3. **Promote Adoption**: Encourage the adoption of Polkadot by lowering the entry barrier for developers through improved tooling. ## Key features for the first version ### Multichain support Easy chain selection via React context. ```tsx const Root = () => ( ); ``` Or via options override ```ts const account = useQueryStorage("system", "account", [accountAddress], { genesisHash: "0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe", }); ``` ### Reading of storage Access and read data stored in the Substrate-based storage directly from your React components. ```ts // Reading single value // this value is live from chain and will be updated automatically const totalIssuance = useQueryStorage("balances", "totalIssuance", []); console.log("Total issuance:", totalIssuance.toHuman()); // Reading multiple values const poolMetadatum = useQueryStorage( "nominationPools", "metadata", [0, 1, 2, 3], { multi: true, }, ); for (const poolMetadata of poolMetadatum) { console.log("Pool name:", poolMetadata.toUtf8()); } ``` ### React suspense compatibility React suspense are first class citizen for async & error handling. ```tsx const CurrentBlock = () => { const currentBlock = useQueryStorage("system", "number", []); return
Current block: {currentBlock}
; }; const App = () => ( ); ``` ### Caching, deduplication & persistent Multiple reads of the same value throughout the application will only be fetched once, cached, and is kept up to date everywhere. ```tsx const myAccount = "SOME_ACCOUNT_ADDRESS"; const FreeBalance = () => { // First invocation will initiate subscription via web socket const account = useQueryStorage("system", "account", [myAccount]); return
Free balance: {account.data.free.toHuman()}
; }; const FrozenBalance = () => { // Second invocation will only wait for and reuse value coming from the first invocation const account = useQueryStorage("system", "account", [myAccount]); return
Frozen balance: {account.data.frozen.toHuman()}
; }; const ReservedBalance = () => { // Third invocation will also only wait for and reuse value coming from the first invocation const account = useQueryStorage("system", "account", [myAccount]); return
Reserved balance: {account.data.reserved.toHuman()}
; }; const App = () => (
{/* `useQueryStorage("system", "account", [myAccount])` will only be executed once & is kept up to date for all 3 components */}
); ``` ### Full TypeScript support with autocompletion The library aim to provides strong TypeScript definition with 1-1 mapping to Substrate pallets definition. #### Autocompletion    #### Strong return type definition   ### And more The scope of this library can expand significantly based on community interest. Potential future features include: 1. Wallet/account connections management 2. Submitting transactions 3. Utility hooks outside of reading storage 4. Auto conversion of SCALE type encoding to native JS types (i.e. `U32` -> `Number`, `U256` -> `BigInt`, etc) 5. Multi-adapter support: Polkadot.js, Polkadot-API, DeDot, etc 6. Multi-framework support: React, Vue, Angular, etc 7. Etc ## Demo A working proof of concept showcasing the library can be found [here](https://stackblitz.com/edit/vitejs-vite-lyatc5?file=src%2FApp.tsx). ## Code comparison The below code snippets perform the following tasks: - Initiate connection to the chain - Reading the chain current block and the account balance - Display loading and error (if there's any) state - Display the final result after finished loading all values with no error ### With Polkadot.js ```tsx import { ApiPromise, WsProvider } from "@polkadot/api"; import type { u32 } from "@polkadot/types-codec"; import type { FrameSystemAccountInfo } from "@polkadot/types/lookup"; const MY_ACCOUNT = "SOME_ADDRESS"; const LOADING = new Symbol(); const App = () => { const [api, setApi] = useState(); const [currentBlock, setCurrentBlock] = useState(); const [account, setAccount] = useState< FrameSystemAccountInfo | LOADING | Error >(); useEffect(() => { (async () => { setApi(LOADING); try { const api = await ApiPromise.create({ provider: new WsProvider("wss://my.chain"), }); setApi(api); } catch (error) { setApi(new Error("Unable to initialize ApiPromise", { cause: error })); } })(); }, []); useEffect(() => { if (api === LOADING || api instanceof Error) { return; } const unsubscribePromise = (async () => { setCurrentBlock(LOADING); try { return api.query.system.number((currentBlock) => setCurrentBlock(currentBlock), ); } catch (error) { setCurrentBlock( new Error("Unable to get current block", { cause: error }), ); } })(); return () => { unsubscribePromise.then((unsubscribe) => { if (unsubscribe === undefined) { return; } unsubscribe(); }); }; }, [api]); useEffect(() => { if (api === LOADING || api instanceof Error) { return; } const unsubscribePromise = (async () => { setAccount(LOADING); try { return api.query.system.account(MY_ACCOUNT, (account) => setAccount(account), ); } catch (error) { setAccount(new Error("Unable to get account", { cause: error })); } })(); return () => { unsubscribePromise.then((unsubscribe) => { if (unsubscribe === undefined) { return; } unsubscribe(); }); }; }, [api]); if (api === LOADING || currentBlock === LOADING || account === LOADING) { return
Loading...
; } if ( api instanceof Error || currentBlock instanceof Error || account instanceof Error ) { return
Sorry, something went wrong.
; } return (
Your account free balance is: {account.data.free.toHuman()} at block{" "} {currentBlock.toNumber()}
); }; ``` ### With `reactive-dot` ```tsx const MY_ACCOUNT = "SOME_ADDRESS"; const _Balance = () => { const currentBlock = useQueryStorage("system", "number", []); const account = useQueryStorage("system", "account", [MY_ACCOUNT]); return (
Your account free balance is: {account.data.free.toHuman()} at block{" "} {currentBlock.toNumber()}
); }; const Balance = () => ( Sorry, something went wrong.
}> Loading...
}> <_Balance /> ); const App = () => ( ); ``` ## Timeline & Budget Requested amount: 6,000 DOT Estimated length of work: 8 weeks/~320 hours Estimated rate: 18.75 DOT or ~139.20 USD per hour The requested amount also covers the retrospective work from numerous experiments and research efforts that validated this idea and led to the development of the initial working proof of concept. ### Planned schedule 1. Week 1: Research & planning 2. Week 2-4: Core development 3. Week 5-6: Writing unit & integration tests 4. Week 7: Documentation, walkthrough & website 5. Week 8: Official version 1 6. Ongoing: Feedback & iteration Of which version 1 will include React support for the capabilities outlined in the [section](#key-features-for-the-first-version) before, excluding possible [future goals](#and-more) ## Conclusion `reactive-dot` aims to revolutionize the way developers interact with the Polkadot network by providing a robust, user-friendly, and feature-rich React library. By simplifying the development process and fostering a vibrant community, `reactive-dot` will play a pivotal role in promoting the adoption and growth of the Polkadot ecosystem. We seek the support and funding from the treasury to bring this ambitious project to life.
Proposal Passed
3
of 3Summary
0%
Aye
0%
Nay
Aye (62)0.0 DOT
Support0.0 DOT
Nay (8)0.0 DOT
Voting Data
Approval%
Support%
Threshold0.00%
Threshold0.00%
Comments