Proposed by:
Requested amount:
0 DOT

#1477 · Funding for ReactiveDOT & related developments

Building upon #812, #948 & #1334. This proposal requests funding for the continuation of ReactiveDOT, alongside its related projects (DOTConsole, DOTConnect).

Reactive DOT is a library designed to:

  • Simplify development: Provide a set of intuitive front-end functions and utilities to facilitate Substrate chain interactions, making development accessible for developers of all skill levels.
  • Enhance developer experience: Reduce boilerplate code and complexity involved in integrating DApps with Substrate chains, allowing developers to focus on building robust applications.
  • Promote adoption: Lower the entry barrier for developers through improved tooling and familiar patterns, encouraging the adoption of Polkadot.

Previous goals

Stabilise API

Various updates were made to the public-facing API, moving many components to internal APIs, except for those identified as highly stable:

Achieve greater than 65% test coverage

Current code coverage details are available here: https://app.codecov.io/gh/tien/reactive-dot/

Provide detailed API documentation and sample usage

Continued documentation updates and maintenance for new features include:

Create improved tutorials

  • Created PR to enhance the "Substrate Kitties" tutorial, showcasing the significant reduction in code by using ReactiveDOT.
  • Assisted Parity in developing create-polkadot-app, a CLI tool designed to quickly bootstrap Polkadot DApps using PAPI, ReactiveDOT, and DOTConnect. My role was primarily advisory, with Parity employees handling the core development.

Cut release for version 1.0.0

Although the API is currently highly stable and ready for a v1.0.0 release, I've opted to remain in the pre-1.0 phase to retain flexibility for additional features work (will be outlined in later sections).

Previous stretch goals achieved

Context-based controllable subscription state

Useful for building screens with a large number of subscriptions, requiring targeted optimization, i.e. infinite lazy loadable list.

PR: https://github.com/tien/reactive-dot/pull/525

Documentation: https://reactivedot.dev/react/guides/performance#controlling-subscriptions

Demonstration: https://x.com/TienNguyenK/status/1895581356908065277

Ergonomic light-client config & Substrate Connect integration

PR: https://github.com/tien/reactive-dot/pull/368

Demonstration: https://x.com/TienNguyenK/status/1896888190499443132

Mimir wallet support

As per how plug-n-play wallet was designed, integration is optional, and dependencies must be explicitly added with:

yarn add @reactive-dot/wallet-mimir
import { defineConfig } from "@reactive-dot/core";
import { MimirWalletProvider } from "@reactive-dot/mimir";

export const config = defineConfig({
  // ...
  wallets: [new MimirWalletProvider()],
});

PR: https://github.com/tien/reactive-dot/pull/517

Announcement by Mimir: https://x.com/Mimir_global/status/1894576922228916584

Stable React 19 suspense behavior

React 19 changed how suspense works, resulting in micro-suspense render even for resolved promises. For users, this means occasionally seeing sub-second flickers of loading state.

From countless hours researching & debugging (including on how React works internally), a solution was found and fixed via:

New features on DOTConsole

Mainly used as a testing ground for ReactiveDOT, demonstrating how little code is needed for building highly performant, light-client first DApp.

Miscellaneous external contributions

Besides working on my own projects, I also help out other projects within the ecosystem whenever possible, you can check my activities here.

Future goals

Ink contract support

Whereas chain queries are default-reactive. Contract queries are chain-agnostic and default non-reactive. Integration goals, besides basic Ink support, will be to aid developers in dealing with the contract's default-non-reactive nature. In short, beating the dev experience of the current gold standard of Contract FE DApp development: Ethereum's WAGMI.

Incremental loading

Currently, if you need to derive values based on the results of multiple queries, the UI will remain suspended until all data has fully loaded. This approach isn't ideal when you'd prefer to display partial UI content as soon as some data becomes available.

Here's a current scenario:

function Component() {
  const items = useLazyLoadQuery((builder) =>
    builder.readStorageEntries("Pallet", "Items", []),
  );

  // This causes significant UI suspension time
  const itemAmounts = useLazyLoadQuery((builder) =>
    builder.readStorages(
      "Pallet",
      "ItemAmount",
      items.map(([key]) => key),
    ),
  );

  // Ideally, we'd derive values progressively from loaded data
  const sortedItems = useMemo(
    () =>
      items
        .map((item, index) => ({ ...item, amount: itemAmounts.at(index)! }))
        .toSorted((a, b) => a.amount - b.amount),
    [items, itemAmounts],
  );

  return (
    <ol>
      {sortedItems.map((item, index) =&gt; (
        <li>{/* ... */}</li>
      ))}
    </ol>
  );
}

A better approach might involve introducing an API like this:

import { pending } from "@reactive-dot/core";

function Component() {
  const items = useLazyLoadQuery((builder) =&gt;
    builder.readStorageEntries("Pallet", "Items", []),
  );

  const itemAmounts = useLazyLoadQuery((builder) =&gt;
    builder.readStorages(
      "Pallet",
      "ItemAmount",
      items.map(([key]) =&gt; key),
      {
        concurrency: "wait-for-none", // Immediately returns available data without suspending; alternative strategies like `wait-for-one` could also be implemented
      },
    ),
  );

  const sortedItems = useMemo(
    () =&gt;
      items
        .map((item, index) =&gt; {
          const itemAmount = itemAmounts.at(index)!;
          return { ...item, amount: itemAmount === pending ? 0 : itemAmount };
        })
        .toSorted((a, b) =&gt; a.amount - b.amount),
    [items, itemAmounts],
  );

  return (
    <ol>
      {sortedItems.map((item, index) =&gt; (
        <li>{/* ... */}</li>
      ))}
    </ol>
  );
}

This proposed API enables partial UI rendering, making your components more responsive and improving overall user experience.

Query composition

Current state

ReactiveDOT suspense-powered query ideal usage is as follows:

function ChildComponent({ id }) {
  const [item1, item2] = useLazyLoadQuery((builder) =&gt;
    builder
      .readStorage("Pallet1", "Storage1", [id])
      .readStorage("Pallet2", "Storage2", [id]),
  );

  return (
    <article>
      <header>{item1}</header>
      <p>{item2}</p>
    </article>
  );
}

function ParentComponent() {
  const overviewInfo = useLazyLoadQuery((builder) =&gt;
    builder.readStorage("Pallet3", "Storage3", []),
  );

  const ids = [
    /* ... */
  ];

  return (
    <section>
      <header>{overviewInfo}</header>

      <ul>
        {ids.map((id) =&gt; (
          <li>
            Loading item {id}<p></p>}&gt;
              
            
          </li>
        ))}
      </ul>
    </section>
  );
}

export function App() {
  return (
    Loading<p></p>}&gt;
      
    
  );
}

Where a component requests exactly and only what it needs, and directly map this data to UI elements. This bottom-up approach ensures components remain lean, load quickly, render fast, and are easy to understand and maintain.

However, this ideal scenario isn't always achievable because values often need to be derived or reduced from complex storage structures. Current approaches include:

  • Custom hooks using PAPI directly: Offers maximum flexibility and is currently the recommended approach.
  • Custom hooks leveraging ReactiveDOT's useLazyLoadQuery: Easy to implement and effective for simpler cases but susceptible to suspense waterfalls.
  • Hooks/functions derived from values provided by useLazyLoadQuery: Technically optimal but may negatively impact developer experience due to increased complexity and additional code requirements.

Planned R&D aims to provide developers with enhanced hooks and utilities, simplifying the composition of queries that are both suspendable and resistant to suspense waterfalls. This could occur either:

  • During render: Simpler but less performant.
  • Outside render: More performant but complex, requiring effective caching strategies.

Note: These challenges could potentially be resolved entirely through WASM view functions, making ReactiveDOT's ideal usage consistently effective.

Achieve greater than 90% test coverage

Currently, the test coverage is approximately ~70%, with more than 90% coverage already achieved for core logic packages (@reactive-dot/core, @reactive-dot/utils, @reactive-dot/wallet-*). The remaining gaps are primarily within the render layer (@reactive-dot/react, @reactive-dot/vue).

End-to-end (E2E) tests will likely be necessary to reach the targeted coverage. These tests may be implemented using tools like chopsticks or zombienet.

Continued improvements to DOTConnect & DOTConsole

Further enhancements will be guided by community feedback or requirements identified to support the ongoing testing and development of ReactiveDOT.

Requesting

  • Period: March 2025 to June 2025.
  • Duration: 4 months / 86 workdays / 688 hours.
  • Requested amount: 89,440 USDC @ 130 USDC an hour.
Read more
StatusConfirming · 6h
99%Aye
Aye (254)
38.60M DOT
Nay (19)
8.15K DOT
Decision23 / 28d
Confirmation3 / 4d
0.0%0.78%
0.28%Support Threshold
0Support Threshold
Support(0.62%)
9.60M DOT
Issuance
1.54B DOT
Vote
tienMar 18
TINY

Thanks for Tien's integration. Tien did great job in development and very detailed docs. ReactiveDOT/DOT Connect is very useful and powerful!

Thanks for the support @TINY 🙏

TINYMar 18

Thanks for Tien's integration. Tien did great job in development and very detailed docs. ReactiveDOT/DOT Connect is very useful and powerful!

Lucky Friday have voted AYE. Please consider this a temporary notification after our preliminary vote has gone on chain. If you would like additional feedback on our rationale for this vote, please join our OpenGov Public Forum on Telegram here: https://t.me/+559tyPSfmGg0NzUx

Lucky Friday provides feedback once per week (Fridays) if specifically requested in our OpenGov Public Forum, and we respectfully ask that all proponents of referenda interact with us here for the sake of transparency. Please tag our Head of Protocol Partnerships “Phunky” with your referendum number so that he can gather the relevant commentary from our internal deliberations.

tienMar 11
Carlo

Strong AYE. Tien has been very helpful to PAPI team by testing new releases on day 0, proposing changes, and findings bugs. Besides that, his libraries have been super helpful for many teams in their migration to PAPI.

Thanks Tien!

Thanks for the support @Carlo 🙏

CarloMar 11

Strong AYE. Tien has been very helpful to PAPI team by testing new releases on day 0, proposing changes, and findings bugs. Besides that, his libraries have been super helpful for many teams in their migration to PAPI.

Thanks Tien!

Mar 11

Dear Proposer,

Thank you for your proposal. Our first vote on this proposal is AYE.

The Medium Spender track requires 50% quorum and simple majority of non-abstain voters according to our voting policy. This proposal has received five aye and zero nay votes from ten members, with one member abstaining. Below is a summary of our members' comments:

> The voters generally expressed support for the proposal, emphasizing the importance of keeping the developer engaged with Polkadot and enhancing developer experience. They highlighted the need for future initiatives to align with the Polkadot Hub narrative and focus on the adoption of the library by existing projects. Comments also stressed the significance of providing user-friendly tools to attract developers and suggested that future proposals should include metrics on developer engagement and library utilization. One voter chose to abstain from the decision.

The full discussion can be found in our internal voting.

Kind regards,
Permanence DAO

tienMar 6

The formatting on Polkassembly is a bit wonky right now, so I recommend checking out the one on Subsquare instead: https://polkadot.subsquare.io/referenda/1477

Powered by Subsocial