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.

Missing clean-up of refs

See original GitHub issue

It looks like neither callback refs nor object refs are getting cleaned up.

In the case of callback refs, this means you can’t use a callback to clean up any side-effects - here’s an example:

const DatePicker = () => {
  const init = el => {
    if (el) {
      // initialize date-picker using some third-party plain JS lib...
    } else {
      // clean up any date-picker pop-ups that may still be open...
    }
  }
  return <span ref={init}/>
}

The clean-up call never happens, so we currently can’t really rely on refs for this sort of thing.

Note that this may not be a very useful feature in a codebase that exclusively uses hooks, but if we want to support class-based components at some point down the line, many class-based libraries rely on refs for side-effects and clean-up.

I noticed earlier that object refs also do not get cleaned up - so if you have something like this:

const Component = () => {
  const item = useRef()
  const [showItem, setShowItem] = useState(true)

  // ...

  return (
    <div>
      {showItem && <div ref={item}/>}
    </div>
  )
}

If showItem gets updated to false, item.current will still point to the DOM element, which gets removed from the DOM, but now it can’t be garbage-collected.

As always, I also compared the behavior with preact, and it also deviates from react on this point, so I have opened an issue here as well.

As explained in that issue, I am not actually sure if what I reported there is a bug in preact or react, since preact appears to behave more accurately as it’s described in the React docs.

Until we know if the behavior regarding null updates is correct in react or preact, I would actually advise you wait to address this issue - there is definitely an issue in fre either way, since it doesn’t remove references at all, but at the moment I couldn’t tell you precisely if react or preact dispatches the null updates correctly, so lets wait and see.

For comparison, here’s the output from all three:

image

Note that, besides the missing null updates, it also looks like callback refs are also currently being dispatched twice during every update in fre.

It could be you actually meant for one of those calls to be the null update after clearing out the internal reference? That would seem to bring the behavior very close to that of react as it behaves currently.

You might to address that detail right away, as well as the null updates missing after removal.

Or just wait and see who’s right between react and preact first. 😏

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:28 (12 by maintainers)

github_iconTop GitHub Comments

3reactions
mindplay-dkcommented, Nov 12, 2019

I don’t understand that example. What element would the ref function be receiving here? <App> is not an element, it’s a functional component, so in React, you would receive the component class instance - but since this library is hooks-only, there are no component classes.

Refs exist primarily to obtain references to DOM elements - with functional components and hooks, this is primarily useful in conjunction with effects, and effects often depend on refs.

Does its sub elements need to perform cleanup?

Yes, definitely. Refs themselves can have direct effects on the DOM (or other global state) and these types of effects aren’t complete without a clean-up cycle - just as useEffect would not be very useful if it didn’t support clean-up.

I look forward to discussing completely different results from react and modifying the test

The point of having tests is to define expectations - they’re an executable specification of what you intend to build. You can change the tests, if the specification changes, but you need to know why you’re changing them; you can’t just change the tests to match what the library does, not without knowing precisely what you want the library to do.

I can go into this at length, but it's off-topic to this discussion, so I'll leave the rest of my comments as a collapsible - click here. 🙂 It's never a good idea to commit changes that break the tests, because you lose track of anything else you might break before you get the tests working again.

It’s a much better idea to change the tests first - this documents the change you’d like to make to the specification, and at that point, you know why the tests are failing and can work towards fixing the code to match the spec.

It’s also not a good idea to commit breaking changes directly to master - if you’re going to change the tests, you should always do so on a separate branch, so the changes to the spec and library can be reviewed and tested together.

I’m not opposed to deviations from React, but you shouldn’t be changing (or breaking) the tests unless the requirements have changed. Tests are not an afterthought to show how something works - you should think of them as a plan to show how something should work.

In other words, “the test is always right” - if the test fails, it should be because you changed the test, never because you changed the library. (unless you discover that the test itself contains a bug, which does of course also happen.)

This is not to say I think my tests are always right! 😉

But since you didn’t have any tests, we had to start somewhere, and I’m basing these tests on what I’ve learned from React, Preact, and a handful of other libraries of this type. If I had merely written tests that matched what your library already did, we wouldn’t have found or corrected many of the bugs, so you can probably already see how this works. Again, my tests may not align with your expectations, but then you need to describe your expectations with tests before changing the library, otherwise the tests have no real value.

I understand you’re new to testing, just trying to help 🙂

This article summarizes the principles of test-driven development:

The principles of TDD are as follows:

  • Tests are always written before the code that will make them pass. The test anticipates the correct behavior of the code.
  • Development proceeds one test at a time. Once a test goes from failing to passing, the next test is written and development can continue.
  • Again, tests should be as simple as possible, only long enough to break the application in its current state. After the test is written, all development must focus on making the software pass the test.

TDD proceeds in cycles which are usually called “red, green, refactor.” This cycle is usually done once for each unit test that you write. The cycle consists of three stages:

  • Red: Write a unit test that fails due to missing functionality in the software (since the color red usually denotes failure).
  • Green: Write code that fixes the issue by making the software pass the test (since the color green usually denotes success).
  • Refactor: Clean up the code base to account for the presence of this new code.

As with anything, there are no “universal truths” about software development, but these principles are mostly unchallenged - I promise, you will be happier, more successful and more productive, if you learn to follow these principles. 😊

2reactions
yisarcommented, Nov 12, 2019

Yes, definitely. Refs themselves can have direct effects on the DOM (or other global state) and these types of effects aren’t complete without a clean-up cycle - just as useEffect would not be very useful if it didn’t support clean-up.

If so, I need to refactor it again.

And about tests, I would like to comply with the tes, after we discuss the results, I will try to update the tests and make a report.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Cleanup ref issues in React
Just save the current ref value to a locally scoped variable to be closed over in the effect's cleanup function. React Hook useEffect...
Read more >
React callback ref cleanup function · Issue #15176
It's works for me. I lost a lot of time to use useEffect, but it's hard to catch unmount event. fer function helps....
Read more >
Cleanup redundant refs/keep-around (#19225) - GitLab.org
Under this scheme, we'd've only kept around abe (because the others are redundant), but then if you delete abe , we'd need to...
Read more >
ReFS - reclaim space on array?
To clean up all unused disk space, set DisableDeleteNotify to zero and run either in Command Prompt "defrag C: /L /V" or in...
Read more >
1448820 – [Ref-Arch] HA cleanup steps for satnode2 are missing
Bug 1448820 - [Ref-Arch] HA cleanup steps for satnode2 are missing. Summary: [Ref-Arch] HA cleanup steps for satnode2 are missing ...
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