React technical principles and code development practice: from components to life cycle

1. Background Introduction

React is a JavaScript library for building user interfaces. It originated from Facebook, which implemented a front-end view layer framework in JavaScript and open sourced the project. Facebook is now at the helm full-time. React has many features, such as:

  1. Declarative programming: React adopts a declarative programming method, that is, using JSX to describe the structure and behavior of the UI, rather than the traditional command calling method of imperative programming.

  2. Modular development: React splits complex interfaces into multiple components, each component independently completes its own function. Modules can be combined during the development process to achieve higher reuse rate and maintainability.

  3. Virtual DOM: React separates the real DOM and the virtual DOM. By comparing the two, it avoids direct manipulation of the DOM and improves performance.

  4. One-way data flow: React passes data from parent components to child components through Props, but Props can only be modified by the parent component, and child components cannot modify Props, thus ensuring data consistency and one-way flow.

  5. JSX syntax is concise and easy to read: JSX is an XML-like syntax extension, a JS expression that can be mixed with ordinary JS variables, functions, etc. It makes it easy to create elements, bind events, and introduce styles.

  6. Support server rendering: React supports server-side rendering, which can improve the loading speed of the first screen of the page.

This article will explore the basic principles, core concepts and mechanisms of React technology.

2. Core concepts and connections

React technology mainly has three aspects:

  1. Component: React splits the UI into independent widgets (Components). These widgets can be combined into complex UI interfaces, and it also provides a rich API for us to call. We can pass state and properties between components and also nest sub-components.

  2. State: Each component has its own internal state, including props attributes and self-defined states. When the state changes, the component will re-render.

  3. Lifecycle: React provides multiple lifecycle methods to perform some tasks at different stages. Life cycle callback functions can help us implement various functions of components. For example, the componentDidMount() method is called after the component is mounted, the componentDidUpdate() method is called after the component is updated, and the componentWillUnmount() method is called before the component is unmounted.

How does React work? First, we need to create a ReactDOM object, which is responsible for rendering our component. The ReactDOM.render() method can convert JSX into actual DOM and insert it into the specified container. ReactDOM then recursively renders the component tree and calls the appropriate functions based on its lifecycle methods.

Communication between components is achieved through the Props property. Props can be passed from parent components to child components, but can only be modified by the parent component. Subcomponents cannot modify Props, but get the value in Props by calling this.props.propName when the constructor or state changes.

The relationship diagram between Props, status and life cycle is as follows:

3. Core Algorithm Principle and Operation Steps

3.1 Data-driven view

The most important concept in React is data-driven views. React does not have a command system like Vue, and all its changes are determined by data. Changes in data will trigger a re-rendering of the component and the view will be updated. We use useState hook to manage the state of the component. useState returns two values: the current state value and a function for updating the state value. Each time the state value changes, a rerender is triggered, so we can use this feature to implement complex business logic. For example, form validation, filtering and sorting functions.

3.2 Component Combination

React uses JSX to describe the structure and behavior of components, which is very similar to HTML and CSS syntax. React only cares about the rendering result of the component, so we only need to render JSX into the actual DOM. The relationship between components is determined by their Props. Props refers to the data passed from the parent component to the child component. Child components can obtain the props passed by the parent component through this.props. If a subcomponent needs special processing of certain props, you can override its render function.

The combination of components forms a component tree. When a subcomponent needs to obtain the status or data of an ancestor component, we can pass parameters through the component’s superior-subordinate relationship. Data communication between components can be made simple through props.

3.3 Virtual DOM

React generates an object called Virtual DOM when rendering, which records all information in the real DOM context. When the state is updated, React generates a new Virtual DOM by comparing the old and new Virtal DOM, and then uses the diff algorithm to find the smallest set of operations to update the view. So React’s views update very quickly.

3.4 Event Processing

React provides addEventListener and removeEventListener methods to listen to and remove DOM events. React’s event model is basically the same as the browser’s native event model, but there are a few caveats.

  1. Default events: By default, event handlers used in JSX are bound to component instances.

  2. Synthetic events: React adds cross-browser compatible polyfills for all standard DOM events. But for some advanced features, such as input method key combinations, React cannot polyfill. Therefore, for a better experience, it is recommended not to rely on the default behavior provided by the browser.

  3. Anti-shake and throttling: React’s built-in useEffect hook can be used to solve this problem. useEffect can receive a function as a parameter. This function is run after the component is rendered and before it is updated. It can be used to achieve anti-shake and throttling.

3.5 Browser Rendering

React does not directly operate the DOM when rendering, but generates a new virtual DOM, uses an OT (Operational Transformation) algorithm to calculate the smallest change, and finally applies this change to the real DOM. When data is updated, React automatically figures out which DOM needs to be updated, and how. This approach of React can reduce unnecessary DOM operations and improve performance.

4. Code Examples and Explanations

4.1 Hello World!

import React from'react';

function App() {
  return (
    <div>
      <h1>Hello, world!</h1>
      <p>This is a React app.</p>
    </div>
  );
}

export default App;

This code defines a function component called App that returns a JSX element containing two tags: h1 and p . This is a typical way of writing React components.

There is a key point here: the name of the component must start with a capital letter. This rule is officially recommended by React to distinguish JSX elements from ordinary JS objects.

Another key point: the export of a component should always be placed at the bottom of the file. Although this is not a mandatory requirement, it can make calling components more convenient because the IDE can automatically prompt import statements, making the development experience better.

4.2 Component properties

Components can accept external data through the props attribute and access it through this.props. For example:

//Parent component
<Child name="John" age={30}/>

// Subassembly
class Child extends Component {
  render() {
    const {name, age} = this.props;
    return (
      <div>{`My name is ${name}, and I am ${age} years old.`}</div>
    )
  }
}

Here, the child component receives the name and age parameters passed by the parent component through the props attribute, and displays them during rendering. Note that in JSX you can use curly braces to wrap variables so that strings can be concatenated.

In addition, components can also set default properties so that parent components can omit unnecessary parameters. For example:

//Subcomponent
class Greeting extends Component {
  static defaultProps = {
    salutation: "Hello",
    emoji: ":)"
  };

  render() {
    const {salutation, emoji} = this.props;
    return (
      <div>{`${salutation}, ${this.props.children}${emoji}`}</div>
    );
  }
}

// parent component
<Greeting>World</Greeting>; // output: "Hello, World :)"
<Greeting salutation="Hi">Everyone</Greeting>; // output: "Hi, Everyone :)"
<Greeting emoji=", where are you?">Alex</Greeting>; // output: "Hello, Alex, where are you?" 

Here, the Greeting component sets two default properties: salutation and emoji. The parent component can omit any of the parameters, but must specify the remaining parameters.

4.3 Component status

In addition to the properties defined by the component itself, the component’s state also contains other data. For example, the value of a form input box, or the number of clicks on a button, etc. State can be managed through useState hook. useState returns two values: the current state value and a function for updating the state value.

The setState() method can be used to update the state. For example:

const [count, setCount] = useState(0);

<button onClick={() => setCount(count + 1)}>Increment</button>

Here, we create a counter with an initial value of 0 . When the user clicks the button, the setCount() function will be called, updating the value of count to count + 1.

In addition, you can also set the initialization value of the state through the second parameter of useState(). For example:

const [username, setUsername] = useState("anonymous");

In this case, the component will initially display “anonymous” as the username.

There are other ways to manage state, such as Redux or Mobx. However, this is beyond the scope of this article, and I hope you can find a solution that suits you.

4.4 Event Processing

React provides many more event handlers than native browser event handlers. In addition to native event types, React also supports custom event types. Event handlers can be passed to component properties, for example:

<button onClick={(event) => handleClick(event)}>Click me</button>

Here, the handleClick() function receives an event object, which contains event-related information, such as the location of the mouse click.

React also has a built-in SyntheticEvent object, which is an encapsulation of the browser’s native event object and provides some additional APIs. Therefore, the additional API provided by it can be used in custom event handling functions, such as the preventDefault() method.

React’s throttling and debounce APIs can be used in event handlers. useEffect() can be used to control rendering frequency, similar to setTimeout() and clearTimeout(). For example:

const [value, setValue] = useState("");

useEffect(() => {
  const timeoutId = setTimeout(() => {
    console.log(`Input value changed to "${value}"`);
  }, 3000);

  return () => clearTimeout(timeoutId);
}, [value]);

return <input type="text" onChange={(event) => setValue(event.target.value)} />;

Here, the useEffect() function is used to print out the value in the text box after the user stops typing for 3 seconds. The first argument to useEffect() is a function that runs after the component is rendered but before it is updated. The second parameter is an array describing the dependencies. useEffect() will only be rerun when the values in this array change.

useEffect() has some other parameters, but this is beyond the scope of this article. I hope you can find a solution that suits you.

4.5 Controlled components and uncontrolled components

There are two types of components in React: controlled components and uncontrolled components.

Controlled components mean that the component has its own state and its values are synchronized with input elements in the DOM. In other words, the component’s value is specified by props and is not affected by the input elements in the DOM. For example, when you enter text in a text box and submit the form, the value of the text box is submitted to the server. This type of component is typically used in scenarios with complex interactions, such as forms.

An uncontrolled component means that the component’s value is out of sync with the input element in the DOM. In other words, the component’s value is determined by the input element in the DOM, not by props. This type of component is usually used for simple input scenarios, such as a single-line text box.

To implement a controlled component, you need to put the value of the input element into state and update state in the handleChange() function. For example:

class ControlledInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = { inputValue: "" };
  }

  handleChange = (event) => {
    this.setState({ inputValue: event.target.value });
  };

  render() {
    return (
      <input
        type="text"
        value={this.state.inputValue}
        onChange={this.handleChange}
      />
    );
  }
}

Here, the ControlledInput component inherits from React.Component and initializes the state in the constructor. The handleChange() function is called when the input value changes and updates the state through setState(). Bind the input value to the input box through value={this.state.inputValue} and onChange={this.handleChange}.

To implement an uncontrolled component, you do not need to store the input value in state, but set it directly with defaultValue. For example:

class UncontrolledInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = { inputValue: "" };
  }

  render() {
    return <input type="text" defaultValue="" />;
  }
}

Here, the UncontrolledInput component also inherits from React.Component, but without state, it only renders an empty input box.

React’s documentation recommends using controlled components because it provides better control over the component’s behavior.