Forem

Cover image for React `useImperativeHandle` Magic
Tomek Poniatowicz for Aexol

Posted on • Originally published at aexol.com

5 4

React `useImperativeHandle` Magic

Blog post by Artur Czemiel - Aexol


Intro

I am a full-stack TypeScript developer and I've been learning the Elm language a lot recently. Will I switch to Elm? Definitely not. However, the learning process helped me understand and demand more things in React.

Today I will show you a React anti-pattern that will allow you to share state outside components. Why would you do this? Why not use a hook? etc. etc. I will show you an example with a small form.

Note: This is not a "real-world" example. In production env I am using this hook to create synchronized S3 File browser, but it might be too much for the sake of this article.

The Hook

So this combination of State and Callback is to connect the ref function later and listen when the ref changes:

import { useState, useCallback } from "react";

export const useImperativeRef = <T>() => {
  const [refState, setRefState] = useState<T>();
  const ref = useCallback((n: T | null) => {
    if (n) {
      setRefState(n);
    }
  }, []);
  return [refState, ref] as const;
};
Enter fullscreen mode Exit fullscreen mode

The form

We don't need anything fancy for this example so we will use a simple form with 2 inputs:

import React, {
  useImperativeHandle,
  useState,
} from "react";


interface FormValues{
    username: string;
    password: string;
}

export interface MyFormHandle{
    values: FormValues
}

export const MyForm = React.forwardRef(({},ref: React.ForwardedRef<MyFormHandle>) => {
    const [values,setValues]  = useState<FormValues>({
        username: "",
        password: "",
    })
    useImperativeHandle(
        ref,
        () => ({
            values
        })
    )
    return <div>
        <input type="text" value={values.username} onChange={e => 
            setValues({
                ...values,
                username: e.target.value
            })
        } />
        <input type="password" value={values.password} onChange={e => 
            setValues({
                ...values,
                password: e.target.value
            })
        } />
    </div>
}) 
Enter fullscreen mode Exit fullscreen mode

Using the form

Et voila! We can listen to state changes of components underneath:

export default () => {
    const [values,setRef] = useImperativeRef<MyFormHandle>()
    useEffect(() => {
        console.log(`Values changed!, Values: ${JSON.stringify(values,null,4)}`)
    }, [values])
    return <div>
        <MyForm ref={setRef} />
    </div>  
}
Enter fullscreen mode Exit fullscreen mode

Of course, we can pass change functions and values, but let's be true - that's Yuck! In my opinion, this way is much more elegant and codeless in many cases.

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay