Tech Talk: React in the Terminal? Hold my Beer 🍺
Manuel Artero Anguita 🟨
Posted on December 20, 2023
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:
The slides: up & running hosted at Vercel; I used SliDev, 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 🤔.my-react-dom: Repository with a custom implementation of
react-dom
used to live coding. Two branches:playground
andmain
ink-world: Repository for a tiny-tiny-tiny game using Ink + React on the terminal again, used to live coding. Two branches:
playground
andmain
.
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
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.
Next step: react-reconciler
: the package the React team provides to create your custom renderer.
Time to live coding session (my-react-dom
)
This is the goal: replace the ReactDOM
object with one of our own and keeping the Counter APP running.
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/
If you open de dev tools, you may check the console:
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.
Ink is a React renderer for the terminal.
I did prepare a playground (check the playground
branch) with just 2 dependencies: ink
and react
You can check the final code at GitHub. Anyway, in a nutshell:
...
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:
Honestly, it was a tremendous success; many people congratulated me.
thanks for reading 💛.
Posted on December 20, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.