React Workspaces
Description / Purpose
Instead of using the production-style React rendering pipeline (i.e. what you see with create-react-app
or Next.js), we have a custom setup for running React workspaces. At the time of writing, this includes react-16
and react-18
workspace types.
Workspace Structure
To build a React workspace, you'll need index.html
, index.js
, and .babel-adapter.json
. The HTML file should import index.compiled.js
and our hosted React bundle. Here's an example:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="bundle.css" />
</head>
<body>
<main id="app"></main>
<script src="https://content.codecademy.com/courses/React/react-18-course-bundle.min.js"></script>
<script src="/index.compiled.js"></script>
</body>
</html>
index.js
import React from "react";
import ReactDOM from "react-dom/client";
function App() {
return <h1>Hello from the App component</h1>;
}
ReactDOM.createRoot(document.getElementById("app")).render(<App />);
.babel-adapter.json
{
"excludedModules": ["react", "react-dom"]
}
1. Import the react bundle
The React bundle used in the above index.html
is for react-18
.
<script src="https://content.codecademy.com/courses/React/react-18-course-bundle.min.js"></script>
If you are building in React 16, you'll use a different bundle: replace react-18
with react-16
in the src
attribute.
2. Use an existing DOM node
In index.js
, we select an existing DOM node in index.html
. In this case it's the element with id app
.
ReactDOM.createRoot(document.getElementById("app")).render(<App />);
3. You can import js files
You don't need to write everything in index.js
: you can define additional components in separate files and import them. For example, we could have defined the App
component in an App.js
file and imported it:
import { App } from "./App.js";
4. Use any pre-installed npm package
You can use any npm package available in the Docker image, such as prop-types
, react-redux
, and msw
. The full list of available packages for react-18 is in the ein repository here.
5. Do not include a package.json
For the sake of long-term maintainability, don't do it. If you need additional packages installed, talk to the Learning Platform team.
6. .babel-adapter.json
should always have the excludedModules
entry
This speeds up compilation and load time by skipping compilation of the react
and react-dom
packages. Those are pre-compiled and imported via the bundle (see #1).
Note: if you don't want to use index.js
or app.js
as the entrypoint, you can define one in this file. For example, if your JavaScript entrypoint is main.js
, you would have JSON like this (you would have to update index.html
to import main.compiled.js
as well):
{
"entryFiles": ["main.js"],
"excludedModules": ["react", "react-dom"]
}
7. Import bundle.css
(optional)
The react-18
workspace type supports .css
imports in JavaScript files. For example, one of your components could have the lines:
import styles from "./style.module.css";
// or
import "./App.css";
If you use this approach, you'll need to import bundle.css
in your index.html
file. bundle.css
will be automatically generated during compilation of your React components:
<link rel="stylesheet" href="bundle.css" />
Behind the scenes
When you have a react-16
or react-18
workspace type and you click Run with any .js
file open in the code editor, this script is executed.
- It reads the contents of
.babel-adapter.json
if it exists - It determines the file to compile:
index.js
,app.js
, or whatever is defined as the entry file(s) in.babel-adapter.json
. - It determines which modules, if any, to exclude from compilation. These are defined in the
excludedModules
array in.babel-adapter.json
. Usually we skipreact
andreact-dom
because those are pre-compiled and imported inindex.html
. - It compiles the file(s) with browserify and babelify. The output file is something like
index.compiled.js
orapp.compiled.js
, depending on your entry file name.
In most cases, this is the effective command that is run in the Docker container:
browserify index.js -o index.compiled.js -t babelify -p [ css-modulesify -o bundle.css ] -t imgurify --exclude react --exclude react-dom
Debugging
Since the compilation process happens in the Docker container, you won't see compilation errors in your browser or console. This is something we would like to fix in the future. For now, the best way to identify compilation errors is by inspecting the websocket connection.
- Use your browser's developer tools and open the network tab.
- Filter for websocket, or WS, request types (if you don't see any websocket requests, reload the page with the network tab open).
- Look for messages with the method
EvalService.Run
. - Check for
stderr
in the response. - If there is a string there, you have a compilation error. Copy and paste that base64-encoded string into a decoder, e.g. base64decode.org.
Examples
- Initialize State uses the
react-16
workspace type