Lesson 13-Preact Basics and Usage

Preact is a fast, lightweight alternative to React, designed for high performance. With a size of just 3kB, it offers an API closely aligned with React, making it an ideal choice for building modern web applications in resource-constrained environments.

Preact Fundamentals

Introduction to Preact

Preact is a fast, lightweight alternative to React, optimized for performance. At only 3kB, it provides a React-like API, making it perfect for creating modern web applications in environments with limited resources.

Features and Benefits of Preact

  1. Lightweight: Preact’s core is only 3kB, significantly smaller than React, resulting in faster loading and lower bandwidth usage.
  2. High Performance: Its compact size and efficient rendering mechanism deliver exceptional performance.
  3. React Compatibility: Preact’s API mirrors React’s, enabling easy migration from React to Preact.
  4. Rich Ecosystem: Preact supports most React ecosystem libraries and tools, such as Redux and React Router.
  5. Ease of Use: Preact’s API is simple and intuitive, making it easy to learn and implement.

Similarities and Differences Between Preact and React

Similarities

  1. API: Preact’s API closely resembles React’s, including components, state, and props.
  2. Component-Based: Like React, Preact allows developers to create reusable components.
  3. Virtual DOM: Preact uses virtual DOM technology to enhance rendering performance.

Differences

  1. Size: Preact is only 3kB, while React is larger and slower to load.
  2. Performance: Preact’s smaller footprint often makes it faster than React.
  3. Features: Preact lacks some of React’s advanced features, like the Context API (available via plugins).
  4. Community and Ecosystem: React has a larger community and more third-party libraries and tools.

Installing Preact

Install Preact using npm or yarn:

npm install preact
# or
yarn add preact

Preact Basic Concepts

Creating Components

Preact components can be either class components or function components, similar to React.

Function Component:

import { h } from 'preact';

const Hello = ({ name }) => {
  return <h1>Hello, {name}!</h1>;
};

export default Hello;

Class Component:

import { h, Component } from 'preact';

class Hello extends Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

export default Hello;

Rendering Components

Use the render method to mount components to the DOM:

import { h, render } from 'preact';
import Hello from './Hello';

render(<Hello name="World" />, document.getElementById('root'));

Component State

Preact components can manage state, similar to React:

import { h, Component } from 'preact';

class Counter extends Component {
  state = { count: 0 };

  increment = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

export default Counter;

Lifecycle Methods

Preact provides lifecycle methods akin to React’s:

import { h, Component } from 'preact';

class LifecycleDemo extends Component {
  componentDidMount() {
    console.log('Component mounted');
  }

  componentDidUpdate() {
    console.log('Component updated');
  }

  componentWillUnmount() {
    console.log('Component will unmount');
  }

  render() {
    return <div>Check the console for lifecycle logs</div>;
  }
}

export default LifecycleDemo;

Preact Core Principles

Preact is a fast, lightweight React alternative focused on delivering a similar developer experience with a smaller footprint and higher performance. Its core concepts include component-based development, virtual DOM, and diff algorithms.

Component-Based Development

Function Components

Function components are the simplest form, ideal for stateless, straightforward components.

import { h } from 'preact';

const HelloWorld = () => {
  return <h1>Hello, World!</h1>;
};

export default HelloWorld;

Class Components

Class components are suited for complex components requiring state and lifecycle methods.

import { h, Component } from 'preact';

class HelloWorld extends Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

export default HelloWorld;

Component Composition

Components can be composed to build complex UIs.

import { h } from 'preact';

const Header = () => <h1>Header</h1>;
const Footer = () => <h1>Footer</h1>;

const App = () => (
  <div>
    <Header />
    <p>Main content</p>
    <Footer />
  </div>
);

export default App;

Understanding Virtual DOM and Diff Algorithm

Virtual DOM

The virtual DOM is a lightweight JavaScript object representing the DOM structure. It allows frameworks to efficiently manipulate and compute changes in memory before applying them to the actual DOM.

Example:

const virtualDOM = h('div', { id: 'app' }, h('h1', null, 'Hello, World!'));

Diff Algorithm

The diff algorithm is central to the virtual DOM, comparing two virtual DOM trees to identify changes and apply only the necessary updates to the real DOM.

Basic Diff Algorithm Steps:

  1. Node Comparison: Compare the root nodes of two virtual DOM trees. If types differ, replace the entire node.
  2. Attribute Comparison: If node types match, compare attributes. Update differing attributes.
  3. Child Node Comparison: Recursively compare child nodes, applying minimal changes.

Example:

// Initial virtual DOM
const oldVNode = h('div', { id: 'app' }, h('h1', null, 'Hello, World!'));

// New virtual DOM
const newVNode = h('div', { id: 'app' }, h('h1', null, 'Hello, Preact!'));

// Diff algorithm to compare and update the real DOM
function diff(oldVNode, newVNode, parent) {
  if (!oldVNode) {
    parent.appendChild(createElement(newVNode));
  } else if (!newVNode) {
    parent.removeChild(oldVNode._dom);
  } else if (changed(oldVNode, newVNode)) {
    parent.replaceChild(createElement(newVNode), oldVNode._dom);
  } else if (newVNode.type) {
    const oldLength = oldVNode.children.length;
    const newLength = newVNode.children.length;
    for (let i = 0; i < oldLength || i < newLength; i++) {
      diff(oldVNode.children[i], newVNode.children[i], oldVNode._dom);
    }
  }
}

function changed(node1, node2) {
  return typeof node1 !== typeof node2 ||
         (typeof node1 === 'string' && node1 !== node2) ||
         node1.type !== node2.type;
}

function createElement(vnode) {
  const node = document.createElement(vnode.type);
  vnode.children.map(createElement).forEach(node.appendChild.bind(node));
  return node;
}

// Initial render
const root = document.getElementById('root');
root.appendChild(createElement(oldVNode));

// Update render
diff(oldVNode, newVNode, root);

Hello, World! Example

Let’s demonstrate Preact’s basic usage with a “Hello, World!” example.

Step 1: Install Preact

npm install preact

Step 2: Create Project Files

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Preact Hello World</title>
</head>
<body>
  <div id="root"></div>
  <script src="bundle.js"></script>
</body>
</html>

index.js

import { h, render } from 'preact';

// Create a simple function component
const HelloWorld = () => <h1>Hello, World!</h1>;

// Render the component to the DOM
render(<HelloWorld />, document.getElementById('root'));

Step 3: Configure Babel and Webpack

Install Babel and Webpack:

npm install --save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader webpack webpack-cli webpack-dev-server

Create the Babel configuration file .babelrc:

{
  "presets": [
    ["@babel/preset-env", { "targets": "> 0.25%, not dead" }],
    ["@babel/preset-react", { "pragma": "h", "pragmaFrag": "Fragment" }]
  ]
}

Create the Webpack configuration file webpack.config.js:

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  devServer: {
    static: path.join(__dirname, 'dist'),
    compress: true,
    port: 9000
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  },
  resolve: {
    alias: {
      'react': 'preact/compat',
      'react-dom/test-utils': 'preact/test-utils',
      'react-dom': 'preact/compat'
    }
  }
};

Step 4: Build and Run the Project

npm run build
npm start

Open your browser and visit http://localhost:9000 to see “Hello, World!”.

Preact Basic Applications

Installing Preact

Ensure Node.js and npm are installed. Create a new project directory and initialize it:

mkdir preact-app
cd preact-app
npm init -y

Install Preact and related dependencies:

npm install preact
npm install --save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader webpack webpack-cli webpack-dev-server

Configuring Babel and Webpack

Create the Babel configuration file .babelrc:

{
  "presets": [
    ["@babel/preset-env", { "targets": "> 0.25%, not dead" }],
    ["@babel/preset-react", { "pragma": "h", "pragmaFrag": "Fragment" }]
  ]
}

Create the Webpack configuration file webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  devServer: {
    static: path.join(__dirname, 'dist'),
    compress: true,
    port: 9000
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  },
  resolve: {
    alias: {
      'react': 'preact/compat',
      'react-dom/test-utils': 'preact/test-utils',
      'react-dom': 'preact/compat'
    }
  }
};

Creating Project Files

Create the HTML file index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Preact App</title>
</head>
<body>
  <div id="root"></div>
  <script src="bundle.js"></script>
</body>
</html>

Create the JavaScript file src/index.js

import { h, render } from 'preact';

// Create a simple function component
const HelloWorld = () => <h1>Hello, World!</h1>;

// Render the component to the DOM
render(<HelloWorld />, document.getElementById('root'));

Building and Running the Project

Add build and start scripts to package.json:

"scripts": {
  "build": "webpack",
  "start": "webpack serve"
}

Run the following commands to build and start the project:

npm run build
npm start

Visit http://localhost:9000 in your browser to see “Hello, World!”.

Understanding the render and h Functions

h Function

The h function is a factory function provided by Preact to create virtual DOM nodes, similar to React’s createElement. It builds a virtual DOM tree, which Preact’s diff algorithm converts into actual DOM operations.

Example:

import { h } from 'preact';

// Create a virtual DOM node
const vnode = h('h1', null, 'Hello, World!');

This code generates a virtual DOM node representing <h1>Hello, World!</h1>.

render Function

The render function mounts a virtual DOM tree to the actual DOM, taking a virtual DOM node and a DOM container as arguments.

Example:

import { h, render } from 'preact';

// Create a simple component
const HelloWorld = () => <h1>Hello, World!</h1>;

// Render the component to the DOM
render(<HelloWorld />, document.getElementById('root'));

This renders the HelloWorld component into the DOM element with the ID root.

Component-Based Development

Component-based development breaks the UI into reusable components, which can be function or class components. Below is an example of creating and composing components.

Creating Components

Header.js

import { h } from 'preact';

const Header = () => <header><h1>Header</h1></header>;

export default Header;

Footer.js

import { h } from 'preact';

const Footer = () => <footer><h1>Footer</h1></footer>;

export default Footer;

App.js

import { h } from 'preact';
import Header from './Header';
import Footer from './Footer';

const App = () => (
  <div>
    <Header />
    <main>
      <h1>Main Content</h1>
    </main>
    <Footer />
  </div>
);

export default App;

Update index.js

import { h, render } from 'preact';
import App from './App';

// Render the App component to the DOM
render(<App />, document.getElementById('root'));
Share your love