(React-three-fiber) Trying to understand the react workflow

Hey everyone,

Preface: I have been using unity and unreal engine professionally since 2018 and have been a web developer since 2016. I would not call myself a React expert, but I know enough to be productive. Please feel free to give high or low level responses, I am just trying to get a gauge for how to go about developing with r3f and ensure I am not fighting with it.

TL;DR building dynamic game architectures with r3f seems like I am fighting the library. Not sure where to use vanilla Three.js and where to use r3f, and whether that’s a bad practice.

I have recently started working with r3f and while I have managed to get through the simple stuff rather easily, .e.g., loading and viewing .gltf models, setting up cameras, and scenes, updating models in the game loop, etc., but now I am beginning to wonder how I should go about architecting more “game-like” and architecturally scalable systems.

From what I can tell one of the selling points of r3f is the ability to setup scenes, meshes, essentially all Three.js components declaratively. I see the utility in that sort of workflow for web developers who just want to setup simple scenes for a web page or share a demo, but building a game requires much more than the objects in the scene at the first render. Three.js makes handling managing object instances, adding and removing objects, etc., rather easy, but it is unclear how might be the best way to do the same in r3f. The docs seem to assume most of what you’d want to add would be known prior to first render and provide examples for doing that.

Most examples and projects I’ve scene generally build very simple/static, albeit pretty scenes. I also noticed that most use a state library for managing state across their components, which seems weird because Three.js already stores most information about the scene, object, etc., but I can see why they do it.

Formalized question: How should I manage scene objects (add, remove, get), update game object properties, manage object relationships, etc., within the r3f workflow.

neither threejs nor fiber are game engines, you will have a hard time with both. you need things like game controllers, ecs, etc. these things are currently being built for fiber with some companies investing into that. your best way forward would be to visit the Poimandres game dev channel where you can chat with the devs directly. one example for ecs is miniplex. but it’s currently all going towards r3f v9, which is the first release that will make it game-ready at least so tooling around that will be a lot easier to establish. think rendering stages, better clocks, damping, support for editors and level-editors, etc.

the simple setups and sites you see are just demos, they’re small so people can grasp a concept or feature. fiber is being used for real applications, the cad-type usually, or things like flux.ai. there are also first games popping up, sougen for instance or gucci. i think whenever you go into any scale react will prove a benefit, but again, games need different constructs.

TL;DR building dynamic game architectures with r3f seems like I am fighting the library. Not sure where to use vanilla Three.js and where to use r3f, and whether that’s a bad practice.

as a rule of thumb, you never add/remove stuff. you mount. react gives you an outlet to manage your code and separate concerns, or execute imperative side effects.

  • useMemo for local dynamic state and or imperative calculations
  • useState for local static state
  • useEffect for side effects that execute after render
  • useLayoutEffect for side effects that execute before threejs render out
  • useFrame for any frame loop based stuff, animations etc

for games the biggest problem is awareness. ecs games usually have means to query very quickly: give me all npc’s in the vicinity of x deg with life > 0.5 weapons > 1 and make them attack. this is what miniplex would already give you.

ps here’s an article about that Simplifying React Three Fiber with Entity Component System · douges.dev

Thanks for the response!

I did just discover miniplex and was looking into using it moving forward. Concerning your mentioning of mounting/unmounting is there any benefit to having react manage the objects being added or removed from a scene? You said that this should be done with mounting, but isn’t object instancing in threejs just adding elements to the WebGL canvas and not the DOM? If so I’m not sure why having react manage that is necessary. There’s a lot I don’t know here, so excuse me if I’ve misunderstood something.

I know r3f does take a different approach in that objects or models are actually jsx components so in that sense I can see why mounting/unmounting is important.

in react the view is a function of state.

a function has input, a body, and output. given const foo = (a) => a + 1, foo(1) will always return 2. a deterministic pure function in other words. react works the same way, state in, view out.

const Red = ({ children }) => <div style={{ color: "red" }}>{children}</div>

<Red>hi</Red> -----> <div style={{ color: "red" }}>hi</div>

the component doesn’t add or remove stuff, that is the job of the reconciler. it just returns a result. and if something is in it that wasn’t there before it will be mounted, vice versa if it’s missing, then it will be removed. if it existed previously it will be updated.

this is what makes you app deterministic, stable, predictable and fast. you don’t add something into somewhere, you don’t mutate parent.parent.parent.foo = bar, because that is what causes fragility. everything is the outcome of state.

other than that,

only that which needs to update will, it will manage memory and auto dispose, when push comes to shove it will outperform threejs without trying, see React Three Fiber Documentation not that you would ever add thousands of objects runtime, but still.

Thanks again for the detailed responses!

I am pretty familiar with how react’s engine manages the virutal dom and state across an app, but what I’m trying to understand is why we would want a game engine to use react’s scheduler and rendering engine to manage scene data, especially if scene data is in the canvas, and not the dom. There might be something here that I haven’t fully grokked yet. I checked out the example you mentioned and the github repo linked in it, good job btw! Its really impressive how performant react’s concurrency model will be moving forward, but I am unsure if sharing the scheduler is a great idea if you are going to be running anything more than just a game at once, e.g., if your use case is to use three in some parts of your app, but you are not building a game… think Sketchfab, Itch.io, etc. There’s a lot of other things going on in those apps and I can’t imagine their renderers sharing the same scheduler for updating state across the app. If that is the case, then that’s awesome because it drastically simplifies a lot, but I’m not sure. So is the idea then just to leverage react’s scheduler to manage performance in three and what is it about that which makes it so much more performant than the vanilla three tests you performed? Is it the deferred instancing due the balancing of the scheduler?

P.S. I found the link you left to Dan’s talk on the repo so I will check that out. Do you think react’s automatic balancing will be something developers would like?

you let react build the scaffold. a game probably just mounts everything, it doesn’t add stuff runtime, except new levels. it will much rather use lod’s and things like that. building the scaffold has still benefits in react because it will be clean, easy to read, safe, especially async and lazy loading is inbuilt into react with suspense. mounting a level becomes {condition && <Level />} and that is it.

imagine doing the same in javascript. updating heads up display, cleaning up stuff, mutating here, deleting that, loading this, adding it when ready, keeping async in sync, hopefully no race conditions (there will be). the updater has to pretty much touch every corner of the codebase there is, no separation of concerns whatsoever, and just to load a new scene.

the actual game will function through ecs, for this react is not relevant. you use it

  • to keep overall state and sanity
  • for re-usability and forming sharable abstractions
  • for memory concerns and speed
  • to benefit from the eco system

much of that will clear up this year, there will be a lot more tooling and libraries around games.