React technical principles and code development practice: from React Bootstrap to Semantic UI

1. Background Introduction

React is one of the hottest front-end JavaScript frameworks currently. Its main advantages are simplicity, flexibility and performance. This article will combine my own research experience and work summary to share how to better understand and master some features and technical details of React.

React has become a mainstream front-end UI framework, and the JSX syntax that the underlying framework relies on has also received widespread attention from the community. This article will try to elaborate on these features and related key technologies in detail to help readers further improve their understanding and control of the React technology stack.

In addition, this article will also provide a complete code implementation, aiming to enable readers to truly master various scenarios and solutions in React development through comparative learning, accelerate development efficiency, improve development quality, and maximize business needs.

2. Core concepts and connections

What is React?

React is a library for building user interfaces. It can be used to build complex web applications. It is a component-based view library.

Its features include:

  • Virtual DOM: It is a virtualized DOM used internally by React. It does not actually re-render the entire page, but only updates the parts that need to change;
  • JSX: an XML-like syntax extension that can describe HTML elements in React;
  • Components: React divides the application into multiple reusable components, each component is only responsible for one thing, which makes code organization and maintenance easier.

Of course, React has many other features, such as one-way data flow, state management, routing, etc., but in this article, we only discuss the three most important features: Virtual DOM, JSX and components.

Virtual DOM

One of the core mechanisms of React is Virtual DOM.

Virtual DOM is a programming concept developed by Facebook that provides a simple and efficient way to update the browser’s DOM tree.

React uses Virtual DOM to represent the application interface as a Javascript object. Then, React will calculate the difference between the two Virtual DOM trees and apply the difference to the browser’s DOM tree to achieve partial refresh of the view.

In fact, Virtual DOM is just an abstract concept used as a renderer and underlying infrastructure in React.

Figure 1: Three different types of DOM tree structures. The one on the left is a completely static DOM tree, and the one on the right is a dynamic DOM tree generated after using Virtual DOM.

JSX

JSX (JavaScript XML) is a JavaScript language extension that provides syntax sugar similar to XML syntax. You can use JSX to define virtual DOM nodes of React components.

For example, the following JSX statement:

const element = <h1>Hello, world!</h1>;

Indicates creating a h1 tag with a text node "Hello, world!" inside.

The JSX compiler will translate JSX code into a React.createElement() function call, so that React elements can be created in JavaScript.

For example:

const element = (
  <div>
    <h1>Hello, world!</h1>
    <Button onClick={() => console.log('Clicked!')}>Click me</Button>
  </div>
);

A component containing child elements can be defined via JSX.

function Welcome(props) {
  return <h1>Welcome, {props.name}!</h1>;
}

A component named Welcome is defined above, which receives an attribute name and displays the welcome message on the page.

Component

Component is one of the core features of React. It is an independent, composable, and reusable UI unit.

The component acts like a function or class, accepting input parameters props and returning the output virtual DOM element.

A component usually contains three parts:

  1. state: the data and status of the component itself;
  2. props: attribute values passed in from outside;
  3. render() method: Render the corresponding virtual DOM based on state and props.

For example:

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
    this.timerID = setInterval(() => this.tick(), 1000);
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <p>Current time is: {this.state.date.toLocaleTimeString()}</p>
      </div>
    );
  }
}

A component named Clock is defined above, which inherits from React.Component, and the state this.state is initialized in the constructor.

This component defines two life cycle hook functions, namely componentDidMount() and componentWillUnmount(), which execute corresponding logic when the component is mounted and destroyed respectively.

Finally, the component’s render() method renders the corresponding virtual DOM based on the current timestamp.

In this way, React can easily assemble, combine, and reuse components to build rich user interfaces.

3. Detailed explanation of core algorithm principles, specific operation steps, and mathematical model formulas

Virtual DOM implementation

React’s Virtual DOM is based on JavaScript objects, and all components are converted into corresponding React elements. So when we modify the state or trigger certain events, React will automatically update the DOM tree.

  1. First, React creates an empty Virtual DOM Tree during the rendering phase, which uses a blank object as the root node.

  2. When a state change or other event occurs that requires re-rendering, React creates new React elements and recursively compares them to old React elements.

  3. If a difference is detected between Virtual DOM, React will update the corresponding DOM node, otherwise it will remain unchanged.

  4. After the update is completed, React will replace the old DOM Tree with the newly generated Virtual DOM Tree, and then re-render the page.

  5. Depending on the browser’s rendering mode, React may re-render the entire page or only a small area.

JSX implementation

JSX is a markup language embedded in JavaScript. It allows you to create React components declaratively. React DOM uses JSX to parse and render JSX elements.

JSX expressions can be wrapped in curly braces, and these expressions can reference variables, functions, arithmetic operators, conditional expressions, etc. JSX elements consist of JSX expressions, attributes, and child elements.

Compiled JSX elements are regular JavaScript objects, so they can be added to arrays or other data structures. You can create components via JSX and render them onto the page.

implementation of component

React builds applications through components, which are independent and composable. Each component is a class or function, accepts input parameters, returns JSX or null, and can have its own state and behavior. The component obtains data through the props attribute and updates its own state by calling the setState method to achieve interaction. The design of components should follow the single-function principle and remain as simple and reusable as possible. Components can be nested, assembled, and even inherited.

To support component composition and inheritance, React provides several important APIs, including PropTypes, PureComponent, and higher-order components. PropTypes can verify whether the component receives the correct property type, PureComponent can reduce unnecessary rendering, and higher-order components can implement more complex inter-component logic.

4. Specific code examples and detailed explanations

Virtual DOM implementation example

For example, let’s say we want to display a numeric counter:

import React from'react';
import ReactDOM from'react-dom';

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  handleClick = () => {
    this.setState((prevState) => ({ count: prevState.count + 1 }));
  }

  render() {
    const { count } = this.state;
    return (
      <div>
        <button onClick={this.handleClick}>{count}</button>
      </div>
    )
  }
}

ReactDOM.render(<Counter />, document.getElementById('root'));

In the above code, we define a Counter class, which inherits from React.Component and implements constructor and handleClick code> and render three methods.

The constructor method initializes the state of the component, which is count, and the initial value is 0.

The handleClick method is an event handling function that uses the arrow function to bind this in order to obtain the setState method of the component instance. Each time the button is clicked, it calls the setState method and increments the count by 1.

The render method returns a div tag, which contains a button. The text content of the button is the current count.

Finally, we call the ReactDOM.render method to render the component to the id="root" element.

Then let’s take a look at how Virtual DOM is implemented here:

let virtualDomTree = { type: "div", props: {}, children: [] };

// update the button text content
virtualDomTree.children[0].type = "button";
virtualDomTree.children[0].props.onClick = function() {};
virtualDomTree.children[0].props.children = 0;

// create a new tree with updated button content and its container div
let rootElement = React.createElement("div");
rootElement.props = {};
rootElement.props.children = [virtualDomTree];
virtualDomTree = rootElement;

In the above code, we initialize a virtual DOM object, then update the text content of the button according to the current state, and then create a new virtual DOM object as a child element of the container element and return it to React.

When the renderer finds that the current virtual DOM is different from the last Virtual DOM, it will update and re-render the page.

JSX implementation example

Suppose we want to define a Message component with props:

import React from'react';

function Message(props) {
  return <h1>{props.text}</h1>;
}

export default Message;

The Message component receives a props, which is an object containing the text field, wrapped in curly braces props.text , making it a JSX expression.

When we import this component, we call the Message component and pass in { text: 'Hello World' } as props:

import React from'react';
import Message from './Message';

function App() {
  return (
    <div className='App'>
      <Message text='Hello World'></Message>
    </div>
  );
}

export default App;

In the above code, we import the Message component, call it, and pass in { text: 'Hello World' } as props.

The compiler then compiles the JSX code into a React.createElement() function call.

var appElement = React.createElement(
  "div",
  { className: "App" },
  React.createElement(Message, { text: "Hello World" })
);

The above code is the compiled result. appElement is an ordinary JavaScript object that describes a React element.

Component implementation example

For example, if we define a component called Greeting, it will receive a name attribute:

import React from'react';

function Greeting(props) {
  return <h1>Hello, {props.name}!</h1>;
}

export default Greeting;

We import this component and call it:

import React from'react';
import Greeting from './Greeting';

function App() {
  return (
    <div className='App'>
      <Greeting name='Alice'/>
    </div>
  );
}

export default App;

This example shows the simplest component rendering. The Greeting component receives a name attribute and renders a greeting.

If we want to create a more complex component, we can define multiple subcomponents and then use them together to form a complete page.

Example of combined components

For example, we define a ContactList component, which will render a list of ContactItem components:

import React from'react';
import ContactItem from './ContactItem';

function ContactList(props) {
  let contactItems = [];
  for (let i = 0; i < props.contacts.length; i + + ) {
    contactItems.push(<ContactItem key={i} {...props.contacts[i]} />);
  }
  return <ul>{contactItems}</ul>;
}

export default ContactList;

The ContactList component receives a contacts property, which is an array containing several props of the ContactItem component.

For each prop in the array, the ContactList component renders a ContactItem component.

Because the ContactItem component receives a props, ...props expands the props and passes them to the ContactItem component.

In addition, the ContactList component will also set a unique key attribute for each ContactItem component so that React can track these components. Variety.

Examples of higher-order components

For example, we want to add a filtering function to the ContactList component, so we first define a higher-order component called withFilter:

import React from'react';

function withFilter(WrappedComponent) {
  class FilteredComponent extends React.Component {
    constructor(props) {
      super(props);

      // set initial filtered contacts list to all available contacts
      this.state = { contacts: props.contacts || [], filterText: ''};
    }

    handleInputChange = event => {
      // update the search filter value when input changes
      this.setState({filterText: event.target.value});
    };

    render() {
      // filter out contacts that don't match the current filter value
      const filteredContacts = this.props.contacts.filter(contact =>
        contact.firstName.toLowerCase().indexOf(this.state.filterText.toLowerCase())!== -1 ||
        contact.lastName.toLowerCase().indexOf(this.state.filterText.toLowerCase())!== -1
      );

      // pass down the filtered contacts array as prop to wrapped component
      const propsWithFilteredContacts = Object.assign({}, this.props, {contacts: filteredContacts});
      return <WrappedComponent {...propsWithFilteredContacts} />;
    }
  }

  return FilteredComponent;
}

export default withFilter;

withFilter is a higher-order component that receives a WrappedComponent as a parameter and returns a new component FilteredComponent.

FilteredComponent is a class component that inherits from React.Component and defines its own constructor and render methods.

The constructor method initializes the state of the component, including the contacts array and the filterText string.

The handleInputChange method is an event handling function that is called when the user enters the content of the search box and updates the filterText state.

The render method renders the filtered filteredContacts and passes the contacts array as prop to the wrapped component WrappedComponent.

Finally, we export the withFilter component, which can filter the ContactList component.

Use React-redux in Redux

When we need to integrate Redux in an application, we generally use the third-party react-redux library.

react-redux provides Provider and connect functions. The Provider component allows us to connect the Redux store with React components. The connect function allows us to connect the React component to the state in the Redux store.

As an example, let’s write a counter application.

First, we create the store:

import { createStore } from'redux';

const counterReducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;
  }
};

const store = createStore(counterReducer);

The above code creates a reducer named counterReducer, which handles two actions: ‘INCREMENT’ and ‘DECREMENT’, which increase and decrease the counter value respectively.

We also created a Redux store and passed counterReducer as a parameter to the createStore function.

In the second step, we write the counter’s View component.

import React from'react';
import { connect } from'react-redux';

function CounterView(props) {
  return (
    <div>
      <button onClick={props.increment}>Increment</button>
      <span>{props.count}</span>
      <button onClick={props.decrement}>Decrement</button>
    </div>
  );
}

function mapStateToProps(state) {
  return { count: state };
}

function mapDispatchToProps(dispatch) {
  return {
    increment: () => dispatch({ type: 'INCREMENT' }),
    decrement: () => dispatch({ type: 'DECREMENT' })
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(CounterView);

CounterView is a pure component that receives a count property and two button click event callback functions.

mapStateToProps is a function that maps state in the Redux store to props. In this example, we directly map the state in the Redux store to props.count.

mapDispatchToProps is a function that maps action creator functions to props. In this example, we define two action creator functions increment and decrement, which will send Redux actions.

The third step is to connect View and Store.

import React from'react';
import ReactDOM from'react-dom';
import { Provider } from'react-redux';
import store from './store';
import CounterView from './components/CounterView';

ReactDOM.render(
  <Provider store={store}>
    <CounterView />
  </Provider>,
  document.getElementById('root')
);

The Provider component allows us to connect the Redux store and React components.

Finally, we introduce the component and Store in the index.js file.