Preface ⏰

People who are new to react always start with the basics, and Monday was no exception. Redux and React-Router are plugins that can be used with react.

In this paper, a large number of cases 🌰 and gifs 🕹️ are combined for demonstration. React can be used as a primer for how to react. If you don’t understand the syntax, use this primer to learn how to react.

Ding, without further ado, let’s explore the mysteries of React 👏

📝 basic use of React

1. Basic use of JSX

(1) Variables and expressions

React contains variables and expressions in the following form:

The first type: fetching variables, interpolation

import React from 'react'

class JSXBaseDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: 'Nuggets of gold: Monday Lab'.imgUrl: 'https://p3-passport.byteacctimg.com/img/user-avatar/cc88f43a329099d65898aff670ea1171~300x300.image'.flag: true}}render() {
        // Get variable interpolation
        const pElem = <p>{this.state.name}</p>
        return pElem
    }
}

export default JSXBaseDemo

Copy the code

Note that the react interpolation is in the form of a single curly brace {}. The final browser display looks like this:

The second type: expressions

import React from 'react'

class JSXBaseDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: 'Nuggets of gold: Monday Lab'.imgUrl: 'https://p3-passport.byteacctimg.com/img/user-avatar/cc88f43a329099d65898aff670ea1171~300x300.image'.flag: true}}render() {
        / / expression
        const exprElem = <p>{this.state.flag ? 'yes' : 'no'}</p>
        return exprElem
    }
}

export default JSXBaseDemo

Copy the code

React also supports using expressions directly in interpolation, such as this.state.flag? ‘yes’ :’ no ‘. The final browser display looks like this:

(2) Class and style

Normally, if we give a tag a class name, we give the tag a class. In React, if you want to add a class to a tag, you add a className to it. The specific code is as follows:

import React from 'react'
import './style.css'
import List from '.. /List'

class JSXBaseDemo extends React.Component {
    render() {
        // class
        const classElem = <p className="title">Set the CSS class</p>

        // style
        const styleData = { fontSize: '30px'.color: 'blue' }
        const styleElem1 = <p style={styleData}>Set the style</p>
        {{and}}}
        const styleElem2 = <p style={{ fontSize: '30px',  color: 'blue' }}>Set the style</p>
       
		// Return the result
		return [ classElem, styleElem1, styleElem2 ]
    }
}

export default JSXBaseDemo

Copy the code

The browser displays:

Note also that react uses double curly braces {{}} to write inline styles inside labels.

(3) Child elements and components

The first type: child elements

For child elements, it can be used within the tag. The following code looks like this:

import React from 'react'

class JSXBaseDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: 'Nuggets of gold: Monday Lab'.imgUrl: 'https://p3-passport.byteacctimg.com/img/user-avatar/cc88f43a329099d65898aff670ea1171~300x300.image'.flag: true}}render() {
        / / child elements
        const imgElem = <div>
            <p>My head is</p>
            <img src="xxxx.png"/>
            <img src={this.state.imgUrl}/>
        </div>
        return imgElem
    }
}

export default JSXBaseDemo

Copy the code

Finally, the browser should look like this:

The second type: load components

If we want to load a component in React, we can do this. The specific code is as follows:

import React from 'react'
import './style.css'
import List from '.. /List'

class JSXBaseDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: 'Nuggets of gold: Monday Lab'.imgUrl: 'https://p3-passport.byteacctimg.com/img/user-avatar/cc88f43a329099d65898aff670ea1171~300x300.image'.flag: true}}render() {
        // Load the component
        const componentElem = <div>
            <p>Load a component in JSX</p>
            <hr/>
            <List/>
        </div>
        return componentElem
    }
}

export default JSXBaseDemo

Copy the code

The code for the list.js component is as follows:

import React from 'react'

class List extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: 'React'.list: ['a'.'b'.'c']}}render() {
        return <div>
            <p onClick={this.changeName.bind(this)}>{this.state.name}</p>
            <ul>{
                this.state.list.map((item, index) => {
                    return <li key={index}>{item}</li>})}</ul>
            <button onClick={this.addItem.bind(this)}>To add a</button>
        </div>
    }
    changeName() {
        this.setState({
            name: Monday Lab})}addItem() {
        this.setState({
            list: this.state.list.concat(`The ${Date.now()}`) // Use immutable values}}})export default List
Copy the code

The browser displays the following information:

From there, we inject a component into the component.

4) Native HTML

Moving on, let’s see how native HTML is used in React. Let’s start with the following code:

import React from 'react'

class JSXBaseDemo extends React.Component {
    render() {
        / / native HTML
        const rawHtml = ' Rich text content < I > italic 
        bold '
        const rawHtmlData = {
            // Assign rawHtml to __html
            __html: rawHtml // Note that it must be in this format
        }
        const rawHtmlElem = <div>
            <p dangerouslySetInnerHTML={rawHtmlData}></p>
            <p>{rawHtml}</p>
        </div>
        return rawHtmlElem
    }
}

export default JSXBaseDemo

Copy the code

The browser displays the following information:

As you can see, if you use native HTML in React, you must use const rawHtmlData = {__html: rawHtml} to parse the native HTML code. Otherwise react won’t parse native HTML properly.

2. Conditional judgment

(1) If else

Take a look at the following code:

import React from 'react'
import './style.css'

class ConditionDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            theme: 'black'}}render() {
        const blackBtn = <button className="btn-black">black btn</button>
        const whiteBtn = <button className="btn-white">white btn</button>

        // if else
        if (this.state.theme === 'black') {
            return blackBtn
        } else {
            return whiteBtn
        }
    }
}

export default ConditionDemo

Copy the code

The code for style.css is as follows:

.title {
    font-size: 30px;
    color: red;
}

.btn-white {
    color: #333;
}
.btn-black {
    background-color: #Awesome!;
    color: #fff;;
}

Copy the code

The browser displays:

As you can see, when we set the theme to black, we end up with black. If we set theme to any other state, the final display will be white.

(2) Ternary expressions

Let’s start with some code:

import React from 'react'
import './style.css'

class ConditionDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            theme: 'black'}}render() {
        
        const blackBtn = <button className="btn-black">black btn</button>
        const whiteBtn = <button className="btn-white">white btn</button>

        // The ternary operator
        return <div>
             { this.state.theme === 'black' ? blackBtn : whiteBtn }
        </div>}}export default ConditionDemo

Copy the code

The browser displays:

As you can see, we can also use the ternary this.state.theme === ‘black’? BlackBtn: The whiteBtn way to judge some conditions.

(3) logical operators && | |

Let’s start with some code:

import React from 'react'
import './style.css'

class ConditionDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            theme: 'black'}}render() {
        
        const blackBtn = <button className="btn-black">black btn</button>
        const whiteBtn = <button className="btn-white">white btn</button>

        / / &&
        return <div>
            { this.state.theme === 'black' && blackBtn }
        </div>}}export default ConditionDemo

Copy the code

The browser displays the same result as above. Details are as follows:

This.state. theme === ‘black’ && blackBtn If this.state.theme === ‘black’ && blackBtn if this.state.theme === ‘black’ && blackBtn

3. Render the list

(1) Map and key

Let’s start with some code:

import React from 'react'

class ListDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            list: [{id: 'id-1'.title: 'heading 1'
                },
                {
                    id: 'id-2'.title: 'title 2'
                },
                {
                    id: 'id-3'.title: 'title'}}}]render() {
        return <ul>{/* Similar to vue v-for */ this.state.list.map((item, index) => {// The key here is similar to vue key, mandatory, cannot be index or random return<li key={item.id}>
                            index {index}; id {item.id}; title {item.title}
                        </li>})}</ul>}}export default ListDemo

Copy the code

The browser displays the following information:

In React, list.map is used to render lists. Note that list.map() is a function, so for now we assume that list.map(item => item.id) follows a set of rules inside the function.

At this point, we treat.map as an array recombination, and the recombination rule is item => item.id. Meanwhile, list.map returns an array.

4. React events

(1) Bind this

Let’s start with some code:

import React from 'react'

class EventDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: 'zhangsan'
        }

        // Change the this pointer to the method
        // Bind is executed only once when the component is initialized
        this.clickHandler1 = this.clickHandler1.bind(this)}render() {
        // this - use bind
        return <p onClick={this.clickHandler1}>
            {this.state.name}
        </p>
    }
    clickHandler1() {
        // console.log('this.... ', this) // This defaults to undefined
        this.setState({
            name: 'lisi'}}})export default EventDemo

Copy the code

The final browser display looks like this:

In this code, we bind clickHandler1 with this.clickHandler1 = this.clickHandler1.bind(this).


There’s another way to bind this. Let’s start with some code:

import React from 'react'

class EventDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: 'zhangsan'}}render() {
       // this - Use static methods
        return <p onClick={this.clickHandler2}>
            clickHandler2 {this.state.name}
        </p>
    }
    Static method, this refers to the current instance
    clickHandler2 = () = > {
        this.setState({
            name: 'lisi'}}})export default EventDemo

Copy the code

The browser displays:

In this case, clickHandler2 is a static method whose this points to the current instance.

(2) About event parameters

Let’s start with some code:

import React from 'react'

class EventDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: 'zhangsan'}}render() {
       // event
        return <a href="https://imooc.com/" onClick={this.clickHandler3}>
            click me
        </a>
    }
    // Get event (key)
    clickHandler3 = (event) = > {
        event.preventDefault() // Prevent default behavior
        event.stopPropagation() // Prevent bubbling
        console.log('target', event.target) // Refers to the current element, i.e. the current element is triggered
        console.log('current target', event.currentTarget) // Point to the current element, false!!

        // React encapsulates event. __proto__. Constructor can be thought of as a SyntheticEvent composite event
        console.log('event', event) // It is not a native Event, it is a MouseEvent
        console.log('event.__proto__.constructor', event.__proto__.constructor)

        // The native event is as follows. Its __proto__. The constructor is MouseEvent
        console.log('nativeEvent', event.nativeEvent)
        console.log('nativeEvent target', event.nativeEvent.target)  // Refers to the current element, i.e. the current element is triggered
        console.log('nativeEvent current target', event.nativeEvent.currentTarget) // Point to document!!}}export default EventDemo

Copy the code

The browser displays the following information:

Based on the above, the following points need to be noted:

  • eventIt’s a composite eventSyntheticEventIt can be simulatedDOMThe ability of all events;
  • eventReactEncapsulated, andevent.nativeEventIs a native event object;
  • All events will be mounted todocumentOn;
  • ReactIn, andDOMThe events are different, andVueEvents are different.

(3) Pass custom parameters

Let’s start with some code:

import React from 'react'

class EventDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: 'zhangsan'.list: [{id: 'id-1'.title: 'heading 1'
                },
                {
                    id: 'id-2'.title: 'title 2'
                },
                {
                    id: 'id-3'.title: 'title'}}}]render() {
       // Pass arguments - bind(this, a, b)
        return <ul>{this.state.list.map((item, index) => {
            return <li key={item.id} onClick={this.clickHandler4.bind(this, item.id.item.title)} >
                index {index}; title {item.title}
            </li>
        })}</ul>
    }
    // Pass parameters
    clickHandler4(id, title, event) {
        console.log(id, title)
        console.log('event', event) // Add a final parameter to receive the event}}export default EventDemo

Copy the code

In this case, the browser displays:

As you can see, we pass arguments to react events using the form this.clickHandler4.bind(this, item.id, item.title).

(4) Pay attention

  • React 16Bind events todocumentOn;
  • React 17Bind events torootComponent;
  • The advantage of this is that it benefits manyReactVersions coexist, for exampleMicro front-end.

As shown below:

5, forms,

(1) Controlled components

Let’s start with some code:

import React from 'react'

class FormDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: Monday Lab.info: 'Personal Information'.city: 'GuangDong'.flag: true.gender: 'female'}}render() {

        // Controlled components
        return <div>
            <p>{this.state.name}</p>
            <label htmlFor="inputName">Name:</label>{/* replace for */} with htmlFor<input id="inputName" value={this.state.name} onChange={this.onInputChange}/>
        </div>
    }
    onInputChange = (e) = > {
        this.setState({
            name: e.target.value
        })
    }
}

export default FormDemo

Copy the code

The browser displays the following information:

React uses the onChange event to manually change the value of state.

(2) Input textarea select value

Now that we’ve covered input, let’s look at textarea and SELECT.

Let’s start with the textarea code:

import React from 'react'

class FormDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: Monday Lab.info: 'Personal Information'.city: 'GuangDong'.flag: true.gender: 'female'}}render() {

        // textarea - uses value
        return <div>
            <textarea value={this.state.info} onChange={this.onTextareaChange}/>
            <p>{this.state.info}</p>
        </div>
    }
    onTextareaChange = (e) = > {
        this.setState({
            info: e.target.value
        })
    }
}

export default FormDemo

Copy the code

The browser will print:

Similarly, textarea binds values with value and onChange.


Let’s move on to select. The specific code is as follows:

import React from 'react'

class FormDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: Monday Lab.info: 'Personal Information'.city: 'GuangDong'.flag: true.gender: 'female'}}render() {

        // textarea - uses value
        return <div>
            <textarea value={this.state.info} onChange={this.onTextareaChange}/>
            <p>{this.state.info}</p>
        </div>
    }
    onSelectChange = (e) = > {
        this.setState({
            city: e.target.value
        })
    }
}

export default FormDemo

Copy the code

In this case, the browser displays:

As with input and textarea, the final value is changed by manipulating value and onChange.

(3) checked the radio

Let’s start with ckeckbox. The code is as follows:

import React from 'react'

class FormDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: Monday Lab.info: 'Personal Information'.city: 'GuangDong'.flag: true.gender: 'female'}}render() {

        // checkbox
        return <div>
            <input type="checkbox" checked={this.state.flag} onChange={this.onCheckboxChange}/>
            <p>{this.state.flag.toString()}</p>
        </div>
    }
    onCheckboxChange = () = > {
        this.setState({
            flag:!this.state.flag
        })
    }
}

export default FormDemo

Copy the code

The browser displays:

In the code above, the checkbox changes the value of state by manipulating checked and onChange.


Radio is similar, as shown in the following code:

import React from 'react'

class FormDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: Monday Lab.info: 'Personal Information'.city: 'GuangDong'.flag: true.gender: 'female'}}render() {

        // radio
        return <div>
            male <input type="radio" name="gender" value="male" checked={this.state.gender= = ='male'} onChange={this.onRadioChange}/>
            female <input type="radio" name="gender" value="female" checked={this.state.gender= = ='female'} onChange={this.onRadioChange}/>
            <p>{this.state.gender}</p>
        </div>
    }
    onRadioChange = (e) = > {
        this.setState({
            gender: e.target.value
        })
    }
}

export default FormDemo

Copy the code

The browser displays:

6. Use of components

There are three things you need to know about parent-child components: props passing data, props passing function, and props type checking.

Let’s start with some code:

import React from 'react'
import PropTypes from 'prop-types'

class Input extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            title: ' '}}render() {
        return <div>
            <input value={this.state.title} onChange={this.onTitleChange}/>
            <button onClick={this.onSubmit}>submit</button>
        </div>
    }
    onTitleChange = (e) = > {
        this.setState({
            title: e.target.value
        })
    }
    onSubmit = () = > {
        const { submitTitle } = this.props
        submitTitle(this.state.title)

        this.setState({
            title: ' '}}})// props type check
Input.propTypes = {
    submitTitle: PropTypes.func.isRequired
}

class List extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        const { list } = this.props

        return <ul>{list.map((item, index) => {
            return <li key={item.id}>
                <span>{item.title}</span>
            </li>
        })}</ul>}}// props type check
// propTypes makes it clear what type of data a list needs
List.propTypes = {
    list: PropTypes.arrayOf(PropTypes.object).isRequired
}

class Footer extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        return <p>
            {this.props.text}
            {this.props.length}
        </p>
    }
    componentDidUpdate() {
        console.log('footer did update')}shouldComponentUpdate(nextProps, nextState) {
        if(nextProps.text ! = =this.props.text || nextProps.length ! = =this.props.length) {
            return true // Can render
        }
        return false // Do not repeat rendering
    }

    // React default: the parent component is updated and the child component is updated unconditionally!!
    // Performance tuning is more important for React!
    // Does the SCU have to be used every time? -- optimize when needed (SCU is shouldComponentUpdate)
}

/ / the parent component
class TodoListDemo extends React.Component {
    constructor(props) {
        super(props)
        // The status (data) is improved
        // The list data needs to be placed in the parent component
        this.state = {
            list: [{id: 'id-1'.title: 'heading 1'
                },
                {
                    id: 'id-2'.title: 'title 2'
                },
                {
                    id: 'id-3'.title: 'title'}].footerInfo: 'Bottom text'}}render() {
        return <div>
            <Input submitTitle={this.onSubmitTitle}/>
            <List list={this.state.list}/>
            <Footer text={this.state.footerInfo} length={this.state.list.length}/>
        </div>
    }
    onSubmitTitle = (title) = > {
        this.setState({
            list: this.state.list.concat({
                id: `id-The ${Date.now()}`,
                title
            })
        })
    }
}

export default TodoListDemo

Copy the code

The browser displays the following information:

Based on the code above, we introduce the various types of props.

(1) Props pass data

The last TodoListDemo is the parent, and all the others are children. In the Input component and the List component, we pass the contents of the props property to the parent component as this.props. XXX.

(2) Props transfer function

React differs from Vue in the transfer function part. For vue, if a parent component wants to pass a function to a child component, then the child component needs to use event passing and $emit to fire the function.

Navigate to the Input component, where we pass the submitTitle as a function to the onSubmitTitle in the parent component.

(3) Props type check

Let’s locate two props type checks. Using PropTypes in React, we can do a type check on the currently used properties. For example: submitTitle: PropTypes. Func. IsRequired show that submitTitle is a function, and is a mandatory.

In this way, we learned about attribute passing, attribute validation, and how parent and child components communicate in the form of passing events.

7, setState

(1) Immutable value

An immutable value means that the set value never changes. At this point, we need to create a copy to set the value of state.

A few highlights:

First point: State is defined in the constructor. The following code looks like this:

import React from 'react'

// The function component has no state by default
class StateDemo extends React.Component {
    constructor(props) {
        super(props)

        // First, state is defined in the constructor
        this.state = {
            count: 0}}render() {
        return <div>
            <p>{this.state.count}</p>
        </div>}}export default StateDemo

Copy the code

Second, don’t change state directly; use immutable values. The following code looks like this:

import React from 'react'

// The function component has no state by default
class StateDemo extends React.Component {
    constructor(props) {
        super(props)

        // First, state is defined in the constructor
        this.state = {
            count: 0}}render() {
        return <div>
            <p>{this.state.count}</p>
        	<button onClick={this.increase}>cumulative</button>
        </div>
    }
    increase= () = > {
        // Second, do not modify state directly, use immutable values
        // this.state.count++ // if the value is incorrectly written, the original value will be directly modified
        this.setState({
            count: this.state.count + 1 / / ShouldComponentUpdate to SCU}}})export default StateDemo

Copy the code

As you can see in the code above, we modify the value of state in the form this.state({}). Note that many users use this.state.count++ to change the value of state. This is not allowed in react. Therefore, pay attention to this point.

Third, react operates on array values. The following code looks like this:

// Immutable values (functional programming, pure functions) - arrays
const list5Copy = this.state.list5.slice()
list5Copy.splice(2.0.'a') // Insert/delete in between
this.setState({
    list1: this.state.list1.concat(100), / / append
    list2: [...this.state.list2, 100]./ / append
    list3: this.state.list3.slice(0.3), / / interception
    list4: this.state.list4.filter(item= > item > 100), / / filter
    list5: list5Copy // Other operations
})
// Note that you cannot push pop splice, etc. on this.state.list directly, as this violates immutable values
Copy the code

Fourth, react operates on object values. The following code looks like this:

// Immutable value - object
this.setState({
    obj1: Object.assign({}, this.state.obj1, {a: 100}),
    obj2: {... this.state.obj2,a: 100}})// Note that you cannot attribute this.state.obj directly, such as this.state.obj.xxx, which would violate immutable values
Copy the code

(2) It may be asynchronous update

The state in React might update asynchronously. Take a look at this code:

import React from 'react'

class StateDemo extends React.Component {
    constructor(props) {
        super(props)

        this.state = {
            count: 0}}render() {
        return <div>
            <p>{this.state.count}</p>
        	<button onClick={this.increase}>cumulative</button>
        </div>
    }
    increase= () = > {
        // setState can be asynchronously updated (or synchronously updated)
        this.setState({
            count: this.state.count + 1
        }, () = > {
            Vue $nexttick-dom
            console.log('count by callback'.this.state.count) // The latest state is available in the callback function
        })
        console.log('count'.this.state.count) // Async, can not get the latest value}}export default StateDemo

Copy the code

The browser displays:

As you can see, the first half of this.state is not updated at the same time, so it is asynchronous. The contents of the latter arrow function can be updated synchronously, so the latter part of the function is the synchronization operation.


It is worth noting that setTimeout is synchronous in setState. Take a look at this code:

import React from 'react'

class StateDemo extends React.Component {
    constructor(props) {
        super(props)

        this.state = {
            count: 0}}render() {
        return <div>
            <p>{this.state.count}</p>
        	<button onClick={this.increase}>cumulative</button>
        </div>
    }
    increase= () = > {
        // setState is synchronized in setTimeout
        setTimeout(() = > {
            this.setState({
                count: this.state.count + 1
            })
            console.log('count in setTimeout'.this.state.count)
        }, 0)}}export default StateDemo

Copy the code

In this case, the browser displays:


One other thing to note is that if it’s a self-defined DOM event, it’s synchronized in setState, used in componentDidMount.

If it is a destruction event, it is used in the componentWillMount life cycle. The code is as follows:

import React from 'react'

class StateDemo extends React.Component {
    constructor(props) {
        super(props)

        this.state = {
            count: 0}}render() {
        return <div>
            <p>{this.state.count}</p>
        	<button onClick={this.increase}>cumulative</button>
        </div>
    }
    bodyClickHandler = () = > {
        this.setState({
            count: this.state.count + 1
        })
        console.log('count in body event'.this.state.count)
    }
    componentDidMount() {
        // The setState of the DOM event is synchronized
        document.body.addEventListener('click'.this.bodyClickHandler)
    }
    componentWillUnmount() {
        // Destroy custom DOM events in time
        document.body.removeEventListener('click'.this.bodyClickHandler)
        // clearTimeout}}export default StateDemo

Copy the code

The browser displays:

(3) May be merged

When an object is passed in, setState is merged before it is updated. Take a look at this code:

import React from 'react'

class StateDemo extends React.Component {
    constructor(props) {
        super(props)

        this.state = {
            count: 0}}render() {
        return <div>
            <p>{this.state.count}</p>
        	<button onClick={this.increase}>cumulative</button>
        </div>
    }
    increase= () = > {
        // Incoming objects will be merged (similar to object.assign). The result is +1 only once
        this.setState({
            count: this.state.count + 1
        })
        this.setState({
            count: this.state.count + 1
        })
        this.setState({
            count: this.state.count + 1}}})export default StateDemo

Copy the code

The browser displays:

Some people might think that if you add three setStates at a time, the result should be +3. But in fact, if the object is passed in, the result will be three merged into one, and only executed once.


In another case, if a function is passed in, the result will not be merged. Take a look at this code:

import React from 'react'

class StateDemo extends React.Component {
    constructor(props) {
        super(props)

        this.state = {
            count: 0}}render() {
        return <div>
            <p>{this.state.count}</p>
        	<button onClick={this.increase}>cumulative</button>
        </div>
    }
    increase= () = > {
        // The function passed in will not be merged. The result is +3
        this.setState((prevState, props) = > {
            return {
                count: prevState.count + 1}})this.setState((prevState, props) = > {
            return {
                count: prevState.count + 1}})this.setState((prevState, props) = > {
            return {
                count: prevState.count + 1}}}})export default StateDemo

Copy the code

The browser displays:

As you can see, if you pass in a function, the result is executed three times.

Component life cycle

React’s component lifecycle includes a single component declaration cycle and a parent component declaration cycle. The parent-child component life cycle is similar to Vue.

Here attached a websites related to life cycle: the projects. Wojtekmaj. Pl/react – lifec…

A diagram of the life cycle is attached below:

📖 2. React Advanced features

1. Function components

Let’s look at what a class component and a function component look like. First look at the class component, which looks like this:

/ / class components
class List extends React.Component {
    constructor(props) {
        super(props)
    }
    redner() {
        const { list } = this.props
        
        return <ul>{list.map((item, index) => {
            return <li key={item.id}>
                	<span>{item.title}</span>
                </li>
        })}</ul>}}Copy the code

Function components are of the following form:

// Function components
function List(props) {
    const { list } = this.props
    
    return <ul>{list.map((item, idnex) => {
        return <li key={item.id}>
            	<span>{item.title}</span>
            </li>
    })}</ul>
}
Copy the code

Now let’s tease out the differences between a class component and a function component. The so-called function component has the following characteristics:

  • Just aPure functionsAnd what it inputs ispropsAnd the output isJSX
  • Function components have no instances, no life cycles, and no states;
  • Function components cannot extend other methods.

In contrast, a class component has the characteristics of a functional component.

2. Uncontrolled components

In the form module above, we talked about controlled components. Next, let’s talk about uncontrolled components.

An uncontrolled component is a value inside an input that is not controlled by state. Let’s take a look at a few scenarios.

(1) input

Let’s start with some code:

import React from 'react'

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: Monday Lab.flag: true,}this.nameInputRef = React.createRef() / / create the ref
    }
    render() {
        // input defaultValue
        return <div>{/* use defaultValue instead of value, use ref */}<input defaultValue={this.state.name} ref={this.nameInputRef}/>{/* state does not change with */}<span>state.name: {this.state.name}</span>
            <br/>
            <button onClick={this.alertName}>alert name</button>
        </div>

    }
    alertName = () = > {
        const elem = this.nameInputRef.current // Get the DOM node from ref
        alert(elem.value) / / not enclosing state. The name}}export default App

Copy the code

The browser displays:

As you can see, if the component is uncontrolled, you need to use defaultValue to control the value of the component. And no matter how we change the contents of the final input box, the value of state will not be affected.

(2) the checkbox

For the checkbox, look at the following code:

import React from 'react'

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: Monday Lab.flag: true,}}render() {
        // checkbox defaultChecked
        return <div>
            <input
                type="checkbox"
                defaultChecked={this.state.flag}
            />
           	<p>state.name: { this.state.flag === true ? 'true' : 'false' }</p>
        </div>}}export default App

Copy the code

The browser displays the following information:

As you can see, if the checkbox is used when an uncontrolled component is used, then use defaultCkecked to control the value. And we also see that ultimately, no matter how checked changes, state doesn’t change.

(3) the file

Let’s start with some code:

import React from 'react'

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: Monday Lab.flag: true,}this.fileInputRef = React.createRef()
    }
    render() {
        // file
        return <div>
            <input type="file" ref={this.fileInputRef}/>
            <button onClick={this.alertFile}>alert file</button>
        </div>
    }
    alertFile = () = > {
        const elem = this.fileInputRef.current // Get the DOM node from ref
        alert(elem.files[0].name)
    }
}

export default App

Copy the code

The browser displays:

In the above code, we use ref to get the DOM node, and then to get the file name. A fixed type like file, whose value is not always fixed, is also an uncontrolled component.

(4) Summary and sorting

SetState can only handle display and render related things like the front end, but not interaction types like file uploads. Let’s take a look at some of the most common usage scenarios for uncontrolled components. Details are as follows:

  • Must be done manuallyDOMElements,setStateYou can’t do it manuallyDOMElements;
  • File Upload Type<input type=file>
  • Some rich text editors need to be passed inDOMElements.

The difference between a controlled component and an uncontrolled component is as follows:

  • Preferred to useThe controlled components, in line with theReactDesign principles;
  • Must operateDOM, and then useUncontrolled component.

3, Protals

(1) Why Protals?

By default, components are rendered in a nested hierarchy. Something like this:

<div id="root">
    <div>
        <div>
            <div class="model">Content of the Modal</div>
        </div>
    </div>
</div>
Copy the code

As you can see, this is nesting, but there’s only one layer of content that’s useful. In a way, it’s very bad. So what we want to do is, how do we make this component render outside of the parent component?

That’s where Protals come in.

(2) How to use it

Let’s start with some code:

import React from 'react'
import ReactDOM from 'react-dom'
import './style.css'

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
        }
    }
    render() {
        // Render normally
        // return <div className="modal">
        // {this.props.children} {/* vue slot */}
        // </div>

        // Use Portals to render on the body.
        // The fixed element should be placed on the body for better browser compatibility.
        return ReactDOM.createPortal(
            <div className="modal">{this.props.children}</div>.document.body / / the DOM node)}}export default App

Copy the code

Style.css looks like this:

.modal {
    position: fixed;
    width: 300px;
    height: 100px;
    top: 100px;
    left: 50%;
    margin-left: -150px;
    background-color: #000;
    /* opacity: .2; * /
    color: #fff;
    text-align: center;
}
Copy the code

At this point, let’s take a look at the rendering of the browser node. Details are as follows:

As you can see, Portals are created by using reactdom.createPortal (). Finally, the Modals node is successfully detached from the parent component and rendered outside of the component.

(3) Usage scenarios

Now, let’s comb through some common scenes of Protals.

Protals is often used to solve CSS compatibility problems. Common usage scenarios include:

  • overflow:hidden;The triggerbfc
  • The parent componentz-indexValue is too small;
  • position:fixedNeed to put inbodyLevel one.

4, the context

(1) Usage scenarios

Sometimes we often have scenes that switch frequently, such as language switch, or theme switch, so how to communicate the corresponding switch information to each component effectively?

Using props, it’s a little cumbersome; With Redux, too much drama.

So, this requires we can use the context in React.

(2) Illustrate with examples

Let’s start with some code:

import React from 'react'

// Create a Context and fill in the default value (any js variable)
const ThemeContext = React.createContext('light')

// Underlying component - functions are components
function ThemeLink (props) {
    // const theme = this.context // an error is reported. Functional components have no instances, that is, no this

    // Functional components can use Consumer
    return <ThemeContext.Consumer>
        { value => <p>link's theme is {value}</p> }
    </ThemeContext.Consumer>
}

// Underlying component - class component
class ThemedButton extends React.Component {
    // Specify contextType to read the current theme context.
    // static contextType = ThemeContext // ThemedButton.contextType = ThemeContext
    render() {
        const theme = this.context // React will find the nearest theme Provider and use its value.
        return <div>
            <p>button's theme is {theme}</p>
        </div>
    }
}
ThemedButton.contextType = ThemeContext // Specify contextType to read the current theme context.

// Intermediate components no longer have to specify the theme to pass down.
function Toolbar(props) {
    return (
        <div>
            <ThemedButton />
            <ThemeLink />
        </div>)}class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            theme: 'light'}}render() {
        return <ThemeContext.Provider value={this.state.theme}>
            <Toolbar />
            <hr/>
            <button onClick={this.changeTheme}>change theme</button>
        </ThemeContext.Provider>
    }
    changeTheme = () = > {
        this.setState({
            theme: this.state.theme === 'light' ? 'dark' : 'light'}}})export default App

Copy the code

The browser displays:

In the image above, we’ve switched themes. Now, let’s examine the above code.

First, we create a Context, ThemeContext, and pass in the light value.

Second, the core is in the
component. Existing Toolbar components are ThemedButton and The Link. The contextType of ThemedButton is specified to read the ThemeContext, and the default is light.

Next, come the ThemeLink component. ThemeLink is a functional component, so we can pass values to themecontext.consumer directly.

The values for both components are taken, but that is just the initial value for ThemeContext. React finds the most recent themecontext. Provider and uses value={this.state.theme} to modify and use the ThemeContext value.

5. Asynchronous components (lazy loading)

During project development, it is inevitable to load large components, and asynchronous loading is required. In Vue, we usually use import() to load asynchronous components, but not in React.

React usually uses React. Lazy and React.Suspense to load large components.

The following code looks like this:

import React from 'react'

const ContextDemo = React.lazy(() = > import('./ContextDemo'))

class App extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        return <div>
            <p>Introduce a dynamic component</p>
            <hr />
            <React.Suspense fallback={<div>Loading...</div>} ><ContextDemo/>
            </React.Suspense>
        </div>

        // 1. You can see the loading loading as follows:
        // 2. Watch network js load}}export default App

Copy the code

First we use import to import the component we want to load. Then use React. Lazy to register the component, which is ContextDemo. Finally use React.Suspense to load ContextDemo. At this point, we are finished loading the asynchronous component.

6. Performance optimization

(1) shouldComponentUpdate (SCU)

Let’s start with the following code:

shouldComponentUpdate(nextProps, nextState) {
    if(nextState.count ! = =this.state.count || nextProps.text ! = =this.props.length) {
        return true // Can render
    }
    return false // Do not repeat rendering
}
Copy the code

In React, by default, when the parent component is updated, the child component is updated unconditionally. It would not be good if updates were triggered every time.

Therefore, we need to use shouldComponentUpdate to determine if the render should be triggered when the property changes. When the property does not change, that is, when the values are the same, no rendering is triggered.

At this time, we need to consider a question: must SCU be used every time? The answer is not yes.

We’re going to use SCU, in some sense for optimization. Therefore, we need to optimize as needed, based on the current development scenario.

Now, let’s summarize the use of SCU as follows:

  • SCU The defaultreturntrue, i.e.,ReactRerender all child components by default;
  • Must be used with immutable values;
  • Can not firstSCU, and then consider using it when there are performance problems.

(2) PureComponent and React. Memo

PureComponent is used in react as follows:

class List extends React.PureComponent {
    constructor(props) {
        super(props)
    }
    render() {
        const { list } = this.props

        return <ul>{list.map((item, index) => {
            return <li key={item.id}>
                <span>{item.title}</span>
            </li>
        })}</ul>
    }
    shouldComponentUpdate() {/* shallow comparison */}}Copy the code

If we use PureComponent, the SCU will do a shallow comparison, layer by layer.


Let’s look at the memo. A: Let’s have a memo. React is used as follows:

function MyComponent(props) {
    /* props render */
}

function areEqual(prevProps, nextProps) {
    /* Return true if nextProps is the same as preProps if render is the same, otherwise false */
}
export default React.memo(MyComponent, areEqual);
Copy the code

Memo, so to speak, is the PureComponent of a function component. Meanwhile, using the form react.Memo (), we compare our function component to the value of areEqual and return a new function.

It is worth noting that shallow comparisons are used in most situations in React. In general, try not to do deep comparisons.

(3) Immutable value

In React, there is a library for Immutable values: Immutable. Js. This library has the following features:

  • Embrace immutable values

  • Based on shared data (not deep copy), good speed

  • There are certain learning and migration costs to use on demand

Here’s an example:

const map1 = Immutable.Map({ a: 1.b: 2.c: 3 })
const map2 = map1.set('b'.50)
map1.get('b') / / 2
map2.get('b') / / 50
Copy the code

Basically, this library is now used in development to deal with immutable values. In actual use, you can see the official documents as needed.

7. The withdrawal of common logic for components

In React, there are three main ways to detach component common logic. Details are as follows:

  • mixinThat has beenReactdeprecated
  • High order componentHOC
  • Render Props

The high-level components HOC and Render Props will be explained below.

(1) High-order component HOC

Let’s start with some code:

// Higher-order components are not a feature, but a design pattern
// 1. Pass in a Component
const HOCFactory = (Component) = > {
    class HOC extends React.Component {
        // Define common logic for multiple components here
        render() {
            // 2. Return the result of the concatenation
            return <Component {this.props} / >}}return HOC
}
const EnhancedComponent1 = HOCFactory(WrappedComponent1)
const EnhancedComponent2 = HOCFactory(WrappedComponent2)
Copy the code

High-level component HOC is passing in a component and returning a new component, as shown in code 1 and 2 above.


Let’s look at an example, as shown in the following code:

import React from 'react'

// Higher-order components
const withMouse = (Component) = > {
    class withMouseComponent extends React.Component {
        constructor(props) {
            super(props)
            this.state = { x: 0.y: 0 }
        }
  
        handleMouseMove = (event) = > {
            this.setState({
                x: event.clientX,
                y: event.clientY
            })
        }
  
        render() {
            return (
                <div style={{ height: '500px'}}onMouseMove={this.handleMouseMove}>{/* 1. Pass through all props 2.<Component {. this.props} mouse={this.state}/>
                </div>)}}return withMouseComponent
}

const App = (props) = > {
    const { x, y } = props.mouse // Receive mouse attributes
    return (
        <div style={{ height: '500px' }}>
            <h1>The mouse position is ({x}, {y})</h1>
        </div>)}export default withMouse(App) // Return the higher-order function

Copy the code

The browser displays the following information:

In the code above, we defined the higher-order withMouse Component, which passes the

passes out the mouse properties of the props and props parameters for use by the child component App.


It’s worth noting that one of the more common higher-order components in React is Redux Connect. Use this code to demonstrate:

import { connect } from 'react-redux';

// Connect is a higher-order component
const VisibleTodoList = connect(
	mapStateToProps,
    mapDispatchToProps
)(TodoList)

export default VisibleTodoList

Copy the code

Now, let’s look at the source of CONNECT, as follows:

export const connect =(mapStateToProps, mapDispatchToProps) = > (WrappedComponent) = > {
    class Connect extends Component {
        constructor() {
            super(a)this.state = {
                allProps: {}}}/* omit N lines */
        render () {
			return <WrappedComponent {. this.state.allProps} / >}}return Connect
}
Copy the code

As you can see, Connect does the same thing, passing in a component and returning a component.

(2) Render Props

Let’s start with some code:

// The core idea of Render Props
// 1. Pass the state of the class component as props to the pure function component via a function
class Factory extends React.Component {
    constructor() {
        tihs.state = {
            /* state is the common logical data of multiple components */}}/* 2
    render() {
        return <div>{this.props.render(this.state)}</div>}}const App = () = > {
    // 3. Use the higher-order component here, passing in the render attribute from the higher-order component
    <Factory render={/ *renderIs a function component */ (props) = > <p>{props. A}} {props. B...</p>} / >
}

export default App;
Copy the code

In the higher-order component HOC above, the resulting return is also a higher-order component. But in the Render Props, we wrapped the Factory in the defined App component and finally returned the App.

It’s worth noting that in Vue there is a similar use of higher-order components, but not a similar use of Render Props, which needs a little attention.


Here’s an example, with the following code:

import React from 'react'
import PropTypes from 'prop-types'

class Mouse extends React.Component {
    constructor(props) {
        super(props)
        this.state = { x: 0.y: 0 }
    }
  
    handleMouseMove = (event) = > {
      this.setState({
        x: event.clientX,
        y: event.clientY
      })
    }
  
    render() {
      return (
        <div style={{ height: '500px'}}onMouseMove={this.handleMouseMove}>{/* Pass the current state to render(render is a function component) */} {this.props. Render (this.state)}</div>
      )
    }
}
Mouse.propTypes = {
    render: PropTypes.func.isRequired // You must receive a render attribute, which is a function
}

const App = (props) = > (
    <div style={{ height: '500px' }}>
        <Mouse render={/ *renderIs a function component */ ({x.y}) = > <h1>The mouse position is ({x}, {y})</h1>} / ></div>
)

/** * that is, the Mouse component is defined with only the ability to get x and y. * As for how Mouse components are rendered, the App tells Mouse through Render Prop. * /

export default App

Copy the code

The browser displays the following information:

In the above code, we pass the properties of the Mouse component to the App in the form of this.props. Render (this.state) and let the App use the properties of the Mouse successfully.

(3) HOC vs Render Props

Now, let’s tease out the difference between HOC and Render Props, as follows:

  • HOC: The pattern is simple, but adds layers of components
  • Render Props: Simple code, high learning cost
  • Each has its own advantages and disadvantages, and can be used according to the actual situation

📚 Redux and React-router

1, the story

(1) Brief introduction of the concept of Redux

React is a lightweight framework that doesn’t have a view layer. If you want to use it to pass data around, you pass it from parent to child, and then slowly move up the hierarchy.

But with Redux, if we want the data for a component, the data for that component will be stored in a store and managed by Redux. Then, through the Store, the data is passed step by step to the following components.

It is worth noting that we can view Redux as a combination of Reducer and Flux.

(2) Work flow of Redux

Redux is essentially a data layer framework that puts all data in a Store. Let’s start with a picture:

You can see the store in the middle, where all the data is stored. If you look at the arrow down store, then each component is going to fetch data from the store.

We use an example to tease out the whole picture, as follows:

  • ① There is one on the whole picturestoreIt holds all the data, i.eA common area for storing data;
  • ② Each component should be fromstoreTake the data inside;
  • ③ Suppose we have a situation where we need to borrow books from a library. So we can takereact ComponentAfter that, the borrower has to go to the librarian to get the book. And the transfer of data in the process of borrowing a book can be regarded asAction CreatorsCan be understood as“What book do you want to borrow?”This sentence.
  • (4)Action CreaturesGo to thestore. At this point we’re going tostoreAs alibrarianHowever, there is no way for a librarian to remember the data status of all the books. Generally speaking, it requires a record book, you want to borrow what kind of books, so she will first check; Or if you want to return a book, she also checks to see where it needs to be put back.
  • ⑤ At this time, it is necessary to followreducersGo to the correspondence. We can getreducersRegard as abookLibrarians use thisbookTo record the data you need. The administratorstorethroughreducerI know you should give it to the borrowerComponentsWhat kind of data.

(2) the react – story

The points to understand in React-Redux are Provider, Connect, mapStateToProps, and mapDisptchToProps.

Take a look at the following code:

import React from 'react'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'

let store = createStore(todoApp)

export default function() {
    return <Provider store={store}>
        	<App />
        </Provider>
}
Copy the code

React-redux provides the ability to provide a Provider. You can see the code at the end. The Provider wraps
, which means it provides store capabilities for all the components it wraps around.

Here’s another piece of code:

import { connect } from 'react-redux'
import { toggleTodo } from '.. /actions'
import TodoList from '.. /components/TodoList'

// Lists of different types of todo
const getVisibleTodos = (todos, filter) = > {
  switch (filter) {
    case 'SHOW_ALL':
      return todos
    case 'SHOW_COMPLETED':
      return todos.filter(t= > t.completed)
    case 'SHOW_ACTIVE':
      return todos.filter(t= >! t.completed) } }const mapStateToProps = state= > {
  // State is the total vuex status defined in Reducer /index.js
  return {
    // Filter data based on completion status
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}

const mapDispatchToProps = dispatch= > {
  return {
    // The switchover is complete
    onTodoClick: id= > {
      dispatch(toggleTodo(id))
    }
  }
}

// Connect the higher-order component to inject state and dispatch into the component props
const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

export default VisibleTodoList
Copy the code

In the code above, connect injects state and dispatch into the props of the component, passing properties to the TodoList component.

(3) Asynchronous action

The sync action in redux looks like this:

The action / / synchronization
export const addTodo = text= > {
    // Return the action object
    return {
        type: 'ADD_TODO'.id: nextTodoId++,
        text
    }
}
Copy the code

The asynchronous action in redux looks like this:

/ / asynchronous action
export const addTodoAsync = text= > {
    // Returns a function with the dispatch argument
    return (dispatch) = > {
        // Ajax gets data asynchronously
        fetch(url).thne(res= > {
            // Perform an asynchronous action
            dispatch(addTodo(res.text))
        })
    }
}
Copy the code

(4) Redux data flow diagram

The single data flow diagram of Redux is as follows:

For more details on Redux, check out this article: Redux from beginner to advanced, just read this one!

2, the React – the router

(1) Routing mode

The React-router and vue-router work in two modes. Details are as follows:

  • hashMode (default), such ashttp://abc.com/#/user/10
  • H5 historyPatterns, such ashttp://abc.com/user/20
  • The latter requiresserverEnd support, so no special requirements can choose the former

The hash route configuration code is as follows:

import React from 'react'
import {
    HashRouter as Router,
    Switch,
    Route
} from 'react-router-dom'

function RouterComponent() {
    return(
    	<Router>
        	<Switch>
        		<Route exact path="/">
        			<Home />
        		</Route>
        		<Route exact path="/project/:id">
        			<Project />
        		</Route>
        		<Route path="*">
        			<NotFound />
        		</Route>
        	</Switch>
        </Router>)}Copy the code

The route configuration in History mode is as follows:

import React from 'react'
import {
    BrowserRouter as Router,
    Switch,
    Route
} from 'react-router-dom'

function RouterComponent() {
    return(
    	<Router>
        	<Switch>
        		<Route exact path="/">
        			<Home />
        		</Route>
        		<Route exact path="/project/:id">
        			<Project />
        		</Route>
        		<Route path="*">
        			<NotFound />
        		</Route>
        	</Switch>
        </Router>)}Copy the code

Note that the difference between Hash and history is the HashRouter and BrowserRouter in import.

Learn more about hash and history in this article: A Brief introduction to front-end routing principles Hash and History

(2) Route configuration

ⅰ. Dynamic routing

Suppose we now have a parent RouterComponent that looks like this:

function RouterComponent() {
    return(
    	<Router>
        	<Switch>
        		<Route exact path="/">
        			<Home />
        		</Route>
        		<Route exact path="/project/:id">
        			<Project />
        		</Route>
        		<Route path="*">
        			<NotFound />
        		</Route>
        	</Switch>
        </Router>)}Copy the code

There is also a Project component within this component that requires dynamic parameter passing.


Moving on, let’s look at how the subcomponent Project component performs dynamic parameter passing. The specific code is as follows:

import React from 'react'
import { Link, useParams } from 'react-router-dom'

function Project() {
    // Get url parameters such as '/project/100'
    const { id } = useParams()
    console.log('url param id', id)
    
    return (
    	<div>
        	<Link to="/">Home page</Link>
        </div>)}Copy the code

As you can see, React uses const {id} = useParams() to dynamically pass arguments.


There is another case of jump routing. Take a look at the following code:

import React from 'react'
import { useHistory } from 'react-router-dom'

function Trash() {
    let history = useHistory()
    function handleClick() {
        history.push('/')}return (
    	<div>
        	<Button type="primary" onClick={handleClick}>Back to the home page</Button>
        </div>)}Copy the code

As you can see, by using useHistory, click events to jump to the home page.

Ⅱ. Lazy loading

import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import React, { Suspence, lazy } from 'react';

const Home = lazy(() = > import('./routes/Home'));
const About lazy(() = > import('./routes/About'));

const App = () = > {
    <Router>
    	<Suspense fallback={<div>Loading...</div>} ><Switch>
        		<Route exact path="/" component={Home}/>
                 <Route path="/about" component={About}/>
        	</Switch>
        </Suspense>    
    </Router>
}
Copy the code

In React, we can use lazy() wrapping to lazily load the content of the page. There is also the case where Loading is like Loading a page for the first time. In React you can use Suspense> to solve this problem.

🗞️

In the previous article, we covered the basic uses and advanced features of React. React plugins Redux and React-Router are also introduced.

In react projects, the front-end is always inseparable from the knowledge points mentioned in the above articles. The only difference is that the basic content is used more, while the application scenarios of advanced features are relatively few.

Hope that through the above explanation, small partners have harvest 🥂

  • Pay attention to the public on Monday laboratory, the first time to learn dry goods, more “offer to” interview column for you to unlock ~
  • If this article is useful to you, be sure to leave a footprint
  • See you next time! 🔑 🔑 🔑