How do we know when a React ref.current value has changed?

React JS Faysal Shuvo

Problem: 

We can normally check with props when the ref.current value has changed.

componentDidUpdate(oldProps) {
    if (oldProps.foo !== this.props.foo) {
      console.log('foo prop changed')
    }
}

In this article, we will learn how do we know when a React ref.current value has changed? 

with example.


Solution:

The official react document recommends we should use callback refs to detect ref value change. I will explain to you both hooks and the class component.

Class component:

export class App extends React.Component {
    state = { ref: null, ... };
  
    onRefChangeHandler = node => {
      // same as Hooks example, re-render on changes
      this.setState({ ref: node });
    };
  
    render() {
      return <div ref={this.onRefChangeHandler}>Hello world</div>;
    }
}

Note: when ref changes useRef does not notify. Here is a  test case that re-adds a node and drops a node while triggering onRefChanges callback.

const App = () => {
    const [ref, setRef] = useState(null);
    const [removed, remove] = useState(false);
  
    useEffect(() => {
      setTimeout(() => remove(true), 5000); // drop after 3 sec
      setTimeout(() => remove(false), 3000); // ... and mount it again
    }, []);
  
    const onRefChangeHandler = useCallback(node => {
      console.log("ref changes:", node);
      setRef(node);
    }, []);
  
    return !removed && <div ref={onRefChangeHandler}>Hello, world</div>;
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.1/umd/react.production.min.js" integrity="sha256-vMEjoeSlzpWvres5mDlxmSKxx6jAmDNY4zCt712YCI0=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.1/umd/react-dom.production.min.js" integrity="sha256-QQt6MpTdAD0DiPLhqhzVyPs1flIdstR4/R7x4GqCvZ4=" crossorigin="anonymous"></script>
<script> var {useState, useEffect, useCallback} = React</script>
<div id="root"></div>

Hooks:

export function App() {
  const onRefChangeHandler = useCallback((node) => {
    if (node === null) {
      // DOM node referenced by ref has been unmounted
    } else {
      // DOM node referenced by ref has changed and exists
    }
  }, []);
  return <div ref={onRefChangeHandler}>Hello World!</div>;
}

Here, To prevent the double calling of ref callback with null we used useCallback. But by storing the current node using useState you can also trigger re-render on change.

const [domNode, setDomNode] = useState(null);
const onRefChangeHandler = useCallback((node) => {
  setDomNode(node); // it will trigger re-render on changes
}, []);


Thank you for reading this article. If you have any confusion please comment down below.