<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Matias Facio</title>
    <description>The latest articles on DEV Community by Matias Facio (@matias-tanguito).</description>
    <link>https://dev.to/matias-tanguito</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1250777%2F3864df19-0309-40a1-a329-5ae2d65cb1d2.png</url>
      <title>DEV Community: Matias Facio</title>
      <link>https://dev.to/matias-tanguito</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/matias-tanguito"/>
    <language>en</language>
    <item>
      <title>How to create components interfaces using useRef/forwardRef/useImperativeHandle</title>
      <dc:creator>Matias Facio</dc:creator>
      <pubDate>Fri, 10 Jan 2025 23:29:07 +0000</pubDate>
      <link>https://dev.to/matias-tanguito/how-to-create-components-interfaces-using-userefforwardrefuseimperativehandle-3app</link>
      <guid>https://dev.to/matias-tanguito/how-to-create-components-interfaces-using-userefforwardrefuseimperativehandle-3app</guid>
      <description>&lt;h2&gt;
  
  
  Let's build a case
&lt;/h2&gt;

&lt;p&gt;Imagine you have a counter—a classic, simple counter component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function Counter(){
  const [counter, setCounter] = useState(0)
  const handleIncrement = ()=&amp;gt; setCounter(counter + 1)
  return (
     &amp;lt;&amp;gt;
       &amp;lt;p&amp;gt;Counter: {counter}&amp;lt;/p&amp;gt;
       &amp;lt;button onClick={handleIncrement}&amp;gt;Inc&amp;lt;/button&amp;gt;
     &amp;lt;/&amp;gt;
  )
}

function App(){
  return (
    &amp;lt;&amp;gt;
      &amp;lt;Counter /&amp;gt;
    &amp;lt;/&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, for some reason, you're asked to add a second button that increments the counter but is placed somewhere far away from the Counter component—although still on the same screen.&lt;/p&gt;

&lt;p&gt;There are a few options to achieve this, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lift the counter state up to the parent component.&lt;/li&gt;
&lt;li&gt;Use context to share the state.&lt;/li&gt;
&lt;li&gt;Use useRef, useImperativeHandle, and forwardRef.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll explore the third option: creating an interface to expose the counter's methods via refs.&lt;/p&gt;

&lt;h3&gt;
  
  
  useRef/useImperativeHandle/forwardRef
&lt;/h3&gt;

&lt;p&gt;Our goal is to create an interface that exposes a method using refs. This allows the parent component to access and modify the child component's state.&lt;/p&gt;

&lt;p&gt;Here's how to do it:&lt;br&gt;
Wrap the Counter component with forwardRef (not needed in React 19!) and use useImperativeHandle to expose the handleIncrement method via the passed ref.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Counter = forwardRef((_, ref) =&amp;gt; {
  const [counter, setCounter] = useState(0)
  const handleIncrement = ()=&amp;gt; setCounter(counter + 1)

  useImperativeHandle(ref, ()=&amp;gt; ({
     increment: handleIncrement
  }))

  return (
     &amp;lt;&amp;gt;
       &amp;lt;p&amp;gt;Counter: {counter}&amp;lt;/p&amp;gt;
       &amp;lt;button onClick={handleIncrement}&amp;gt;Inc&amp;lt;/button&amp;gt;
     &amp;lt;/&amp;gt;
  )
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, modify the parent component to pass a ref to Counter and add a button to increment the counter's state from elsewhere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function App(){
  const counterRef = useRef(null)
  return (
    &amp;lt;&amp;gt;
      &amp;lt;Counter ref={counterRef}/&amp;gt;
      {/* somewhere bit far away... */}
      &amp;lt;button onClick={()=&amp;gt; counterRef.current?.increment()}&amp;gt;Increment from here too!&amp;lt;/button&amp;gt;
    &amp;lt;/&amp;gt;
)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it!&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding a Reset Functionality
&lt;/h3&gt;

&lt;p&gt;Now, let's add a reset button to reset the counter. &lt;br&gt;
To do this, create a new handler in the Counter component and expose it through the ref interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Counter = forwardRef((_, ref) =&amp;gt; {
  const [counter, setCounter] = useState(0)
  const handleIncrement = ()=&amp;gt; setCounter(counter + 1)

  useImperativeHandle(ref, ()=&amp;gt; ({
     increment: handleIncrement,
     reset: ()=&amp;gt; setCounter(0)
  }))

  return (
     &amp;lt;&amp;gt;
       &amp;lt;p&amp;gt;Counter: {counter}&amp;lt;/p&amp;gt;
       &amp;lt;button onClick={handleIncrement}&amp;gt;Inc&amp;lt;/button&amp;gt;
     &amp;lt;/&amp;gt;
  )
})

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, use this new method in the parent component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function App(){
  const counterRef = useRef(null)
  return (
    &amp;lt;&amp;gt;
      &amp;lt;Counter ref={counterRef}/&amp;gt;
      {/* somewhere bit far away... */}
      &amp;lt;button onClick={()=&amp;gt; counterRef.current?.increment()}&amp;gt;Increment from here too!&amp;lt;/button&amp;gt;
      &amp;lt;button onClick={()=&amp;gt; counterRef.current?.reset()}&amp;gt;Reset&amp;lt;/button&amp;gt;
    &amp;lt;/&amp;gt;
)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, I didn't add a button to reset the counter inside of the Counter component, why so? Nothing special, I just wanted to show a different implementation that might fit other use cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Use This Pattern?
&lt;/h3&gt;

&lt;p&gt;This pattern opens up new possibilities for component communication without requiring state lifting or context. However, there are trade-offs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caveat: This Pattern Breaks React’s Declarative Model
&lt;/h3&gt;

&lt;p&gt;React is fundamentally declarative—you describe what the UI should look like, and React handles the rest. Introducing an imperative approach with useImperativeHandle can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increase Complexity: You must manually define and handle actions.&lt;/li&gt;
&lt;li&gt;Encourage Side Effects: This can lead to less predictable, harder-to-debug code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While powerful, use this pattern judiciously, especially when simpler alternatives like state lifting or context would suffice.&lt;/p&gt;

</description>
      <category>useref</category>
      <category>react</category>
      <category>customhook</category>
      <category>useimperativehandle</category>
    </item>
  </channel>
</rss>
