Tech Talk: React in the Terminal? Hold my Beer 🍺
Tags: react, node, javascript, typescript
Published: 2023-12-20 13:23:38

My company organizes a big event once per year. Around ~600 attendees. There are five simultaneous tracks for keynotes; Product, Project management, Design, Dev, Devops... It's the main event of the season for us. 

You get the idea. 

***

This year i decided to present a tech talk. 45 min long. The complete thing: slides & live coding. 

I named it: «React in the Terminal? Hold my beer 🍺»

***

While the slides are in English, i'm afraid the talk itself was in my mother language (🇪🇸) and i won't share the recording. 

However, here are the resources I used for the talk:

1. [**The slides**: up & running hosted at Vercel](https://react-in-the-terminal-hold-my-beer.vercel.app/1); I used [SliDev](https://sli.dev/), a framework that turns markdown into a web app which behaves like a Keynote. It's pretty cool. 
I genuinely think SliDev deserves its own post someday 🤔.

2. [**my-react-dom**: Repository with a custom implementation of `react-dom`](https://github.com/manuartero/my-react-dom) used to live coding. Two branches: `playground` and `main`

3. [**ink-world**: Repository for a tiny-tiny-tiny game using Ink + React on the terminal](https://github.com/manuartero/ink-world) again, used to live coding. Two branches: `playground` and `main`.

***

## Talk Recap 

I led a quick journey, starting with the simplest «Hello World» in React...

...But unrolling the `jsx` syntax, making more obvious where we're invoking `react` and where we are calling `react-dom`

![Slide no 7](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rt0t85k8bqte0papqdeb.png)

This led us to the question: why are `react` and `react-dom` separated into two different packages? 

We discussed the philosophy that React embraces; they mention "_interfaces_" and how React is **designed to plug into any interface**.

![Slide no. 8](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uhpyew3aaf6d9qwjmdkc.png)

Next step: `react-reconciler`: the package the React team provides to create your custom renderer.

![Slide no. 9](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rbxspmanrrqoeateetd1.png)

Time to live coding session ([`my-react-dom`](https://github.com/manuartero/my-react-dom))

This is the goal: replace the `ReactDOM` object with one of our own and keeping the Counter APP running.  

```diff
import React from 'react';
-import ReactDOM from 'react-dom';
+import ReactDOM from './my-custom-react-dom';
import './styles.css';
```

✨ Ah! and that's also deployed at Vercel: [https://my-react-hvpqtmgva-manu-artero.vercel.app/](https://my-react-hvpqtmgva-manu-artero.vercel.app/)


![Screenshot for https://my-react-hvpqtmgva-manu-artero.vercel.app/](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wegkej7ppn6x8pxg5aag.png)

If you open de dev tools, you may check the console:

![Screenshot of the console](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m4s87sr3j8op6fgipxr2.png)


***

At this point, I took a small leap: while implementing a tiny `react-dom` is one thing, crafting the plugin for the terminal is a different tale. 

So i introduced the **solution by Vadim Demedes**: [Ink](https://github.com/vadimdemedes/ink).

Ink is a React renderer for the terminal. 

I did prepare a playground ([check the `playground` branch](https://github.com/manuartero/ink-world/tree/playground)) with just 2 dependencies: `ink` and `react`

You can check the [final code at GitHub](https://github.com/manuartero/ink-world/blob/main/index.js). Anyway, in a nutshell:

```js
...

function useWorld({ onGameOver, onGameWin }) {
  const [world, setWorld] = useState([
    "tree",
    "character",
    "tree",
    "tree",
    "tree",
    "enemy",
  ]);

  useEffect(() => {
     ...
      if (random > 0.5) {
        return moveEnemyRight();
      }
      if (random < 0.5) {
        return moveEnemyLeft();
      }
    }
  });

  const moveEnemyRight = () => {
    setWorld((currentWorld) => {
      ... bla bla
    });
  };

  const moveEnemyLeft = () => {
    setWorld((currentWorld) => {
      ... bla bla
    });
  };

  const moveCharacterRight = () => ...

  const moveCharacterLeft = () => ...

  return { world, moveCharacterRight, moveCharacterLeft };
}


function App() {
  const { exit } = useApp();
  const { world, moveCharacterRight, moveCharacterLeft } = useWorld({...});

  useInput((input, key) => {
    if (input === "q") {
      exit();
    }
    if (key.leftArrow) {
      setDisplayText("<-");
      return moveCharacterLeft();
    }
    if (key.rightArrow) {
      setDisplayText("->");
      return moveCharacterRight();
    }
  });

  return (
    <>
      <Ground>
        {world.map((type, i) => (
          <WorldElement key={`${type}-${i}`} type={type} />
        ))}
      </Ground>
      <Text>{displayText}</Text>
    </>
  );
}

...

render(<App />);
```

Resulting in this little fella:

{% embed https://player.vimeo.com/video/896535470?h=968a3ce5a9 %}

***

Honestly, it was a tremendous success; many people congratulated me.

***

thanks for reading 💛.