DEV Community

Greggman
Greggman

Posted on

Heavy Resource References in React

I'm curious if there are any good examples or best practices of managing heavy resources with react. I'm not sure if I can give any good examples but I'll try.

Typical heavy resources are images. Images seem to generally not be managed directly. They are referenced by URI and managed by magic behind the scenes.

const MyComponent = () => { return (
  <div class="recipe">
    <div class="title">Fruit Salad</div>
    <div>
      <img src="images/banana.jpg" />
      <img src="images/strawberry.jpg" />
      <img src="images/grape.jpg" />
    </div>
  </div>
  <div class="recipe">
    <div class="title">Banana Split</div>
    <div>
      <img src="images/ice-cream.jpg" />
      <img src="images/banana.jpg" />
      <img src="images/whipped-cream.jpg" />
    </div>
  </div>
)};

In the example above react declares <img> tags but the resources themselves are managed by the browser. The browser magically looks at the src property and loads the corresponding image. Even though both recipes refer to banana.jpg the actual data for the image will only be loaded once. If this part of the tree of nodes is not part of the active DOM the browser is free to release all those images from memory.

So, let's say we want to do something else just as resource intensive. I'm not sure what a good example is. Let's imagine it's a graph of sales.

I see lots of libraries that do something like this

const MyComponent = () => { return (
  <MyGraphComponent xaxis="date" yaxis="amount">
    <data>
      <datum xaxis="2012/01" yaxis="145" />
      <datum xaxis="2012/02" yaxis="121" />
      <datum xaxis="2012/03" yaxis="131" />
      <datum xaxis="2012/04" yaxis="152" />
      ... 2000 items ...
    </data>
  </MyGraphComponent>
)};

That seems wrong compared to the <img> example. Following the <img> example the data for the component should be external (orthogonal) to the <MyGraphComponent>. Like <img> the data is not children in the graph rather it's something that should be referenced somehow so that multiple components can access the same data.

Another example might be a 3D scene

const MyComponent = () => { return (
  <scene>
    <node transform="translate(10, 20, 30)">
      <sphere radius="1" radialDivisions="100" verticalDivision="50" />
      <material color="red">
    </node>
  </scene>
)};

Here again it looks innocent enough but again it's not following the example set by <img>.

We can imagine a scene with multiple versions of the same sphere. Following the <img> example we'd imagine something more like

const MyComponent = () => { return (
  <scene>
    <node transform="translateX(-5)" geometry="100-50-unit-sphere.geo" material="red-material" />
    <node transform="translateX(0)"  geometry="100-50-unit-sphere.geo" material="red-material" />
    <node transform="translateX(5)"  geometry="100-50-unit-sphere.geo" material="red-material" />
  </scene>
)};

making it clearer the example above this one was probably the wrong model.

We could try

const MyComponent = () => { return (
  const geometry = createSphere(100, 500);
  const material = createMaterial({color: 'red'});

  <scene>
    <node transform="translateX(-5)" geometry={geometry} material={material} />
    <node transform="translateX(0)"  geometry={geometry} material={material} />
    <node transform="translateX(5)"  geometry={geometry} material={material} />
  </scene>
)};

It's not clear how these would work. For <img> they are magically referenced by URIs but for other heavy resources it's much less clear what should happen and how they should be managed.

Maybe by naming things?

const MyComponent = () => { return (
  // only creates resource if resource for that name doesn't already exist
  createGeometry('100by500sphere', () => { return new Sphere(100, 500); });
  createMaterial('redMaterial', () => { return new Material({color: 'red'}); });

  <scene>
    <node transform="translateX(-5)" geometry="100by500sphere" material="redMaterial" />
    <node transform="translateX(0)"  geometry="100by500sphere" material="redMaterial"  />
    <node transform="translateX(5)"  geometry="100by500sphere" material="redMaterial"  />
  </scene>
)};

The above has the strange property that we're making things by name and assuming no name conflicts. But, it does follow the <img> example and works just like <img> in that names (including path) have to be unique.

Maybe another way would be to require declaring resources early like resource factories

resourceManager.registerResources({
  '100by500sphere': () => { return new Sphere(100, 500); },
  'redMaterial': () => { return new Material({color: 'red'}); },
});

then elsewhere

const MyComponent = () => { return (
  <scene>
    <node transform="translateX(-5)" geometry="100by500sphere" material="redMaterial" />
    <node transform="translateX(0)"  geometry="100by500sphere" material="redMaterial"  />
    <node transform="translateX(5)"  geometry="100by500sphere" material="redMaterial"  />
  </scene>
)};

Assuming the registering of resources happens in one place the naming conflict issue disappears and like images resources are defined external to the part react is managing. Of course some would argue it's no fun to do it this way where you can't just declare stuff inline like the first 3D example

And of course there is nothing saying the <img> way is the correct way. It's only the observation that the resources are not truly part of the tree of nodes being managed by React. They aren't children, they are orthogonal resources. And, that the only common similar examples in react are images, audio, and video, all of which are usually referenced by URI, not as children.

Are there any good references for doing this in the true spirit of React?

Top comments (0)