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:
- state: the data and status of the component itself;
- props: attribute values passed in from outside;
- 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.
-
First, React creates an empty Virtual DOM Tree during the rendering phase, which uses a blank object as the root node.
-
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.
-
If a difference is detected between Virtual DOM, React will update the corresponding DOM node, otherwise it will remain unchanged.
-
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.
-
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.