Default value for the React property

Notifys React of the default properties with a static property called defaultProps

// app.jsx file uses Comp component and passes properties
import React from 'react'
import ReactDOM from 'react-dom'
import Comp from './Comp'

const MOUNT_NODE = document.getElementById('root')
ReactDOM.render(<Comp a={5} b={6} />, MOUNT_NODE)
Copy the code

Function component writing

In terms of presentation, the blending of the properties passed by the parent component and the default properties is complete before the function runs

// com.jsx function component
import React from 'react'

const Comp = props= > {
    console.log(props) {a: 5, b: 6, c: 3}}
    return (
        <div>
            a: {props.a}, b: {props.b}, c: {props.c}
        </div>)}/* Property default declaration */
Comp.defaultProps = {
    a: 1.b: 2.c: 3
}
export default Comp
Copy the code

Class component writing

From a presentation point of view, the properties passed by the parent component are mixed with the default properties before the constructor constructor is run (i.e. the properties are mixed during initialization)

// Comp.jsx class components
import React, {Component} from 'react'

class Comp extends Component {
    /* Method 1: Use the static keyword to declare the default property */
    static defaultProps = {
        a: 1.b: 2.c: 3
    }
    constructor(props) {
        super(props)
        console.log(props) {a: 5, b: 6, c: 3}}
    }
    render() {
        return (
            <div> a: {this.props.a}, b: {this.props.b}, c: {this.props.c} </div>)}}/* Method 2: declare the default value of the attribute */
// Comp.defaultProps = {
// a: 1,
// b: 2,
// c: 3
// }
export default Comp
Copy the code

Attribute type check —prop-types

Use the static propTypes property on components to tell React how to check types (React has a prop-types package built into it)

It does not affect the execution of the code, only does type checking at compile time during the development phase, and prints warnings on the console about non-compliance with type constraints

// Comp.jsx class components
import React, {Component} from 'react'
import PropTypes from 'prop-types'

class Comp extends Component {
    static propTypes = {
        a: PropTypes.number.isRequired /* Constraint a attribute is numeric and */ is mandatory
    }
    render() {
        return (
            <div>
                a: {this.props.a}
            </div>)}}export default Comp
Copy the code

It is a weakly typed type checking system compared to TypeScript, and the runtime performance is similar to that of the runtime

A better solution, of course, is to use TypeScript with its strong typing constraints to avoid fewer coding errors

Higher-order component — HOC

HOC:Higher-Order Component

Start with the idea of a higher-order function, pass a component as a parameter, and return a new component with enhanced functionality. We can use the flexibility of JS features to play out a lot of tricks

An 🌰

For example, in one scenario, there are 20 components that need a common function: print the point in time for mounting and unmounting. Instead of using HOC, we need to repeatedly write the hook function for componentDidMount and componentWillUnmount for each of the 20 components

HOC implementation scheme

Using HOC, we simply pull out the public method that prints the log and return a new component that includes logging, as follows:

import React, {Component} from 'react'

// In naming, functions that return HOC usually begin with with, indicating the enhanced component
function withLog (Comp) {
    return class extends Component {
        componentDidMount() {
            console.log(`${Comp.name} is Mounted, the time is The ${Date.now()}`)}componentWillUnmount() {
            console.log(`${Comp.name} will Unmount, the time is The ${Date.now()}`)}render() {
            return <Comp {. this.props} / >}}}export default withLog
Copy the code

In future use, we only need to call this function for each component that needs to print logs to meet the requirements of the log printing function. The example is as follows:

import React, {Component} from 'react'
import withLog from './HOC/withLog'

class AComp extends Component {
    // do somthing...
}

const ALogComp = withLog(AComp)

export default ALogComp
Copy the code

Change the way of thinking

Extend the functionality by implementing a new class that inherits from the React.ponent class

import React, {Component} from 'react'
class LogComponent extends Component {
    componentDidMount() {
        // But we don't know which component is mounted
        console.log(`Component is Mounted, the time is The ${Date.now()}`)}}export default LogComponent
Copy the code

The next 20 components only need to inherit this class to meet the requirement of printing logs… (Of course, this is just my personal thought of a way to cheat, not recommended)

HOC points of attention

  1. The return higher-order component is itself a function, so the parameters passed by the function can be handled by yourself, as well as the internal processing logic

  2. Do not use higher-order components in render functions or function components, otherwise they will be recreated each time, wasting performance and losing the previous state

    import React, {Component} from 'react'
    import Comp from './components/Comp'
    import withLog from './HOC/withLog'
    
    const LogComp = withLog(Comp) // Write here, ALogComp is reused
    
    class AComp extends Component {
        // do somthing...
        render() {
            // Write here, ALogComp will be destroyed and re-created each time
            // const LogComp = withLog(Comp)
            return <LogComp />}}export default AComp
    Copy the code
  3. Do not change incoming components within higher-order components, such as changing lifecycle methods on their prototype chain

    import React, {Component} from 'react'
    
    function withLog (Comp) {
        // Do not override properties and methods passed to components in this way
        // Comp.prototype.componentDidMount = function() {
        // // do something...
        // }
        return class extends Component {
            render() {
                return <Comp {. this.props} / >}}}export default withLog
    Copy the code
  4. Issues with REF passing (problems with encapsulating high order component REF references)

    Just look at the following, the end of this article

Ref (Reference)

In some cases, we may need to call a method on a DOM element, or we may want to use a method directly from a custom component

Scene 1: We click a button to focus the input field

  1. In native JS, we just need to do this:

    const inpDom = document.querySelector('input') // Assume that the page has only one input
    // Then call the focus method to implement the focus
    inpDom.focus()
    Copy the code
  2. React also gives us a way to manipulate the DOM directly, similar to Vue (this.$refs.xx)

    import React, {Component} from 'react'
    
    class Comp extends Component {
        handleClick = () = > {
            // this.refs.txt 就是 input
            this.refs.txt.focus()
        }
        render() {
            return (
                <div>{/* use the 'TXT' string directly. Use the React. CreateRef method to create */}<input type="text" ref="txt" />
                    <button onClick={this.handleClick}>Input field focus</button>
                </div>)}}export default Comp
    Copy the code

Scenario 2: The component Dog has a method called bark, and I need to call the method inside the Dog at the location that references the Dog component

import React, {Component} from 'react'

class Dog extends Component {
    bark() {
        console.log('Dog is barking... ')}render() {
        return <div> Dog Component </div>}}class Comp extends Component {
     handleClick = () = > {
        // this.refs.compDog is the instance object currently generated using the Dog component
        this.refs.compDog.bark() // Call the bark method on the prototype chain
    }
    render() {
        return (
            <div>
                <Dog ref="compDog" />
                <button onClick={this.handleClick}>Call the method of the child component</button>
            </div>)}}export default Comp
Copy the code

Ref use summary:

  1. Ref acts on the React built-in Html component (e.g.<h1> / <div>Etc.), and the result will be the real DOM object
  2. Ref acts on a class component, and the result will be an instance object of the class
  3. Ref (ref (ref (ref (ref (ref (ref (ref))) cannot apply to function components.

The ref attribute recommends passing an object or function

  1. object(byReact.createRef()Create or manually create{ current: null })
import React, {Component, createRef} from 'react'

class Comp extends Component {
    constructor (props) {
        super(props)
        this.txt = createRef() // Return an object stored in the current property
        // In this case, the current property is null
        // After the initial render, the current property is assigned to an instance of a DOM element or class
        /* { current: xxx } */
        
        // It is also possible to create an object manually, as long as it contains the current property
        // This structure is designed to improve efficiency by keeping the reference to this.txt unchanged
        /* this.txt = {current: null
    }
    handleClick = () = > {
        // There is no DOM object in this.refs
        this.txt.current.focus()
    }
    render() {
        return (
            <div>
                <input type="text" ref={this.txt} />
                <button onClick={this.handleClick}>Call the method of the child component</button>
            </div>)}}export default Comp
Copy the code
  1. function

As shown in the following example, we pass a function to ref that takes a DOM element or an instance of a class (object K) that follows the rules in the above summary, and the call time of the function:

  1. componentDidMount(The ref object cannot be used in previous lifecycle functions)
  2. ifrefThe old function is replaced by the new function. The old function and the new function are called respectively at the point in timecomponentDidUpdateBefore.The old function call passes null, the new function passes the object K
  3. ifrefThis function is also called when the component is unloaded. (The passed function uses a reference, as shown belowGetRef method)
import React, {Component} from 'react'

class Comp extends Component {
    handleClick = () = > {
        this.txt.focus() // The function is assigned directly to the this.txt property
    }
    /* This function reference remains unchanged and is executed only during the mount and unload phases
    getRef = el= > {
        console.log('getRef is called ', el)
        this.stableRef = el
    }
    render() {
        return (
            <div>{/* this is where the arrow function is written, each time it is a new function */}<input type="text" ref={el= >{console.log(' arrow function ref reference called ', el) this.txt = el // Save ref reference}} /><input type="text" ref={this.getRef} />
                <button onClick={this.handleClick}>Call the method of the child component</button>
            </div>)}}export default Comp
Copy the code

Forward (refforwardRef)

To solve the problem, here’s an example of an App component that wants to retrieve the object K inside the function component Comp (such as the div element in the example) :

import React, {Component} from 'react'

function Comp (props) {
    return <div>Component Comp</div>
}

class App extends Component {
    render() {
        return (
            <div>{/* function components are not allowed to use ref, give the ref property, the console will report a warning */}<Comp />
            </div>)}}export default App
Copy the code

Therefore, we need to create a new component using the React. ForwordRef function to help us forward the ref, which will then be passed to the function component as its second argument

import React, {Component, createRef, forwardRef} from 'react'

function Comp (props, ref) {
    return <div ref={ref}>Component Comp</div>
}

// Return a new component passed as the second argument to the function component Comp
const RefComp = forwardRef(Comp)
// The ref attribute received by the new component is only passed to the component, not referred to itself

class App extends Component {
    compRef = createRef() // This.pref.current stores the div DOM element
    render() {
        return (
            <div>
                <RefComp ref={this.compRef} />
            </div>)}}export default App
Copy the code

Note:

  1. The argument to the React. ForwardRef function must be a function component, not a class component. And the function component must take the second argument to receive the REF, otherwise a warning will be sent

  2. If a class component is expected to refer directly to an element in the class component Comp in the App component, it can be passed with a simple attribute

    import React, {Component, createRef} from 'react'
    
    class Comp extends Component {
        render() {
            return <div ref={this.props.reference}>Component Comp</div>}}class App extends Component {
        compRef = createRef() // This.pref.current stores the div DOM element
        render() {
            return (
                <div>{/* customize a simple property pass ref object */}<Comp reference={this.compRef} />
                </div>)}}export default App
    Copy the code
  3. In the case of a higher-order component, since the return is directly a new class component, the ref object points to the HOC wrapper when we use the REF attribute on it, which is obviously not what we expect. So you need to encapsulate the ref forwarding processing in the HOC (take the previous withLog example)

    // withLog.jsx [HOC]
    import React, {Component, createRef, forwardRef} from 'react'
    
    export default function withLog(Comp) {
        class LogWrapper extends Component {
            componentDidMount() {
                console.log(`${Comp.name} is Mounted, the time is The ${Date.now()}`)}ForwardRef = ref */
            render() {
                const{forwardRef, ... rest} =this.props
                return <Comp ref={forwardRef} {. rest} / >}}return forwardRef((props, ref) = > {
            return <LogWrapper {. props} forwardRef={ref} />})}// This way, we don't need to worry about ref references when we use the high order withLog component
    // Attribute names that are passed from HOC to REF should be written in a special way to avoid duplication
    
    // main.jsx
    import React, {Component, createRef} from 'react'
    import ReactDOM from 'react-dom'
    import CompA from './components/CompA'
    import withLog from './hoc/withLog'
    
    const LogCompA = withLog(CompA)
    class App extends Component {
        compRef = createRef()
        render() {
            return <LogCompA ref={this.compRef} />}}export default App
    Copy the code

These are the React default properties, high order component use and packaging points to note, ref use and ref forward need to pay attention to related instructions