Skip to content

create FAST multi-threaded React Apps - one App two threads

Notifications You must be signed in to change notification settings

shmuelhizmi/react-multi-threaded

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

React Multi-Threaded

create FAST multi-threaded React Apps - one App two threads

What is "React Multi-Threaded"

"React Multi-Threaded" is a (type/java)script framework that lets you transform your existing/new React-App from a single-threaded Web-App into a multi-threaded faster Web-App.

How does it work?

In "React Multi-Threaded" we have two different types of components

  • UI Component - UI Components are React components that run on the main thread, they run there since dom interactions must come from the main thread.
  • Layout/Logic Component - Layout/Logic-Components are components that run on the web-worker thread, they are used for data fetching, business logic, and high-level layouts.

with "React Multi-Threaded" you can build your app from a mix of those two types of components and "React Multi-Threaded" will separate them into 2 threads, one UI thread with your UI Components and one business logic web-worker thread with your Layout/Logic Components

How do I get started

start by installing the "React Multi-Threaded" along-side all of the regular react apps dependencies, you can install "React Multi-Threaded" with npm install react-multi-threaded or yarn add react-multi-threaded if you are using yarn.

the next step is to configure your Webpack configuration, we will configure it with two app entry points, one to the main thread and one for the web-worker.
// webpack.config.js example

const path = require("path");

module.exports = {
  mode: "none",
  entry: {
    main: path.join(__dirname, "src", "main.js"),
    worker: path.join(__dirname, "src", "index.jsx"),
  },
  target: "web",
  mode: "development",
  resolve: {
    extensions: [".js", ".jsx"],
  },
  module: {
    rules: [
      {
        enforce: "pre",
        test: /\.js$/,
        loader: "source-map-loader",
      },
      // here place your babel or typescript loader
    ],
  },
  output: {
    filename: "[name].bundle.js",
    path: path.resolve(__dirname, "dist"),
  },
};

we also need to create an Html index and initialize our web-worker in it. // html index.html example

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>DEMO</title>
  </head>
  <body>
    <div id="main" />
    <!-- main thread entry point -->
    <script src="main.bundle.js"></script>
    <script>
      // worker entry point
      new Worker("./worker.bundle.js");
    </script>
  </body>
</html>

now that we got done with the technical stuff, let's move on to some programing!
we first need to create a basic react-app although with one simple exception - we need to separate our UI components from our layout components

let's start by creating some UI components

// src/components/UI/Home.jsx

import React from "react";
import { UIComponentProps, AsUIComponent } from "react-multi-threaded";


const Home = (props) => {
  return (
      <div>
        <h1>Hello - {props.username}</h1>
        {props.children}
        <button onClick={() => props.logout()}>logout</button>
      </div>

  )
}

export default AsUIComponent(Home);

// src/components/UI/Login.jsx

import React from "react";
import { UIComponentProps, AsUIComponent } from "react-multi-threaded";

class Login extends React.Component {
  state = {
    username: "",
    password: "",
  };
  render() {
    return (
      <div>
        <input
          type="text"
          onChange={(e) => this.setState({ username: e.target.value })}
          placeholder="username"
        />
        <input
          type="text"
          onChange={(e) => this.setState({ password: e.target.value })}
          placeholder="password"
        />
        <button
          onClick={() =>
            this.props.login(this.state.username, this.state.password)
          }
        >
          LogIn
        </button>
      </div>
    );
  }
}
export default AsUIComponent(Login);

// src/components/UI/Prompt.jsx

import React from "react";
import { AsUIComponent, UIComponentProps } from "react-multi-threaded";


const Prompt = (props) => {
  return (
    <div>
      <h1>{props.message}</h1>
      {props.children}
      <button onClick={() => props.onOk()}>ok</button>
    </div>
  );
}

export default AsUIComponent(Prompt);

// src/components/UI/Gif.jsx

import React from "react";
import { UIComponentProps, AsUIComponent } from "react-multi-threaded";

const Gif = (props) => {
  return (
    <div>
      <img src={props.url} />
    </div>
  );

}

export default AsUIComponent(Gif);

now that we are done with the UI components let's move on to creating some layout components, those components will define our app logic and layout.

// src/components/Layout/App.jsx

import React, { useState } from "react";
import Gif from "../UI/Gif";
import Home from "../UI/Home";
import Login from "../UI/Login";
import Prompt from "../UI/Prompt";

const App = () => {
  const [location, setLocation] =
    (useState < "home") | "error" | ("login" > "login");
  const [name, setName] = useState("");
  return (
    <>
      {location === "home" && (
        <Home logout={() => setLocation("login")} username={name}>
          <Gif url="https://upload.wikimedia.org/wikipedia/commons/7/78/GRACE_globe_animation.gif" />
        </Home>
      )}
      {location === "login" && (
        <Login
          login={(username, password) => {
            if (password === "0000") { // only log-in if the pasword is 0000
              setName(username);
              setLocation("home");
            } else {
              setLocation("error");
            }
          }}
        />
      )}
      {location === "error" && (
        <Prompt message={"worng password"} onOk={() => setLocation("login")}>
          <Gif url="https://upload.wikimedia.org/wikipedia/commons/7/78/GRACE_globe_animation.gif" />
        </Prompt>
      )}
    </>
  );
};

export default App;

congrats we finished creating our entire app UI and layout, we now will need to set-up two app entry points for our app, one for the main thread js bundle and one for the web-worker js bundle.
the main thread index will be called main.jsx

// src/main.js example

import React from "react";
import { render } from "react-dom";
import { MainThreadClient } from "react-multi-threaded";
import { createClient } from "react-multi-threaded";
import Home from "./components/UI/Home";
import Login from "./components/UI/Login";
import Prompt from "./components/UI/Prompt";
import Gif from "./components/UI/Gif";

// we are creating a client and passing it all of our UI-Components
render(
  <MainThreadClient UIComponents={[Home, Login, Prompt, Gif]} />,
  document.getElementById("main")
);

that's should be it for the main thread index, let's move on to the web-worker thread index. the web worker index will be called index.jsx in our example

// src/index.jsx example

import React from "react";
import { RenderApp } from "react-multi-threaded";
import App from "./components/Layout/App";

RenderApp(<App />);

It is finished, we now have a multi-threaded react app example screenshot

we can see in the screenshot above that the App component from `App.jsx` is missing in the react dev-tools tree, that is because it is running it on a separate web worker