React Refs and Babylon Classes

Shout out to @brianzinn for posting an updated ReactJS Scene component to GitHub! Much appreciated. Now on to the next question.

So let’s say I’ve written a Typescript file with an exported class I use to construct 3D meshes. In that class I’m creating custom textures by drawing on a canvas element. Now that I’m working within React I probably should be passing in a ref to a canvas component, but how should I go about modifying that ref; an example would be updating the width/height, but I can’t use hooks or states because I’m not within a React class/functional component. I’ve read accessing the DOM outside the virtual DOM can lead to things getting out of sync.

Thoughts?

Cheers!

let s add @brianzinn here as I bet he already went through the same :slight_smile:

Hi @3D_Wave_Design - good questions. You are correct that the DOM and v-DOM will get out of sync if you make changes yourself, so you will want to control which side is making changes. If you want to flow the changes through using React then you could use middleware (like Redux or write a small one yourself) that modifies the context, which will propagate to your component like props. The advantage of the middleware is that both places can control the canvas element attributes and receive actions. I wrote an interceptor here (GitHub - brianzinn/redux-wiretap: add/remove listeners to monitor redux actions) that I used for a game that needed redux actions, if you are using redux.

It sounds like you want to simply modify the canvas element yourself, so you should ensure your react code is not modifying the properties you want to change. If you want to control width, height yourself then you could remove them from the spread operator in the example to ensure React doesn’t update them (ie: don’t allow prop1 and prop2 to be set on the canvas):

const { prop1, prop2, ...rest } = props;
...
return (<canvas ref={reactCanvas} {...rest} />);

If you had code that didn’t know about the React part of your application - I can think of 3 ways:

  1. Access the canvas through css selector and modify it.
  2. Access the canvas from a reference to the scene: scene.getEngine().getRenderingCanvas().
  3. Create a react portal to an existing canvas (https://reactjs.org/docs/portals.html)

If none of those work or you have more explanation of your use case we can find a solution. Cheers.

2 Likes

@brianzinn These are really great suggestions; I’ve done some top level research into Redux and it looks like it’s a solid option, but I’m a little nervous adding yet another module into the mix haha.

So would I be passing a store object into my Babylon class, which will fire a reducer and change my canvas at the component level?

Also, does this mean I have to ditch all the setState stuff moving forward, or can I use Redux sparingly?

If you aren’t using redux, I wouldn’t add it just for this. Redux just uses context API, which is now officially supported. If you are using setState() then you can pick up changes there as well and send as props to the canvas. I think the general trend is away from stateful components, but nothing wrong with going that way.

@brianzinn Maybe I’m over-complicating this. Does the V-DOM even need to be involved? In my class I create a canvas element, start feeding it image data, grab the URL for a Babylon Texture then canvas.remove().

The vdom is involved whenever you render react classes. For your use case it sounds like you could avoid React entirely and maybe even use a null engine. Are you loading images to make a screenshot and then loading in texture as a data URL?

@brianzinn Just for context, I’m actually developing LiDAR mapping applications for visualizing climate change and emergency management data. In the past, a canvas element was used for processing and modifying Digital Elevation Model data, along running simulations that use Babylon mix materials – nothing that should be rendered by React.

I’ve had some DOM elements act funny in the past, so I’ve opted to build our GUI with React. As a React newbie, I’ve read that interacting with the DOM is a no-no but I wasn’t sure to what extent I could get away with.

If I’m creating/removing elements in the background strictly for running sims that React doesn’t know exist, I may be good. I’ll only implement component communication when it makes sense, and not in TypeScript modules that are imported into the main tsx file – like my “CreateDEMQuads” class.

Thoughts?

If it’s just for running stuff in the background then it doesn’t look necessary to go through React - it just adds unnecessary overhead. That’s why I mentioned before the null engine, which can run server side. The only LiDAR stuff I’ve seen is using point cloud :slight_smile:

If you are doing heavy processing then this is a good starting point to keep your main thread responsive and you should be able to post a message back before tearing down:
https://doc.babylonjs.com/how_to/using_offscreen_canvas
If you go that way and have questions - start a new thread. I haven’t done a lot there.

1 Like

Thanks for this. I’ve done some webworker stuff in the past for these sims, but I wasn’t sure what was on/off the table having React involved. Honestly, it’s a huge sigh of relief knowing I can sneak in some background DOM manipulation for processing.

Also, no point clouds here my friend! All raster derived, and with great results! I’m hoping to share some of our work on this forum in the future! We’ve had great success using BabylonJS :grinning:

Thanks again.

1 Like