question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Non referentially stable refs cause multiple updates

See original GitHub issue

When a React element receives a different ref on re-renderer the previous ref is updated to null and the new one is given the current value — regardless of changes in value.

I made a small CodeSandbox to verify this. In it you can also find a “solution”, which is to transform the API into an hook. This let us introduce useCallback to give the user a referentially stable ref.

This doesn’t solve the problem completely: if one of the refs changes all other refs receive a double (and useless) update too.

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:7
  • Comments:30 (3 by maintainers)

github_iconTop GitHub Comments

4reactions
yuchicommented, Nov 26, 2020

MUI’s version is stable, but only in a single specific sense, and formally fragile. Let me explain.

A perfect ref merger should:

  1. Be stable against re-renders, if no input ref has changed the resulting ref must be the same as the previous one, so that it doesn’t uselessly trigger an update.
  2. Be transparent, from the outside, from the standpoint of a single ref creator, there should be no difference between using the ref as-is and merged with other refs.

MUI’s current implementation has the following problems:

  1. It uses useMemo which as we all know as historically been defined as an optimization tool, and should not be used to have “singleton” semantics. In this case avoiding recreating a ref is some kind of an optimization trick, but since ref updating is in the imperative world (a function ref can have side effects [citation needed]), we need more guarantees.
  2. When one of the ref changes a new merged ref is created and all refs gets updated, again ruining the expectation about when and how function refs are called.
3reactions
agriffiscommented, Jun 12, 2020

I don’t think this needs effects at all. Doesn’t this work to preserve the resulting callback ref?

/* eslint-disable react-hooks/exhaustive-deps */
import React from 'react'

export const useMergedRefs = refs =>
  React.useCallback(current => {
    refs.forEach(ref => {
      if (typeof ref === 'function') {
        ref(current)
      } else if (ref && !Object.isFrozen(ref)) {
        ref.current = current
      }
    })
  }, refs)

The reason for disabling react-hooks/exhaustive-deps is that we’re passing the raw refs array to useCallback, which means that eslint can’t statically check it. But it works fine for the actual checking which preserves and updates the callback.

Also, the Object.isFrozen test shows up for me in tests. I’m not sure under what conditions the ref becomes a frozen, empty object, but we just need to avoid assigning in that case.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Non referentially stable refs cause multiple updates #5 - GitHub
When a React element receives a different ref on re-renderer the previous ref is updated to null and the new one is given...
Read more >
How can I use multiple refs for an array of elements with hooks?
Accessing an undefined ref is not possible. A ref is initialized upon the first read. After that, it remains referentially stable.
Read more >
You're overusing useMemo: Rethinking Hooks memoization
In some cases, useMemo is irrelevant, overused, and harmful to your application performance. Learn these situations and how to avoid them.
Read more >
Foreign Key Constraint | CockroachDB Docs
The `FOREIGN KEY` constraint specifies a column can contain only values exactly matching existing values from the column it references.
Read more >
Jetpack Compose Stability Explained | Android Developers
Compose determines the stability of each parameter of your composables to work out if it can be skipped or not during recomposition.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found