1. React componentization
At present, the core of the three front-end frameworks is the idea of componentization.
- Componentization provides an abstraction that allows us to develop individual reusable components to construct our applications.
- Any application can be abstracted as a component tree
Application of componentization:
- As much as possible, break the page into small, reusable components
- Our code is easier to manage and more extensible
React components are more flexible and diverse than Vue components and can be divided into multiple groups in different ways:
- According to the way components are defined, they can be roughly divided into: function-time components and class-components
- According to whether there is state, it can be divided into stateless components and stateful components
- According to different responsibilities are divided into display components and container components
2. Create class components and function components
The main difference between a class component and a function component is that a class component has internal state while a function component has no internal state.
2.1 class components
First, the class component must meet two requirements:
- Must be inherited from react.ponent
- The render function must be implemented
As shown in the following code:
import React, { Component } from 'react'
export default class App2 extends Component {
// Constructor is optional. We usually initialize some data in constructor;
constructor() {
super(a)// This. State maintains the data inside our component;
this.state={
}
}
render() {
return (
<div>
</div>)}}Copy the code
The second thing we should note is the return type of the Render function:
- React element (ReactElement element)
- Array or fragment (allows the Render method to return multiple elements)
- Portals: The ability to render child nodes into different DOM subtrees
- String or numeric type: will be rendered directly as a text node
- Boolean or NULL: not displayed
2.2 Functional Components
Function components are usually defined directly using function and return the same result as class components.
Function components have the following points to note:
- There is no life cycle, although it will be updated and mounted
- No this (so you don’t need to worry about this pointing)
- No internal state
Here is an example of a function component:
import React from 'react'
export default function App2() {
return (
<div>
</div>)}Copy the code
3. Component lifecycle
The life cycle of the React component is divided into three phases
- Mount stage (Mount)
- Update phase
- Unloading stage (unmount)
React implements some functions inside the component to call back to the current state of the component. We can call back to the lifecycle function at various stages and do some of our business operations in it.
We can learn these life cycle functions based on the official life cycle map.
We can analyze the lifecycle execution flow of the following components based on the figure above:
In the Mounting phase, we execute our constructor function to initialize some data states, and then execute the render function to obtain the ReactElement element and render the DOM. The componentDidMount function is called back when the component is successfully mounted
Then, during the Updating phase, when we modify the props object, or perform setState or forceUpdate, in short, when we modify the data and the UI changes, the render function is called to re-render the DOM of the component to update it. When the update is complete, the componentDidUpdate lifecycle function is called back
In the final Unmounting phase, when our component is no longer in use, the componentWillUnmount function is called back.
Here are a few common lifecycle functions and their usage scenarios:
-
Constructor function: initialize state; Bind this to the event
-
ComponentDidMount functions: DOM dependent operations; Send some network requests; Publish some subscriptions
-
ComponentDidUpdate function: component update, also can operate on the DOM; Sending network Requests
-
ComponentWillUnmount function: Performs necessary cleanup operations, such as timers
Validation:
import React, { Component } from 'react'
class App1 extends Component {
render() {
return (
<div>I am validating the conponentWillUnMount component</div>)}componentDidMount(){
console.log('I am the Mount function of the second component');
}
componentWillUnmount(){
console.log('I am the UnMount function of the second component'); }}export default class App extends Component {
constructor() {
super(a);this.state={
num:1.flag:true
}
console.log('Constructor function period')}render() {
console.log('Render function period')
return (
<div>
<h2>Life cycle function demonstration</h2>
<button onClick={()= > {
this.setState({num:this.state.num+1})
}}>+1</button>
<p>{this.state.num}</p>
<hr/>
<button onClick={()= >{ this.setState({flag:! This.state.flag})}}> Click to hide the second component</button>
{this.state.flag&&<App1></App1>}
</div>)}componentDidMount() {
console.log('componentDidMount period')}componentDidUpdate() {
console.log('componentDidUpdata period')}}Copy the code
Note: our lifecycle operations are done in class components, because functions are components without lifecycle functions.
4. Component nesting
Where did the concept of parent-child components come from in the first place? It’s component nesting, and what component nesting is, is embedding a component that we define into another component. As we develop a mall home page, generally need header, banner, content and footer composition. So we can pull all of these things out into a component that we can use directly in a parent container.
Such as the following code:
import React, { Component } from 'react'
function Header() {
return (
<div>
<h2>I'm a Header component</h2>
</div>)}function Main() {
return (
<div>
<h2>I'm the Main component</h2>
<Banner></Banner>
<ProductList></ProductList>
</div>)}function Banner() {
return (
<div>
<h3>I am the Main_Banner component</h3>
</div>)}function ProductList() {
return (
<div>
<h3>I am the Main_list component</h3>
</div>)}function Footer() {
return (
<div>
<h2>I'm Footer component</h2>
</div>)}export default class App
extends Component {
render() {
return (
<div>
<Header></Header>
<Main></Main>
<Footer></Footer>
</div>)}}Copy the code
In the code above, our children use functional components, and the parent uses class components.
4. Communication between parent and child components
Since we have the concept of components and their nested components, components must be able to communicate with each other. For a simple example, we have a parent-child component. At present, the child component needs the data of the parent component, so we need data communication.
4.1 the father the son
In the parent component, we only need to specify the corresponding property for the child component.
render() {
return (
<div>
<Person name='aoao' age={2} sex='male'></Person>
</div>)}Copy the code
Then subcomponents are divided into the following situations:
When a child component is a class component:
We can initialize a prop from Constructor, and the data we pass in is stored in the props object.
It’s worth mentioning here that in the constructor constructor,
- We can use
this.props=props
Initialize the props object, - We can also use super to initialize prop. Look at the Component source code, which points to props for us
- We don’t even have to initialize it
class Person extends Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
<h2>{this.props.name+""+this.props.age+""+this.props.sex}</h2>
</div>)}}Copy the code
When a child component is a function component:
To use this function, just give the props object as an argument
function Person(props) {
return (
<div>
<div>
<h2>{props.name+""+props.age+""+props.sex}</h2>
</div>
</div>)}Copy the code
PropTypes are needed for validation of data formats that are passed to child components.
4.2 the father
When the React component passes data to the parent component, it actually uses props to pass messages. The react component passes a callback function to the child component, and then calls the function in the child component.
- We usually use the above method to pass the child’s events to the parent, and then manipulate the parent’s state through the contents of the child
- The communication between parent and child components can also be realized by passing parameters.
We can simulate a TabControl case as follows:
The general function is that the content changes with the TAB, and we wrap the TAB as a component (TabControl), the App component as our parent container, and then store the data and the TAB data in the App component. The purpose of this writing is to realize data transmission between the parent and child components.
App. Js code:
import React, { Component } from 'react'
import TabControl from './TabControl'
import './style.css'
export default class App extends Component {
constructor(props) {
super(props)
this.title = ['popular'.'new'.'select']
this.state = {
currentIndex:0}}render() {
return (
<div>
<TabControl sendEvent={(index)= > {
this.sendEvent(index)
}} title={this.title}></TabControl>
<h2>{this.title[this.state.currentIndex]}</h2>
</div>)}sendEvent(index){
// The child passes the parent back index
console.log(index)
this.setState({
currentIndex:index
})
}
}
Copy the code
TabControl code:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class TabControl extends Component {
static propTypes = {
title: PropTypes.array.isRequired
}
constructor(props) {
super(props)
this.state = {
currentIndex: 0}}render() {
let { title } = this.props;
let { currentIndex } = this.state;
return (
<div className='tabcontrol'>
{
title.map((item, index) => {
return <div
onClick={()= > {
this.setState({
currentIndex: index
})
this.props.sendEvent(index)
}}
className={'tabcontrol_item ' + (currentIndex === index ? 'activity' : '')}
key={index}>
{item}
<span className={currentIndex= = =index ? 'activitx' :"'} ></span></div>})}</div>)}}Copy the code
6. Communication across components
In the general case we meet with the parent-child communication components, we will use props objects passed one by one, such as the relationship between the I now have A component in A – > B – > C, A need to transfer data to C now, if you want to use our previous study between components operated by value, you can pass A value through the props to B, To complete this operation, pass props through B to C. But if our middle layer doesn’t use the data in the PROPS object, then the middle layer acts as a conduit here, so it’s a case of data redundancy.
The following code emulates the above situation. The parent component is App, the middle component is Profile, and the target component is ProfileList. We pass the state in App to the target object. In the following code we use props to pass values.
import React, { Component } from 'react'
function ProfileList(props) {
console.log(props)
return (
<div>
<p>{props.name}</p>
<p>{props.address}</p>
</div>)}function Profile(props) {
return (
<div>
<ProfileList {. props}
></ProfileList>
<p>This is what's on the list</p>
</div>)}export default class App extends Component {
constructor(props) {
super(props)
this.state={
name:'Joe'.address:'Beijing'}}render() {
return (
<div>
<Profile {. this.state} ></Profile>
</div>)}}Copy the code
Is there an easier way to do this?
So that brings us to the concept of Context.
In general, there are three steps to using Context:
- create
React.createContext
object - use
Context.Provider
Component wraps the component under our parent container and sets value - Use the class of
contexttype
Property to mount the Context object for the component. - The mounted components are available
this.context
Consumes the value of the Context
Here’s an example:
import React, { Component } from 'react'
const MyContext = React.createContext()
class ProfileList extends Component {
render() {
console.log(this.context)
return (
<div>
<p>{this.context.name}</p>
<p>{this.context.address}</p>
</div>)}}class Profile extends Component{
render(){
console.log(this.context)
return (
<div>
<ProfileList
></ProfileList>
<p>This is what's on the list</p>
</div>)}}export default classusepropsCommunicate across componentsextends Component {
constructor(props) {
super(props)
this.state = {
name: 'Joe'.address: 'Beijing'}}render() {
return (
<div>
<MyContext.Provider value={this.state}>
<Profile></Profile>
</MyContext.Provider>
</div>
)
}
}
ProfileList.contextType=MyContext;
Profile.contextType=MyContext;
Copy the code
Note: When we are a function component, or when we need multiple Context objects, we need to use context.consummer to pass nested values.
Note: Context is used to pass data to non-parent components, but when we pass events, we need to use the EventBus.
Event bus
Context has been used to share data, but what if there is event passing across components in development?
-
In Vue, we can quickly implement an EventBus through the Vue instance to complete the operation;
-
In React, we can rely on a library of events that is used a lot.
We can install events via NPM or YARN:
yarn add events;
Copy the code
Events API:
- Create An EventEmitter object.
- Issue event:
Eventbus. emit(" event name ", parameter list);
- Listening event:
Eventbus. addListener(" event name ", listener function)
; - Remove event:
Eventbus. removeListener(" event name ", listener function)
;
import React, { Component } from 'react';
import { EventEmitter } from "events";
const eventBus = new EventEmitter();
class ProfileHeader extends Component {
render() {
return (
<div>
<button onClick={e= >Enclosing btnClick ()} > button</button>
</div>)}btnClick() {
eventBus.emit("headerClick"."why".18); }}class Profile extends Component {
render() {
return (
<div>
<ProfileHeader />
<ul>
<li>Set 1</li>
<li>Set up 2</li>
<li>Set of 3</li>
<li>Set of 4</li>
<li>Set of 5</li>
</ul>
</div>)}}export default class App extends Component {
componentDidMount() {
eventBus.addListener("headerClick".this.headerClick)
}
headerClick(name, age) {
console.log(name, age);
}
componentWillUnmount() {
eventBus.removeListener("headerClick".this.headerClick);
}
render() {
return (
<div>
<Profile/>
<h2>Other content</h2>
</div>)}}Copy the code
5. Slot implementation
During development, we extracted a component, but in order to make the component more generic, we could not limit the content of the component to fixed elements such as divs, spans, and so on.
We should give users the ability to decide what to store in an area.
- React is very flexible for situations where slots are needed;
- There are two scenarios to implement this: children and props;
Two ways (one is to use the children of props directly, the other is to pass a custom tag)
In fact, this is also done by transferring values between components.
We can use a slot that simulates a Tab Bar:
import React, { Component } from 'react'
import './style.css'
export default class TabBar extends Component {
render() {
console.log(this.props)
let { LeftSlot, MIddleSlot, RightSlot } = this.props
return (
<div className='navBar'>
{
LeftSlot
}
{
MIddleSlot
}
{
RightSlot
}
</div>
)
}
}
Copy the code
This is the method used:
import React, { Component } from 'react'
import TabBar from './TabBar'
import TabBar2 from './TabBar2'
export default class App extends Component {
render() {
return (
<div>
<TabBar LeftSlot={<p>On the left</p>}
MIddleSlot={<p>In the</p>}
RightSlot={<p>right</p>} ></TabBar>
<TabBar LeftSlot={<p>On the left</p>}
RightSlot={<p>right</p>} ><p>12</p>
<p>12</p>
<p>12</p>
</TabBar>
<TabBar2>
<p>2</p>
<p>3</p>
<p>4</p>
<p>4</p>
<p>4</p>
</TabBar2>
</div>)}}Copy the code
7.refs
So the first thing we have to figure out is what does ref do?
Refs is a tool to get instances of DOM nodes or React elements. Refs in React provides a way for users to access DOM nodes or React elements created in the Render method.
In React development mode, it is generally not necessary or recommended to manipulate the DOM native directly. However, in some special cases, it is necessary to retrieve the DOM for certain operations:
Usage Scenarios:
- Control of DOM element focus, content selection, or media playback;
- Control DOM elements to trigger animation effects;
- Integration with third-party DOM libraries.
Avoid using Refs to do anything that can be done with declarative implementations. For example, to avoid exposing open(), show(), hide(), and close() methods inside Dialog, Loading, and Alert components, it is better to control them through isXX properties.
There are three kinds of refs, one of which is recommended to use the second one:
- String refs (obsolete)
- Use the CreateRefs ()
- Refs callback function
Code demo:
import React, { createRef, PureComponent } from 'react'
export default class refsThe use ofextends PureComponent {
constructor(props) {
super(props)
this.refDOM=createRef();
this.reffun=null;
}
render() {
return (
<div>
<h2 ref='dd'>123</h2>
<h2 ref={this.refDOM}>I'm ref of object type</h2>
<h2 ref={(ref)= >{this.reffun=ref}}> callback function</h2>
<button onClick={()= >{this.getdom ()}}> Get the DOM</button>
</div>)}getDOM(){
// Using strings is not recommended
console.log(this.refs.dd)
// Use object, recommended
console.log(this.refDOM.current)
// Use the callback function
console.log(this.reffun)
}
}
Copy the code
8. Controlled components and uncontrolled components
8.1 Controlled Components
In React, both managed and uncontrolled components are for forms.
In HTML, form elements such as ,
React works differently. The component that contains the form tracks the input value in its state and rerenders the component every time a callback function (such as onChange) fires, as the state is updated. The input form elements whose values are controlled by React in this way are called controlled components.
React-controlled components update the state process: (1) You can set the default value of the form in the initial state. (2) Call the onChange event handler whenever the value of the form changes. (3) The event handler gets the changed state by synthesizing the event object E and updates the state of the application. (4) setState triggers the re-rendering of the view to complete the update of the form component value.
import React, { PureComponent } from 'react'
export default class App extends PureComponent {
constructor(props) {
super(props)
this.state={
username:' '}}render() {
return (
<div>
<div>
<label htmlFor="username"></label>
<input type="text" name="" value={this.state.username} id="username" onChange={(e)= >{this.changedata(e)}} />
</div>
<input type="submit" value="Submit" onClick={(e)= >{this.handleclick(e)}} />
</div>)}handleclick(e){
e.preventDefault();
}
changedata(e){
this.setState({
username:e.target.value
})
}
}
Copy the code
In the example above, we implemented a controlled component, so we need to use the controlled component method to operate forms in React
The way we control our forms is our controlled component.
8.2 Uncontrolled Components
React officially does not recommend using uncontrolled components. A form component is an uncontrolled component if it has no value props(checked prop for radio buttons and check boxes). Uncontrolled components typically use a backref to manipulate our native DOM to control form components
A real chestnut:
class NameForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.input = React.createRef();
}
handleSubmit(event) {
alert('A name was submitted: ' + this.input.current.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" defaultValue="Bob" ref={this.input} />
</label>
<input type="submit" value="Submit" />
</form>); }}Copy the code