Missing clean-up of refs
See original GitHub issueIt 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:
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:
- Created 4 years ago
- Comments:28 (12 by maintainers)
Top GitHub Comments
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.
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.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:
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. 😊
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.