Write in front:

1. This article is a summary and review of the learning content of personal class, with the main knowledge points coming from the live online course of “Open Class bar” and React official documents.

2. At present, React is still in the beginning stage and even the front-end stage, so I don’t have a thorough understanding of many contents. The article is more for learning and recording than sharing.

Therefore, if you need to solve project problems, it is recommended to look at the documents of other big players and the React official documentation.

Intercomponent communication

Without third-party libraries, native React supports only one-way flow: a parent can send state and callback functions to its children as props, while children cannot send events to their parents or modify their props. So if the child wants to change the state (that is, the data) of the parent component, it can only do so through the callback function passed down from the parent.

Communication between adjacent hierarchical components (sibling/parent)

Using the example we wrote at the end of the last note, if we want to close one column while expanding the other, but we are directly instantiating three Dl components in the App component, the three components cannot communicate with each other, and only declare a state in their parent (i.e., App), Pass to each instance with props, and declare a callback function that changes the state, passed to all three instances. This allows the App component to change the state when each Dl instance fires the callback, enabling communication between sibling components

//App component // declares a state that indicates which Dl window is open. And declare a callback function that controls the state. state = { openname: } changeOpen = (name) => {this.setState({openName: openName, openName, openName, openName, openName, openName, openName, openName) name }) } render() { const {openname} = this.state; return <div className="friend-list"> {Object.keys(data).map((item,index)=>{ return <Dl key={index} data={data[item]} Name ={item} openName ={openName} // Pass this state to all instantiated Dl components changeOpen ={this.changeOpen} // Pass openName callback to all Dl components /> })} </div> }Copy the code
Render () {let {data, name, openname, changeOpen} = this.props; // Get props for openName and changeOpen return <dl className={' friend-group ${openName === name? "expanded" : ""}`}> <! <dt onClick={() => {changeOpen(openName === name? ":name); // With the changeOpen callback, Modify openname state}} > {data. The title} < / dt > {data. The list. The map ((item, index) = > {return < dd key = {index} > < / dd > {item. The name}})} < / dl >}Copy the code

Cross-level component communication (Ye Sun)

Suppose there are three components:

  • AppComponent: Is the root component
  • ChildComponent: Is a child of an App component
  • SubChildComponent: is a Child of the Child component

If the SubChild component receives information from the App component, it must pass a Child in the middle. If the method is clumsy, it can pass the Child layer by layer to the props=> structural props=> and pass it to the next layer to continue the structural props. Obviously this works, but it’s cumbersome, so native React provides a way to communicate across components (somewhat like VUex) — context

The createContext() method is used to declare a context instance, which is called contextStore for convenience. The contextStore instance provides two methods, Provider and Consumer. The names indicate the offer and receive states, respectively.

import {createContext} from "react";
const contextStore = createContext();
const {Provider,Consumer} = context;
export default contextStore;
export {Provider,Consumer};
Copy the code

In the App component that provides the state, place the state that needs to be passed across by the context method inside the props of the tag, which wraps the component that needs to be passed.

render() {
    const {count,name} = this.state;
    return <>
      <Provider
        value={{
          count,
          name,
          setCount:this.setCount
        }}
      >
        <Child />
      </Provider>  
    </>
  }
Copy the code

No processing is required in the Child component, and the SubChild component can fetch the data directly. Of course, if the Child component wants to use this data, it can just use this.props. There are two ways to fetch data, using the

tag and contextType static property declaration.

With

, the tag wraps around the virtual DOM structure that needs to be returned, and the data needed is deconstructed from the context.

  render() {
    return <Consumer>
      {(context) => {
        const { count, name } = contextStore;
        return <div>
          <p>count:{count}</p>
          <p>name:{name}</p>
        </div>
      }}
    </Consumer>
  }
Copy the code

Alternatively, declare the static contextType property of the Component class to tell the Component where to get the data

class SubChild extends Component { static contextType = contextStore; // contextType, which tells the current component that the context value of the current SubChild component is retrieved from the contextStore instance exported from the context.hs file. Let this.context = contextStore render() { const { count, name, setCount } = this.context; // This. Context is the class component's own context, Return <div> <p>count:{count}</p> <p>name:{name}</p> <button onClick={()=>{setCount(count +1);  </button> </div>}}Copy the code

PS: It is not recommended to use context directly for cross-component communication. Context is typically used by third-party libraries to handle cross-level communication issues.

The life cycle of a class component

Mount the stage

Component creation => Add virtual DOM => generated real DOM => to our DOM tree

  • Constructor: Component initialization

  • Static getDerivedStateFromProps(props) : Associate the props passed in with the state. Note that this function must return a value

  • ComponentDidMount: Generates real DOM nodes, and usually handles side effects (requests) at this stage

  • Render: The component is mounted, and the virtual DOM has generated the real DOM

class Child extends Component { constructor(props){ super(props); Console. log(0," component initialization "); } // associate static getDerivedStateFromProps(props){//console.log(this,props); This console.log(1," associate props to state ") is not present in this method; Return {// The lifecycle function must have a return value, which holds the data to be associated with the state from props info: "Data to be associated"}} state={count: 1} componentDidMount(){console.log(3," componentDidMount, virtual DOM, generated real DOM"); } render() {console.log(2," generate virtual DOM",this.state); const {count} = this.state; Return the < div > < p > {count} < / p > < button onClick = {() = > {enclosing setState ({count: count + 1})}} > increasing < / button > < / div >}}Copy the code

Update the stage

Component state update => The component is re-rendered, and the component is updated from setState until the actual DOM node is updated

Static getDerivedStateFromProps(props, state) : This is a static method to get the latest state from the props

ShouldComponentUpdate () : Determine if and where components need to be updated

Render () : re-render

GetSnapshotBeforeUpdate () : a new virtual DOM node has been built but has not been updated to the real DOM, which is used to get a snapshot of the DOM before the update

ComponentDidUpdate () : Component updates are complete

class Child extends Component {
  // Associate props to state
  static getDerivedStateFromProps(props){
      //console.log(this,props); There is no this in this method
      console.log(1."Associate props to state");
      
      return { // The lifecycle function must have a return value that holds data from the props to be associated with the state
        info: "Data to associate"
      }
  } 
  state={
    count: 1
  }
  shouldComponentUpdate(nextProps,nextState){
      //nextProps Updated props, nextState Updated state
      //console.log(nextState,this.state);
      console.log(2."Determine if the component needs to be updated");
      return true; // If the value is true, the update continues, if the value is false, the update is not performed, and subsequent lifecycle functions are not executed
  }
  getSnapshotBeforeUpdate(){
    // The new virtual DOM has been built, but the real DOM has not been updated
    // Take a snapshot of the DOM before the update
    console.log(4."The new virtual DOM has been built, but the real DOM has not been updated.");
    let box = document.querySelector("#box");
    return box.innerHTML;// Change the return value to the prevDOM parameter for componentDidUpdate
  }
  componentDidUpdate(prevProps,prevState,prevDOM){
    // console.log(prevDOM);
    console.log(5."Component update completed");
  }
  render() {
    console.log(3."Generate virtual DOM".this.state);
    const {count} = this.state;
    return <div id="box">
        <p>{count}</p>
        <button onClick={()= >{this.setState({count: count + 1})}}> incremented</button>
    </div>}}Copy the code

Unloading phase

ComponentWillUnmount () : The component is about to be destroyed, you can clear various listening events at this stage.

class Child extends Component {
  state={
    count: 1
  }
  componentDidMount(){
    let getSize = () = >{
      let size = document.querySelector("#size");
      size.innerHTML = window.innerWidth;
    }
    getSize();
    window.onresize = getSize;
  }
  componentWillUnmount(){
    window.onresize = null;
  }
  render() {
    const {count} = this.state;
    return <div id="box">
        <p>{count}</p>
        <button onClick={()= >{this.setState({count: count + 1})}}> incremented</button>
        <div id="size">0</div>
    </div>}}Copy the code

The controlled components

Vue provided me with a syntax sugar V-model to achieve bidirectional binding between forms and data. However, React was lazy and focused on rendering views, so it didn’t provide such syntax sugar. We needed to declare it manually through state and props. The scenarios in which this controlled component is used are similar: forms that need to be entered or checkboxes that need to be checked.

  • Value: Assigns the state of the component to the value property of the control, listens for changes in the value property of the control through the onChange event, and assigns the value to the state of the component through the callback function

  • Checked: Assigns the state of the component to the checked property of the control, listens for changes in the checked property of the control through the onChange event, and assigns the state of the component through the callback function

class App extends Component { state={ val:"" } componentDidMount(){ let text1 = document.querySelector("#text1"); text1.oninput = function(){ console.log(this.value); } } render() { const {val} = this.state; return <div> <input type="text" value={val} onChange={({target})=>{ this.setState({ val: Target. Value})}} /> <input type="text" id="text1" /> <input type="text" id="text2" defaultValue=" initial value "/> <p>{val}</p> < button onClick = {() = > {enclosing setState ({val: ""})}} > reset < / button > < / div >}}Copy the code

Uncontrolled component

  1. Need to set the initial value, setting the initial value only, do not want this control is controlled, do not use the value | checked, as long as the use value | checked the react would think we do controlled components. So I’m going to use defaultValue and defaultChecked

    <input
    type="text" 
    defaultValue={val}  
    onChange={({target})= >{
       this.setState({
         val: target.value
       })
    }}
    />
    Copy the code
  2. The initial value is left blank.

The instance

// App.js import { Component } from "react"; import "./index.css"; import AddMeaasge from "./AddMeaasge"; import MessageList from "./MessageList"; Class App extends Component{state = {data:[{id:0, name:" chenghao ", MSG :" chenghao "}]}; addMeaasge = (newName, newMsg) => { const {data} = this.state; data.push({ id:Date.now(), name:newName, msg:newMsg }); this.setState({ data }) }; remove = (id) => { const {data} = this.state; this.setState({ data:data.filter(id => {return data.id = ! Render (){let {data} = this.state return <section className="wrap"> <h2 className="title"> <AddMeaasge addMeaasge = {this.addMeaasge} /> <MessageList data = { data } remove = { this.remove } /> </section> }; } export default App;Copy the code
// MessageList.js import { Component } from "react"; class MessageList extends Component{ render(){ const { data,remove } = this.props return <ul className="messageList"> {data.map((item,index) => { return <li key={index}> <h3>{item.name}</h3> <p>{item.msg}</p> <a onClick={()=>{ const id =item.id remove(id)}}> delete </a> </li>}} </ul>}; }; export default MessageListCopy the code
//AddMeaasge.js import { Component } from "react"; class AddMeaasge extends Component{ state={ newName:"", newMsg:"" } render(){ const {newName, newMsg} = this.state const { addMeaasge } = this.props return <div className="addMessage"> <input type="text" Placeholder =" autoComplete="off" value={newName} onChange={({target})=>{this.setState({newName:target.value}) Value ={newMsg} onChange={({target})=>{this.setState({newMsg:target.value})}} ></textarea> <button onClick={() => { addMeaasge(newName,newMsg); Enclosing setState ({newMsg newName: "" :" "})}} > submit comments < / button > < / div >}}; export default AddMeaasgeCopy the code
// index.css/ / a littleCopy the code

End result: