I’m teaching a course in React 18/19 and am trying to explain how React Server Components fundamentally work. Inspired by Dan Abramov’s presentation at ReactConf2004. (https://www.youtube.com/watch?v=T8TZQ6k4SLE&t=18825s).
I create a simple server.js script that runs from node
. It basically statically renders a list of numbers, and then on button click adds to the list a new number. The important part is that when adding, it does not refresh the page and simply adds a new number to the list. Here is the non-react code that does that.
const http = require('http');
// Global variable to store the numbers array
let numbers = [1, 2, 3, 4];
const generateListHTML = (numbers) => {
let listHTML = ' <ul>';
numbers.forEach(number => {
listHTML += `n <li>${number}</li>`;
});
listHTML += 'n </ul>';
return listHTML;
};
const generateButtonHTML = () => {
return `<button id="addItemButton">Add Item</button>`;
};
const server = http.createServer((req, res) => {
const initialListHTML = generateListHTML(numbers);
const buttonHTML = generateButtonHTML();
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Simple React App No JSX</title>
</head>
<body>
<div id="root">
${initialListHTML}
${buttonHTML}
</div>
<script>
// Access the global numbers array from the server
const numbers = ${JSON.stringify(numbers)};
const root = document.getElementById('root');
const addItemButton = document.getElementById('addItemButton');
addItemButton.addEventListener('click', () => {
const lastNumber = numbers[numbers.length - 1];
const newNumber = lastNumber + 3;
numbers.push(newNumber);
const newListItem = document.createElement('li');
newListItem.textContent = newNumber;
root.querySelector('ul').appendChild(newListItem);
});
</script>
</body>
</html>
`);
res.end();
});
server.listen(3000, () => {
console.log('Server running on port 3000');
});
I’ve been experimenting with solutions that are obviously wrong, but thought I just put them here to show my thinking.
import IncrementButton from "./increment-button";
let numbers = [1, 2, 3, 4];
export default async function Home() {
return (
<div>
<IncrementButton initialNumbers={numbers}>
<ul>
{numbers.map((number) => (
<li>{number}</li>
))}
</ul>
</IncrementButton>
</div>
);
}
'use client';
import {useState} from "react";
export default function IncrementButton({initialNumbers, children}) {
const [numbers, setNumbers] = useState(initialNumbers);
return (
<div>
<button id="addItemButton" onClick={() => {
const lastNumber = numbers[numbers.length - 1];
const newNumber = lastNumber + 3;
setNumbers([...numbers, newNumber]);
}}>
Add Item
</button>
{children}
</div>
);
}
Any suggestions for how to do this in React with RSC’s?