Hacker News new | ask | show | jobs
by skrebbel 4000 days ago
we looked at what you can do with a ref to a DOM component and realized that the only useful thing you can do with it is call this.refs.giraffe.getDOMNode() to get the underlying DOM node. In this release, this.refs.giraffe is the actual DOM node.

This only holds if you use Facebook's particular flavour of Flux, I guess.

Personally, I use refs a lot to bubble events down the component hierarchy. For example, when the browser window loses focus and gains it again, some sub sub component of the root component might want to do set some local state. Whatever, display a "welcome back!" message or something. I can use refs to do this.refs.subcomponent.onWindowShow() and this makes my code very clean and flexible - it keeps browser event stuff out of my flux stores, and only the actual important user data in there.

Similarly, I use refs to ask an <Input> component whether its value is valid according to some validation rule. I could also give the Input an onValueValidityChanged prop, but that just increases the amount of bookkeeping I need to do. If I'm rendering a form with 2 inputs, name and email, calling "this.refs.name.isValid()" in some onButtonClick handler makes a lot of sense to me.

I really liked how React was separated from particular architecture patterns, but this change makes that a lot more difficult. I really hope this change can still be reversed.

3 comments

This is only true for DOM components, e.g. an <input> component, which can't have a onWindowShow() method associated with it.

Customer components, e.g. an <MyInput> component that wraps a DOM <input> and provides custom onWindowShow() functionality are unaffected.

Ahhhh of course! OK that makes a lot of sense. Actually, it makes is more uniform: a ref has whatever properties and methods that ref could possibly expose - be it a "built-in" component (aka a DOM element) or a custom one. Nice!
Personally, I use refs a lot to bubble events down the component hierarchy. For example, when the browser window loses focus and gains it again, some sub sub component of the root component might want to do set some local state. Whatever, display a "welcome back!" message or something

Isn't that a bit of an antipattern? It seems that sending events down the hierarchy is something that React goes out of its way to make awkward, and on purpose.

In the case you've given, I'm not clear why you wouldn't just bind to the window's focus and blur events from within componentWillMount, anyway.

I'm not sure that it is an antipattern. Personally, I feel that attaching event handlers to global window events from all over the place is just as bad, basically a singleton in disguise.

Another use case that I came across was where we wanted a search dropdown to open in the header when a user clicked somewhere in the middle of the page content. One option would be to make a PageStateStore and add an isSearchDropdownVisible to it, but I don't like doing flux that way because I don't want UI details to be part of my model later.

I think having isVisible in the SearchDropdown state is much more elegant, but that poses the slight problem of how to change that state from the other side of the page. We did that by having the item in the page have an onWantToSearch event, which the page had a handler for that invoked this.refs.header.showSearch(), which in turn invoked this.refs.searchDropdown.setVisible(true), which did a simple this.setState({isVisible: true}).

So, in general, the event first bubbles all up the tree (through custom react onSomething, onSomethingElse props), and then bubbles down the tree (through method calls on refs). This exposes a really elegant design, because it made the Header component be "a thing that allows searching" (because it has a showSearch() method), and the page item an "a thing that allows a user to initiate a search action" (because it has a onWantToSearch event). The available methods and onSomething props of all components perfectly matched the possible behaviors of the components, and no behaviour is "hidden" inside a singleton mesh of flux stores.

Props are awkward on purpose but not to discourage their use, they're awkward so you're explicit about how you're modeling your data and how your data flows through your app/component tree.
You can cleanly implement that "welcome back!" example using flux actions and stores, without having to bubble down events.
You must have misread the blog post. This change only affects DOM components like <div />, not composites like your <Input />. I wrote in the post:

References to custom component classes work exactly as before.

Yep, I'm blind :-) Thanks!