You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Like many others, I've been experimenting with Turbo and Turbo Frames. We have a large set of one-off React components we like to sprinkle into our templates using react-rails and that pattern works great for enhancing interactivity.
But, like many others, we quickly realized that Turbo doesn't emit any events when a frame loads. This is by design and their logic makes sense.
To fix this we've put together a quick proof of concept MutationObserver that watches the document tree for changes. These changes could come from any source, but in our case it's always a Turbo Frame load. I don't think there any need to differentiate based on the source of the change.
importReactfrom'react'importReactDOMfrom'react-dom'declareconstReactRailsUJSdocument.addEventListener("DOMContentLoaded",()=>{constfindComponents=(childNodes: NodeList,testFn: (n: Node)=>Boolean,nodes: Node[]=[]): Node[]=>{for(letchildofchildNodes){if(child.childNodes.length>0){nodes=findComponents(child.childNodes,testFn,nodes)}elseif(testFn(child)){nodes=nodes.concat([child])}}returnnodes}constmountComponents=(nodes: Node[])=>{for(letchildofnodes){constclassName=(childasElement).getAttribute(ReactRailsUJS.CLASS_NAME_ATTR)if(className){// Taken from ReastRailsUJS as is.constconstructor=ReactRailsUJS.getConstructor(className)constpropsJson=(childasElement).getAttribute(ReactRailsUJS.PROPS_ATTR)constprops=propsJson&&JSON.parse(propsJson)// Improvement:// Was this component already rendered? Just hydrate it with the props coming in.// This is currently acceptable since all our components are expected to be reset// on page navigation.constcomponent=React.createElement(constructor,props)asanyReactDOM.render(component,childasElement)}}}constcallback=function(mutationsList: MutationRecord[],observer: MutationObserver){conststart=performance.now()console.log("ReactRails: Mutation callback started...",mutationsList)for(constmutationofmutationsList){if(mutation.type==='childList'){if(mutation.addedNodes.length>0){constmountableNodes=findComponents(mutation.addedNodes,(child)=>{return!!(childasHTMLElement).dataset?.reactClass})mountComponents(mountableNodes)}}}console.log("ReactRails: Mutation callback complete.",performance.now()-start)};constobserver=newMutationObserver(callback)console.log("ReactRails: Start mutation observer...")observer.observe(document,{childList: true,subtree: true})})
We've simply added this to our application.js pack file and we've found that this works quite well.
Hopefully this helps someone else out and/or starts a discussion about moving react-rails to this model for mounting/unmounting components. It's a lot more robust than watching for Turbo events, I think, but I'm sure there's a ton of edge cases covered by the existing code.
Cheers!
The text was updated successfully, but these errors were encountered:
Hey all,
Like many others, I've been experimenting with Turbo and Turbo Frames. We have a large set of one-off React components we like to sprinkle into our templates using react-rails and that pattern works great for enhancing interactivity.
But, like many others, we quickly realized that Turbo doesn't emit any events when a frame loads. This is by design and their logic makes sense.
To fix this we've put together a quick proof of concept MutationObserver that watches the document tree for changes. These changes could come from any source, but in our case it's always a Turbo Frame load. I don't think there any need to differentiate based on the source of the change.
We've simply added this to our application.js pack file and we've found that this works quite well.
Hopefully this helps someone else out and/or starts a discussion about moving react-rails to this model for mounting/unmounting components. It's a lot more robust than watching for Turbo events, I think, but I'm sure there's a ton of edge cases covered by the existing code.
Cheers!
The text was updated successfully, but these errors were encountered: