How the React Pattern Applies Outside of JS: A Case Study
Tags: python, react, webdev
Published: 2024-06-16 11:07:23

Looking from an oblique angle, React solves a peculiar problem: how to make a static markup language act dynamic. Judging by React's popularity, most of us would agree that React does a particularly good job solving this problem, and code using this ideology of component rendering often leads to self-contained reusable modules.

As an ex-web developer, I always prefer React over any other framework. However, the general design patterns have a much broader use case than solely web development.

### The Problem I was Facing

For quite an extensive amount of time, I have been working with Deepmind's simulation software **Mu**lti-**Jo**int dynamics with **Co**ntact, or MuJoCo for short. For our argument, it does not matter too much what exactly MuJoCo is. We will only focus on its scene definition. By design, simulated environments are compiled from XML files, which define the layout of the simulated scene and its objects.

Just to have an idea, you can find below a minimal example with two cubes, a sphere, and a ground plane.

```xml
<mujoco model="simple_scene">
    <worldbody>
        <!-- Ground plane -->
        <geom name="ground" type="plane" size="10 10 0.1"/>

        <!-- Red box with 6DOF joint -->
        <body name="box_one" pos="0 0 0.1">
            <freejoint />
            <geom name="box_geom" type="box" size="0.1 0.1 0.1"/>
        </body>

        <!-- Blue box with 6DOF joint -->
        <body name="box_two" pos="0 0 0.5">
            <freejoint />
            <geom name="box_geom" type="box" size="0.1 0.1 0.1"/>
        </body>

        <!-- Small sphere -->
        <body name="small_sphere" pos="0 0.5 0.1">
            <geom name="sphere_geom" type="sphere" size="0.05"/>
        </body>

		<!-- would be nice to add other objects -->
    </worldbody>
</mujoco>
```

This scene is particularly boring as not much will happen, but the parallel to HTML should be quite obvious. The problem arises when one wants something more dynamic with a variable number of objects. In my case, I had to dynamically exchange robot grippers and random objects throughout various scenes.

So, my goal was to recreate the basics of React for better handling.

It turned out much simpler than I previously imagined.

### The Basics of React

At a high level, React provides a simple framework with a custom syntax called JSX to specify components. Components are self-contained units that maintain their own logic and markup. Components can also consist of other components, thus creating a hierarchy.

This creates a tree structure that mimics the actual Document Object Model (DOM) in the browser. To generate the final HTML, React uses a “render” method on the root component, which recursively calls the “render” method on all nested components.

Of course, modern React is much more complex than this, but this covers the basics and is all we need.

### Mimicking React in Python 

MuJoCo is written in C/C++ and provides native Python bindings for quick prototyping. As I did not plan to recreate JSX inside of Python, I simply opted for the multiline string using placeholders.

In the example MuJoCo scene above, this would translate to:

```python
XML = """
<mujoco model="simple_scene">
    <worldbody>
		{objects}
    </worldbody>
</mujoco>
"""
```

Fortunately, Python comes with a string formatting convention, allowing us to use named placeholders (`{objects}` in this example) for future replacement using the `format(object="string for replacement")` method call on the template string.

This looks promising.

Similarly, we can create an XML template for the objects. Here is an example template string that allows for custom size settings for a box object:

```python
XML = """
<body name="{name}" pos="0 0 0.1">
    <freejoint />
    <geom name="box_geom" type="box" size="{size}"/>
</body>
"""
```

Next, we need an interface for each component to make them share a method required for rendering each component:

```python
from abc import ABC, abstractmethod

class MjXML(ABC):
	
	@abstractmethod
	def to_xml(self) -> str:
		pass
```

In the final XML assembly step, we will call on each component the `to_xml` method, which will output the resulting string for each component that will be used in the template.

Next, we implement the corresponding component for the example object above:

```python
XML = """
<body name="{name}" pos="0 0 0.1">
    <freejoint />
    <geom name="box_geom" type="box" size="{size}"/>
</body>
"""

class SimpleBox(MjXML):
	def __init__(self, name: str, size: float):
		self.name = name
        self.size = size

	def to_xml(self):
		box_xml = XML.format(name=self.name, size=self.size)
		return box_xml
```

Finally, we implement the component for the scene:

```python
XML = """
<mujoco model="simple_scene">
    <worldbody>
		{objects}
    </worldbody>
</mujoco>
"""

class SimpleScene(MjXML):
	def __init__(self, object_list: List[MjXML]):
		self.object_list = object_list

	def to_xml(self):
		obj_xml = " ".join([obj.to_xml() for obj in self.object_list])
		full_xml = XML.format(objects=obj_xml)
		return full_xml
```

Essentially, this is already a minimal workable example.

To actually simulate, we would need to construct the model and data of MuJoCo using our constructed XML string. As this article is not about MuJoCo, I have excluded the simulation part from this code sample.

### Possible Extensions

While this example covers the basic concept, my specific use case required additional features such as dynamic loading of assets, randomizing names, and custom collision logic. Let me know, if you are interested in more complex physical simulations.

However, these additional features might overcomplicate the core message I want to convey: if you are working with static markup that needs dynamic behavior, consider using a component-based rendering mechanism similar to React. It can simplify development and enhance the flexibility of your applications.