title: Talking about Components in React
date: 2018-03-19 21:00:56
banner: https://cdn.statically.io/gh/YanYuanFE/picx-images-hosting@master/20231128/photo-1454165804606-c3d57bc86b40-(1).3jxklf3d9am0.webp
tags:
- React
The core idea of React is that everything is a component.
In React, everything is a component. Components can divide the UI into a series of independent and reusable parts, allowing us to focus on building each individual part.
Understanding the various types of components in React is crucial for better learning React. Concepts such as UI components, container components, stateless components, and functional components are often not clear to those who are not deeply familiar with React. It is difficult for them to write high-quality React components. This article is my understanding of React components.
Component#
In this context, Component refers to React.Component. Most people learn about React and understand components the most. Using Component makes it very convenient to define a component.
To define a component using ES6 class, you can do the following:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
PureComponent#
In this context, PureComponent refers to React.PureComponent. PureComponent is almost identical to Component, but PureComponent implements shouldComponentUpdate() by shallowly comparing props and state.
class Welcome extends React.PureComponent {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
In the lifecycle of React, when the props and state of a component change, the component's shouldComponentUpdate function is executed. If this function returns true, the component will be re-rendered; if it returns false, the component will not be re-rendered. In components defined using Component, shouldComponentUpdate defaults to returning true. Usually, when a component encounters performance issues, you can use shouldComponent to compare the new and old props and state to reduce unnecessary re-rendering.
PureComponent is a feature introduced in React v15.3.0. It implements a shallow comparison of the new and old props and state in shouldComponentUpdate, reducing unnecessary re-rendering to improve performance.
Note: PureComponent only performs a shallow comparison on objects. When the object hierarchy is deep or the structure is complex, there may be issues where the view does not update even though the deep-level data of the object has changed. Be cautious when using it.
Functional Component#
The components defined using Component and PureComponent above are called declarative components. In React, you can also define a component using a function, which is called a functional component. For example:
const Text = ({ children = 'Hello World!' }) =>
<p>{children}</p>
Functional components are also known as stateless components. They cannot manipulate state within the component. Using functional components makes React code more readable and reduces redundant code such as class and constructor, making it easier to reuse components. Functional components have the following characteristics:
- Functional components are not instantiated, which improves overall rendering performance.
Functional components do not have a class declaration and only use the render method. There is no process of instantiating components, so there is no need to allocate extra memory, which improves rendering performance.
- Functional components cannot access the this object.
Functional components cannot access this.state internally and cannot manipulate state.
3. Functional components do not have lifecycle methods.
4. Functional components only accept props and context parameters and have no side effects.
UI Components and Container Components#
In applications that use Redux to manage data flow, Redux divides components into UI components (presentational components) and container components. UI components are also known as presentational components. In earlier versions of Redux, the author referred to these two types of components as smart components and dumb components, depending on whether they perform data operations. Dumb components correspond to UI components, and smart components correspond to container components. UI components are only responsible for presenting the UI and do not handle business logic. They do not have internal state, and data is provided through the this.props parameter. Container components are responsible for handling business logic and do not handle UI presentation. They have internal state and use Redux's API to connect to UI components.
Usually, in the file structure of a project, the components folder contains UI components, and the containers folder contains container components.
Pure Components#
Before talking about pure components, let's review the concept of "pure functions" in functional programming. Pure functions have three main principles:
- Given the same input, they always return the same output.
- They do not cause any side effects during execution.
- They do not depend on any external state.
In React, using pure components can improve the efficiency of the Virtual DOM. So, what kind of components are considered pure components?
First of all, PureComponent is not a pure component because it has lifecycle methods and internal state, which can cause side effects.
Generally, functional components, UI components, and stateless components that only render data based on props can be considered pure components. They accept the same input props and return the same output, without any side effects.
Controlled Components#
In HTML, form elements like input, textarea, and select maintain their own state and update based on user input. However, in React, mutable state is usually stored in the component's state property and can only be updated using the setState() method.
In React, we use state to control the display of form element data and update the form element data based on user input by updating the state. Form elements whose values are controlled by React are called "controlled components".
Consider the following code:
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
The above code is an example of a controlled component. Since the value attribute is set on our form element, the displayed value will always be the value of this.state.value in React's data source. Since handleChange is triggered with every keystroke to update the current React state, the displayed value will update as the user types different characters.
Uncontrolled Components#
In most cases, we recommend using controlled components to implement forms. In controlled components, form data is handled by React components. If you want the form data to be handled by the DOM instead, you can use uncontrolled components.
To write an uncontrolled component, instead of writing an event handler for each state update, you can use the ref to get form values from the DOM.
Consider the following code:
class Form extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
alert('A name was submitted: ' + this.input.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" ref={(input) => this.input = input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
Since uncontrolled components keep the source of truth in the DOM, it is easier to integrate React with non-React code when using uncontrolled components. If you want a quick and casual solution, this can reduce the amount of code. Otherwise, you should use controlled components.
Generally, it is not recommended to use uncontrolled components.
Higher-Order Components#
Higher-Order Components (HOC) is an advanced technique in React for reusing component logic. However, HOC itself is not a React API. It is just a pattern that naturally emerges from React's compositional nature.
Specifically, HOCs are similar to higher-order functions, and they accept a React component as an argument and return a new component.
const EnhancedComponent = higherOrderComponent(WrappedComponent);
You may have already used HOCs without realizing it in some third-party libraries for React, such as connect in react-redux and withRouter in react-router-dom.
HOCs make code more reusable, logical, and abstract. They can intercept the render method and control props and state.
Consider the following code:
export default function Form(Comp) {
return class WrapperComp extends Component {
constructor(props) {
super(props);
this.state = {
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(key, val) {
this.setState({
[key]: val
})
}
render() {
return <Comp {...this.props} handleChange={this.handleChange} state={this.state}/>
}
}
}
The above code is an HOC that solves the problem of frequently writing event handling functions to manage form data when using form components. You can use it as follows:
class Login extends Component {
constructor(props) {
super(props);
this.state = {
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(key, val) {
this.setState({
[key]: val
})
}
render() {
return (
<div className="login">
<input
type="text"
value={this.props.state.user}
onChange={(e) => this.props.handleChange('user', e.target.value)}
/>
<input
type="password"
value={this.props.state.psw}
onChange={(e) => this.props.handleChange('psw', e.target.value)}
/>
</div>
)
}
}
export default Form(Login);
This way, for components that need to use forms in many places, you can provide component reusability and reduce boilerplate code.
Reference: https://react.bootcss.com/