React [Context_function, function component subscription Context, Fragments, error boundary_concept, error boundary_application, Refs & DOM] (4)

Table of Contents

Context_role

Function component subscribes to Context

Fragments

Error Boundary_Concept

Error Boundary_Application

Refs & DOM


Context_role

Data in React components is passed from top to bottom (from parent to child) through the props attribute, but sometimes some components in the middle may not need the value of props.

//App.js
return (
    <div>
      <ContextTest />
    </div>
 );
//ContextTest.js
import React from 'react'
import NavBar from './NavBar'
export default class ContextTest extends
React.Component{
    constructor(props){
        super(props)
        this.state={user:{name:'小子',account:'baizhan'}}
   }
    render(){
        return <div>
            <NavBar user={this.state.user}/>
        </div>
   }
}
//NavBar.js
import React, { Component } from 'react'
import UserInfo from './UserInfo'
export default class NabBar extends
Component {
  render() {
    return (
      <div style= {<!-- -->{height:'50px',background:'#ccc',display:'flex',justifyContent:'space-around',alignItems: 'center'}}>
       Contents of NabBar
        <UserInfo user={this.props.user}/>
        </div>
   )
 }
}

//UserInfo.js
import React, { Component } from 'react'
export default class UserInfo extends
Component {
  render() {
    return (
      <div>Hello and welcome {this.props.user.account}</div>
   )
 }
}

Context provides a way to pass data between component trees without manually adding props for each layer of components.

When to use Context

If we want to share some data in the component tree and avoid passing props through intermediate elements, we can use Context to achieve this.

Context_Application

1. Create Context object

React.createContext(defaultValue)

const MyContext = React.createContext(defaultValue);

2. Provider component of Context

The Provider component of Contentxt is used to provide data to be shared by other components.
Set the value attribute to set the data to be shared.

<MyContext.Provider value={/* a certain value */}>

3. Provider component of Context

The Provider component of Contentxt is used to provide data to be shared by other components.
Set the value attribute to set the data to be shared.

<MyContext.Provider value={/* a certain value */}>

Tip: We call components that use shared data consuming components.

Class.contextType

Add the contextType attribute to the component class to obtain the Context object.

MyClass.contextType = MyContext;

hint:
The contextType attribute mounted on class will be reassigned to a
The Context object created by React.createContext() . This allows you to use this.context to consume the value from the most recent Context.
You can access it in any life cycle, including the render function.

//MyContext.js
import React from "react";


export default
React.createContext({name:'',account:'xxx'})

//ContextTest.js
import React from 'react'
import MyContext from './MyContext'
import NavBar from './NavBar'
export default class ContextTest extends
React.Component {
    constructor(props) {
        super(props)
        this.state = { user: { name: 'xiaotong',account: 'xiaotong' } }
   }
    
    render() {
        return <div>
             {/* Use Provider component to provide data */}
            <MyContext.Provider value= {this.state.user}>
                <NavBar />
            </MyContext.Provider>
        </div>
   }
}
//NabBar.js
import React, { Component } from 'react'
import UserInfo from './UserInfo'
export default class NabBar extends
Component {
  render() {
    return (
      <div style= {<!-- -->{height:'50px',background:'#ccc',display:'flex',justifyContent:'space-around',alignItems: 'center'}}>
        Contents of NabBar
        <UserInfo />
        
        </div>
   )
 }
}
//UserInfo.js
import React, { Component } from 'react'
import MyContext from './MyContext'

export default class UserInfo extends
Component {
  render() {
    console.log(this.context)
    return (
      <div>Hello and welcome {this.context.account}</div>
   )
 }
}
//Set the contentType attribute of the class to obtain the context object
UserInfo.contextType=MyContext

Function component subscribes to Context

Context.Consumer

<MyContext.Consumer>
 {value => /* Render based on context value*/}
</MyContext.Consumer>
//ContextTest.js
render() {
        return <div>
           {/* Use Provider component to provide data */}
            <MyContext.Provider value= {this.state.user}>
                <NavBar />
                <MyContext.Consumer>
                   {value=><div>
                        <h3>Function component</h3>
                       Obtained account: {value.account}
                        </div>}
                    
                </MyContext.Consumer>
              
            </MyContext.Provider>
            
        </div>
   }

Context update
When the Provider’s value changes, all consuming components inside it will be re-rendered.

//ContextTest.js
render() {
        return <div>
            <button onClick= {()=>this.setState({user:{name:"xiaotong",account:'xiaotong123'}})}>Update user</button>
           {/* Use Provider component to provide data */}
            <MyContext.Provider value={this.state.user}>
                <NavBar />
                <MyContext.Consumer>
                   {value=><div>
                        <h3>Function component</h3>
                       Obtained account: {value.account}
                        </div>}
                    
                </MyContext.Consumer>
              
            </MyContext.Provider>
            
        </div>
   }

Tip: Whether the consuming component is re-rendered is not controlled by shouldComponentUpdate.
Even if its parent component uses shouldComponentUpdate to stop rendering or the consuming component itself uses shouldComponentUpdate to stop rendering, it will not affect the continued updating of consuming components.

//NavBar.js
export default class NabBar extends
Component {
  shouldComponentUpdate(){
      //The current non-consuming component will stop updating and rendering, but its sub-component UserInfo is the consuming component of Context and will continue to be updated.
    return false
 }
  render() {
    console.log('navbar')
    return (
      <div style= {<!-- -->{height:'50px',background:'#ccc',display:'flex',justifyContent:'space-around',alignItems: 'center'}}>
       Contents of NabBar
        <UserInfo />
        
        </div>
   )
 }
}

//UserInfo.js
import React, { Component } from 'react'
import MyContext from './MyContext'
export default class UserInfo extends
Component {
    shouldComponentUpdate() {
        //Although false is returned, the value of context has changed, so I still have to re-render it.
        return false
   }
    render() {
        return (
            <div>Hello, welcome {this.context.account}</div>
       )
   }
}
UserInfo.contextType = MyContext

Fragments

Scene 1:

render() {
    return (
      <p></p>
      <p></p>
   )
 }

Tip: In react, the component interface can only have one root tag.

Scene 2:

render() {
    return (
        //The outermost div may not be needed at all, and we don't want it to be rendered on the page.
        <div>
             <p></p><p></p>
        </div>
    
   )
 }

Fragments allow you to wrap child elements together without adding extra nodes to the DOM.

render() {
    return (
   <React.Fragment>
      <p></p>
      <p></p>
   </React.Fragment>
   )
 }

Final page rendering result:

1. Fragments short syntax

Use <> instead of

render() {
    return (
   <>
      <p></p>
      <p></p>
   </>
   )
 }

hint:
Because Fragments will not be rendered into DOM in the end, do not bind events or set some other attributes on Framents. Currently, only setting the key attribute is supported.

Error Boundary_Concept

Usually an error in one component will cause the entire application to crash and the page to go blank.

//App.js
render() {
    return (
      <>
        <ContextTest />
        <SomethingWrong />
      </>
   )
 }

//SomethingWrong.js
export default class SomethingWrong extends
Component {
  constructor(){
     // If super is not called, an error will be thrown
 }
  render() {
    return (
      <div>
        <Child/>
      </div>
   )
 }
}

If we want components that do not have errors to continue rendering, we can use error boundaries.
ErrorBounds is a React component.
Error Bounds can catch and print JavaScript errors that occur anywhere in its child component tree, and it can render out an alternate UI.
Error Bounds Catch errors during rendering, in lifecycle methods, and in constructors throughout the subcomponent tree.

Tip: Error boundaries cannot capture errors generated in the following scenarios:
1. Event handling
2. Asynchronous code (such as setTimeout or requestAnimationFrame callback function)

3. Errors thrown by itself (not its subcomponents)

Error Boundary_Application

1. Define a component with error boundaries
When either (or both) of the two life cycle methods static getDerivedStateFromError() or componentDidCatch() is defined in a class component, the component becomes A component with error boundaries.

import React, { Component } from 'react'
export default class ErrorBoundary extends
Component {
    constructor() {
        super()
        this.state = { }
   }
    componentDidCatch(error, errorInfo) {
        // Called when an error occurs in the subcomponent tree is captured
        this.setState({
          error: error,
          errorInfo: errorInfo
       })
        
     }
    render() {
        return (
           this.state.errorInfo ? <div>Oh, crap! ! Some errors occurred:
               The error message is as follows:<br />
               {this.state.error & amp; & amp; this.state.error.toString()}
            <br />
           {this.state.errorInfo.componentStack}
              
            </div> : this.props.children
       )
   }
}

Use of error boundaries

//App.js
render() {
    return (
      <>
        <ContextTest />
         {/* Use the component tree wrapped by the error boundary component, and any errors that occur will be captured by this error boundary */}
        <ErrorBoundary>
          <SomethingWrong />
        </ErrorBoundary>
      </>
   )
 }

Errors that cannot be caught by error boundaries

Errors that cannot be captured by error boundaries, such as event processing and asynchronous operations. You can use some methods supported by native js to capture.

try/catch

onClick=()=>{
    try{
        //Using an undefined variable a will report an error
       console.log(a)
   }catch(e){
        console.log(e)
        this.setState({hasError:true})
   }
    
 }
  render() {
    return (
      <div>
        <button onClick={this.onClick}>Click</button>
       {this.state.hasError?<div>An error occurred</div>:null}
      </div>
   )
 }

window.onerror

window.onerror can capture syntax errors and runtime errors. As long as there is an error in the JS script executed in the current window, it will be captured.

window.onerror=function(err){
  console.log(err)
}

Refs & amp; DOM

Refs provide a way to access DOM elements.

<div ref={ref}></div>

Create Refs
Created using React.createRef().
Refs are usually assigned to instance properties so that they can be referenced throughout the component.

constructor(props) {
    super(props);
    this.myRef = React.createRef();
 }

Use Refs
Setting the ref attribute to the corresponding React element is equivalent to using ref to store references to DOM nodes.

render() {
    return <input ref={this.myRef} />;
 }

VisitRefs
When ref is passed to an element in render, a reference to that node can be found in ref ‘s current attribute was visited.

componentDidMount(){
    const node = this.myRef.current;
    node.focus()
 }

hint:
React will pass a DOM element to the current property when the component is mounted, and pass a null value when the component is unmounted.

ref will be called in componentDidMount or componentDidUpdate
Update before life cycle hook is triggered.