The same useRef, but it will callback 🤙
Tags: react, ref, hooks
Published: 2019-06-22 03:34:55

---
title: The same useRef, but it will callback 🤙
published: true
description: React refs is a powerful tool, but it had one limitation...
tags: react, ref, hooks
---

For a long while we there were no `refs` - we had only `ref`, which was callback based. Something will set a `ref` by calling it.

```js
class Example extends React.Component {
  state = {
    ref1: null,
  }

  ref2 = null;

  // updating ref1 would trigger update for this component
  setRef1 = (ref) => this.setState(ref1);
  // updating ref2 would just set it 
  setRef2 = (ref) => this.ref2 = ref; 

  render() {
    return <div ref={ref1}><span ref={ref2}>🤷‍♂️</span></div>
}
```

That was what we were doing for ages, until `createRef` comes to the game. `React.createRef` is more about `ref2` way - current ref would _just set_ to, well, `ref.current`.

> Keep in mind that __useRef doesn’t notify__ you when its content changes. Mutating the .current property doesn’t cause a re-render.

So - _If you want to run some code when React attaches or detaches a ref to a DOM node, you may want to use [a callback ref](https://reactjs.org/docs/hooks-faq.html#how-can-i-measure-a-dom-node) instead. Ie the old way to _ref_.

![Hooks API Reference](https://thepracticaldev.s3.amazonaws.com/i/xihl091fqcx5ok39qsqj.png)

```js
const Example = () => {
   const [ref, setRef] = useState(null);
   const onRefSet = useCallback(ref => {
      setRef(ref);
      ref.current.focus(); // a side effect!
   });
   
   // well, you can re
   return <div ref={onRefSet}>😎</div>
}
```
But later you might try to combine ref-refs and callbacks-refs, and... well that's the road to 🔥hell🔥.

In addition - there is [useImperativeHandle](https://reactjs.org/docs/hooks-reference.html#useimperativehandle) which _partially_ could control _ref propagation_, but every time I was used to use it - it was just a 💩disaster💩.

```js
function FancyInput(props, ref) {
  const inputRef = useRef(null);
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus(); // it just does not usually works :P
    }
  }));
  return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
```

# LET'S FIX IT!

Introducing [use-callback-ref](https://github.com/theKashey/use-callback-ref) - the same `createRef` and `useRef`, but with callback built in.

```js
import {useCallbackRef} from 'use-callback-ref';

const Example = () => {
   const ref = useCallbackRef(null, ref => ref && ref.focus());

   // that's all
   return <div ref={ref}>😎</div>
}
```

It's literally the old good `ref` with an __on-change callback__, nothing more.

> Why not to use callback-based ref? Well, it's much easier to handle one interface, which would be accessible thought all components that ref would be passed, well, thought - while with `setRef` only `callback` would be visible for transitional components. However, that could be a good from isolation point of view. 

This simple approach could also help with `useImperativeHandle` case:
```js
function FancyInput(props, ref) {

  const inputRef = useCallbackRef(null, (newValue) => {
    // notice - this code is __isolated__, and you can move it off this component
    ref.current = { focus: () => newValue.focus() }
    // as long as you don't need to use callback-ref anymore - we could simply this case.
  });

  return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
```


So - Keep in mind that __useRef doesn’t notify__ you when its content changes. Mutating the .current property doesn’t cause a re-render. If you want to run some code when React attaches or detaches a ref to a DOM node, __you may want to use a useCallbackRef instead__.


- 300b, and IE11 support
- based on getters and setters, no Proxies involved

Try it now([codesandbox demo](https://codesandbox.io/s/usecallbackref-example-jldvr)), and call me back later - https://github.com/theKashey/use-callback-ref

> And there is the second part of this article
{% link thekashey/merging-refs-41l8 %}