Logo do site Logo do site
  • Services
  • Venture
  • People
  • Content Hub
  • Contact Us
Close
  • Services
  • Venture
  • People
  • Content Hub
  • Contact Us
Blogpost

Keeping your smart contract calls under control with react hooks

July 17, 2023 4 min

Written by

  • Pedro Oliveira
    Pedro Oliveira

Chapter

  • Introduction
  • Using react custom hooks
  • Testing

Share article

Coppied!

Category

Engineering

When developing a React component, it is common for us developers to include both the logic and markup in the same file. This approach serves the purpose of bootstrapping and containing the scope. It is particularly useful when our goal is to rapidly prototype and test, as it allows us to conveniently write the code without scattering the scope across multiple files.

However, as the application progresses beyond the proof-of-concept stage, this practice can become messy. Components that require additional business logic, beyond state management, need to be carefully managed. It is important to address this issue for istance by separating concerns so we can ensure that each component has a clear and distinct responsibility. This makes it easier to manage and extend the application as it continues to evolve.

When it comes to integrating smart contract calls, the codebase of the component tends to grow significantly. It is crucial to be mindful of separating the logic from the component at this point.

Consider an ERC20 contract as an example, and the allowance function as example:

<span class="token keyword">contract</span> <span class="token class-name">MyToken</span> <span class="token keyword">is</span> ERC20 <span class="token punctuation">{</span>
    <span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span>

    <span class="token keyword">function</span> <span class="token function">allowance</span><span class="token punctuation">(</span><span class="token builtin">address</span> owner<span class="token punctuation">,</span> <span class="token builtin">address</span> delegate<span class="token punctuation">)</span> <span class="token keyword">public</span> override <span class="token keyword">view</span> <span class="token keyword">returns</span> <span class="token punctuation">(</span><span class="token builtin">uint</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> allowed<span class="token punctuation">[</span>owner<span class="token punctuation">]</span><span class="token punctuation">[</span>delegate<span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

A simple call to this contract, using wagmi generated hooks for instance, would look something like:

<span class="token keyword">const</span> <span class="token punctuation">[</span>contractAllowance<span class="token punctuation">,</span> setContractAllowance<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token literal-property property">address</span><span class="token operator">:</span> account <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useAccount</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> myTokenContractAddr <span class="token operator">=</span> <span class="token string">"0x..."</span>

<span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token literal-property property">refetch</span><span class="token operator">:</span> refetchAllowance <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useMyTokenAllowance</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  <span class="token literal-property property">args</span><span class="token operator">:</span> <span class="token punctuation">[</span>account <span class="token operator">??</span> <span class="token string">"0x0"</span><span class="token punctuation">,</span> myTokenContractAddr<span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token literal-property property">enabled</span><span class="token operator">:</span> <span class="token operator">!</span><span class="token operator">!</span>account<span class="token punctuation">,</span>
  <span class="token function-variable function">onSuccess</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
    <span class="token function">setContractAllowance</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

If the component needs to incorporate basic contract calls such as checkAllowance, approveAllowance, and transferTokens, its size will grow significantly.

Using react custom hooks

Fortunately, React provides the flexibility to create custom hooks by using the built-in hooks as building blocks. React docs defines custom hooks in a simple way:

A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks.

Consider a basic component as an example:

<span class="token keyword">const</span> <span class="token function-variable function">MyComponent</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span>address<span class="token punctuation">,</span> allowance<span class="token punctuation">,</span> approveAllowance <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useMyTokenHook</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">return</span> <span class="token punctuation">(</span>
    <span class="token operator"><</span>div<span class="token operator">></span>
      <span class="token operator"><</span>h1<span class="token operator">></span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>address<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span> <span class="token operator"><</span><span class="token operator">/</span>h1<span class="token operator">></span>
      <span class="token operator"><</span>p<span class="token operator">></span>Allowance <span class="token keyword">for</span> myToken contract is <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>allowance<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span>
      <span class="token operator"><</span>button onClick<span class="token operator">=</span><span class="token punctuation">{</span>approveAllowance<span class="token punctuation">}</span><span class="token operator">></span>Approve <span class="token operator">+</span><span class="token number">10</span> myToken allowance<span class="token operator"><</span><span class="token operator">/</span>button<span class="token operator">></span>
    <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
  <span class="token punctuation">)</span>
<span class="token punctuation">}</span>

By leveraging those hooks, creating specific hooks based on the contract or function call becomes straightforward:

<span class="token keyword">const</span> <span class="token function-variable function">useMyTokenHook</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token literal-property property">address</span><span class="token operator">:</span> account <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useAccount</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> myTokenContractAddr <span class="token operator">=</span> <span class="token string">"0x..."</span>

  <span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token literal-property property">data</span><span class="token operator">:</span> allowance<span class="token punctuation">,</span> <span class="token literal-property property">refetch</span><span class="token operator">:</span> refetchAllowance <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useMyTokenAllowance</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    <span class="token literal-property property">args</span><span class="token operator">:</span> <span class="token punctuation">[</span>account <span class="token operator">??</span> <span class="token string">"0x0"</span><span class="token punctuation">,</span> myTokenContractAddr<span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token literal-property property">enabled</span><span class="token operator">:</span> <span class="token operator">!</span><span class="token operator">!</span>account
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token operator">...</span>

  <span class="token keyword">return</span> <span class="token punctuation">{</span> address<span class="token punctuation">,</span> allowance<span class="token punctuation">,</span> <span class="token operator">...</span><span class="token punctuation">}</span>

This approach offers improved readability, scalability, and facilitates testing.

Especially when it comes to unit tests, testing both the hook and the component separately and easily mock the hook’s output to isolate and test the component. This flexibility ensures thorough testing of the codebase, validating the reliability and functionality of both the hook and the component.

Testing

Here is a quick and conceptual example on how testing could work with React Testing Library and Jest.

In a component perspective, the test would mock the hook call and yield a known result and then assert the component is properly getting and rendering that expected result.

<span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">"test component with mock results"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  <span class="token keyword">let</span> mockedData<span class="token punctuation">;</span>

  <span class="token function">beforeEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
    mockedTokenHookResult <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token literal-property property">address</span><span class="token operator">:</span> <span class="token string">"0x..."</span><span class="token punctuation">,</span> <span class="token literal-property property">allowance</span><span class="token operator">:</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token literal-property property">approveAllowance</span><span class="token operator">:</span> <span class="token operator">...</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
    global<span class="token punctuation">.</span>useMyTokenHook<span class="token punctuation">.</span><span class="token function">mockResolvedValue</span><span class="token punctuation">(</span>mockedTokenHookResult<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">"should display mocked data"</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
    <span class="token function">render</span><span class="token punctuation">(</span><span class="token operator"><</span>MyComponent <span class="token operator">/</span><span class="token operator">></span><span class="token punctuation">)</span>
    <span class="token function">expect</span><span class="token punctuation">(</span>screen<span class="token punctuation">.</span><span class="token function">getByText</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Allowance for myToken contract is 10</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBeInTheDocument</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

The hook, in the other hand, is similar, but the mocked call this time is useMyTokenAllowance (and other contract calls), in order to assert that it’s handling the response properly and setting the internal states accordingly:

<span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">"test hook with mock call"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  <span class="token keyword">let</span> mockedData<span class="token punctuation">;</span>

  <span class="token function">beforeEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
    global<span class="token punctuation">.</span>useMyTokenAllowance<span class="token punctuation">.</span><span class="token function">mockResolvedValue</span><span class="token punctuation">(</span><span class="token number">30</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    global<span class="token punctuation">.</span>useAccount<span class="token punctuation">.</span><span class="token function">mockResolvedValue</span><span class="token punctuation">(</span><span class="token string">"0xABCDEF..."</span><span class="token punctuation">)</span>

    <span class="token punctuation">(</span><span class="token operator">...</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">"should return data"</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> <span class="token punctuation">{</span> result <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">renderHook</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">useMyTokenHook</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">const</span> <span class="token punctuation">{</span> allowance<span class="token punctuation">,</span> address<span class="token punctuation">,</span> <span class="token operator">...</span> <span class="token punctuation">}</span> <span class="token operator">=</span> result<span class="token punctuation">.</span>current

    <span class="token keyword">await</span> <span class="token function">waitFor</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span>
      <span class="token function">expect</span><span class="token punctuation">(</span>allowance<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token number">30</span><span class="token punctuation">)</span>
      <span class="token function">expect</span><span class="token punctuation">(</span>address<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token string">"0xABCDEF..."</span><span class="token punctuation">)</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

As a final note, it’s important to be intentional and conscious when using hooks to abstract logic:

  • Ensure that the output of the hook does not include JSX. If it does, consider creating a separate component instead of a hook.
  • Verify that the hook code includes calls to other hooks. If it doesn’t, consider creating a regular function instead.

By adhering to these guidelines, maintaining a clear and effective separation of concerns will result in a more modular and reusable code.

Share article

Coppied!

Category

Engineering

You may also like

Liquity V2 and How Liquidation Bots Work
Blogpost
Liquity V2 and How Liquidation Bots Work
Blockchain
Engineering
Web3
December 2, 2025 4 min
Gabriel Poça
Gabriel Poça
The Lesson Every AI Builder Should Remember
Blogpost
The Lesson Every AI Builder Should Remember
AI
Engineering
Product
October 24, 2025 5 min
Luís Zamith
Luís Zamith
Subscribe to Subvisual Inspo

Go to

  • Services
  • Ventures
  • People
  • Blog
  • Jobs / Careers

We're social

  • Git
  • Dri
  • In
  • X

Contact us

[email protected]

Offices

Remote. Work anywhere in Europe.
Or join our mothership, landed in Braga, Portugal