3

Let me start off by saying I understand you should never use refs.

With that said, I think in my scenario it is very valid. Specifically I am using a window.onscroll handler and cannot simply change props in the onscroll handler. Changing state or props in an onscroll handler rerenders child componentsfar too often, which causes way too much lag.

Hence since my scenario only requires setting the style of an element on scroll, (i.e. I make something sticky to the top of page when it is scrolled out of view), I get 0 lag by simply setting this.refs.myelement.style.position = 'fixed'

My problem is that I am currently doing:

this.refs.childelement.refs.childschildelement.refs.childschildschildselement.refs.style.position = 'fixed'

Basically I have my onscroll handler 3 parents abvoe on the actual element I want to style.

My page consists of a List component. This consists of ReadOnlyOrEditable components. Editable components are RichTextEditors which have a Toolbar Component. It is this Toolbar that I need to set the position to fixed when a window scroll event occurs.

Hence on a page I'll have some 10 ReadOnlyOrEditable components, of which maybe 5 are RichTextEditors, each having a Toolbar.

The toolbar has buttons like italics, bold, etc.

When the user scrolls down, we don't want the user to have to scroll back up if the toolbar goes out of view. I.e. so we want to set the position to be fixed if the toolbar goes out of view so that the user can hit italics / bold / etc. immediately.

So how do I make this nicer without having to pass refs from the top down? The way I currently have it is very ugly...

I prefer to keep the onscroll handler close to the location of the list because having window.onscroll handlers in each Toolbar component implies I am setting multiple window.scroll handlers which is unnecesary work on the browser.

Any suggestions is gladly welcome.

Terence Chow
  • 10,755
  • 24
  • 78
  • 141

2 Answers2

0

Like everything, I believe there are many ways to solve one problem and here's how I would tackle it.

  1. I would abstract the responsibility of registering/unregistering to a node scroll events and firing custom events at predetermined positions to a "service".
  2. The scrollable component or the app container (List component?) would be the glue between the scroll notification service and its children
  3. The scrollable component would pass in a custom registration function to its children via props. This custom registration function would receive the node (Toolbar) to monitor for positions and a callback to set/unset position fixed
  4. The children (RichTextEditor component) would call the function (e.g. onRegisterPinableToolbar ) on componentDidMount with the node from ReactDOM.findDOMNode and a callback that the child can use to modify it's node style.

Notes:

  • Of course you would also need to to cleanup on componentWillUnmount.
  • Don't know much about your application but I would try to create a HOC for the Editor, e.g. PinableToolbarRichTextEditor
  • By abstracting the register to scroll events to a service you can optimize using requestAnimationFrame, and provide fallbacks for older browsers.
  • The function passed to the children would look something like this (node:Node, setPinability:Function) => unregister:Function

I know the naming pinability is not the best

guzart
  • 3,700
  • 28
  • 23
0

Instead of trying to get a ref to a deeply nested component, pass down the 'fixed' state as a prop, and let the great-great-(great?)-grandchild check the property and set its own style.

Radio-
  • 3,151
  • 21
  • 22