“This is the 26th day of my participation in the First Challenge 2022. For details: First Challenge 2022.”

1. React

The core idea of React is data-driven views, which change as data changes.

UI = render(data)

// or 

UI = f(data)
Copy the code

2. One-way data flow

One-way data flow, in which the state of the current component flows in the form of props, can only flow to components lower in the component tree than itself. For example, in a parent-child nested relationship, only the parent component can pass props to the child component, not vice versa.

React Data flow management solution:

  • Using props based one-way data flow to series parent and sibling components;
  • Use third-party data stream Redux
  • Use the Context API to maintain global state
  • Use a publish-subscribe model to drive React data to flow between arbitrary components.

3. Communication mode of components

4. Several modes of props

4.1 Props Chidren Mode

<Container>
    <Children>
</Container>
  
  
function Container(props){
 	return props.children
}  
Copy the code

Function:

  • You can control whether or not Chidren renders as needed.
  • Container can enhance props with React. CloneElement (mixed with new props), or modify child elements of Chidren.

4.2 Render Props mode

<Container>
   { (ContainerProps) = > <Children {. ContainerProps} / > }
</Container>


function Container(props) {
   const ContainerProps = {
      name: 'xiaoming'.mes:'hello'
   }
   return props.children(ContainerProps)
}
Copy the code

Function:

  • Control whether Chidren renders or not as needed.
  • You can pass props that need to be passed to Children directly to the performing function Children as function arguments.

4.3 Mixed Mode

<Container>
    <Children />
    { (ContainerProps) = > <Children {. ContainerProps} name={'haha'} / >  }
</Container>
Copy the code

In this case, we need to iterate over children and determine the children element type:

  • For the Element node, mix props with cloneElement;
  • For a function, pass arguments directly and execute the function.
const Children = (props) = > (<div>
    <div>hello, my name is {  props.name } </div>
    <div> { props.mes } </div>
</div>)

function  Container(props) {
    const ContainerProps = {
        name: 'xiaoming'.mes:'hello'
    }
     return props.children.map(item= >{
        if(React.isValidElement(item)){ // React elment mixed with props
            returnReact.cloneElement(item,{ ... ContainerProps },item.props.children) }else if(typeof item === 'function') {return item(ContainerProps)
        }else return null})}const Index = () = >{
    return <Container>
        <Children />
        { (ContainerProps)=> <Children {. ContainerProps} name={'haha'} / >  }
    </Container>
}
Copy the code

5, Redux

Throughout the work of Redux, the data flow is strictly one-way.

If you want to make changes to your data, there’s only one way to do it: send out actions. Actions will be read by Reducer, and the data will be modified according to the action content and a new state (state) will be generated. This new state will be updated into the store object and drive the corresponding changes at the view level.

For components, any component can read the global state from the Store by convention, and any component can modify the global state by reasonably distributing actions. Redux allows data to move freely and orderly between arbitrary components by providing a unified state container.

  1. Use createStore to create the Store object
/ / into the story
import { createStore } from 'redux'
/ / create a store
const store = createStore(
    reducer,
    initial_state,
    applyMiddleware(middleware1, middleware2, ...)
);
Copy the code
  1. The job of the Reducer is to return the new state to the Store
const reducer = (state, action) = > {
    // Here are the various types of state processing logic
    return new_state
}
Copy the code
  1. Action notifyreducer “Make changes happen”
const action = {
  type: "ADD_ITEM".payload: '<li>text</li>'
}
Copy the code
  1. Dispatch action by dispatch
import { createStore } from 'redux'
/ / create a reducer
const reducer = (state, action) = > {
    // Here are the various types of state processing logic
    return new_state
}
Create state based on reducer
const store = createStore(reducer)
// Create an action identified by "ADD_ITEM"
const action = {
  type: "ADD_ITEM".payload: '<li>text</li>'
}

/ / subscribe
store.subscribe(() = > console.log(store.getState()))

// Assign actions using Dispatch. The actions are sent to the reducer to trigger corresponding updates
store.dispatch(action)
Copy the code

6. Context

Context provides a way to pass data across the component tree without manually adding props for each layer of components.

Basic usage:

const ThemeContext = React.createContext("light") //
const ThemeProvider = ThemeContext.Provider  / / provider
const ThemeConsumer = ThemeContext.Consumer // Subscription consumers
Copy the code

The Provider:

const ThemeProvider = ThemeContext.Provider  / / provider
import ConsumerComponent form './ConsumerComponent'


function ProviderComponent(){
    const [ theme , setTheme ] = React.useState({ theme: "light" })
    return <div>
        <ThemeProvider value={ theme } > 
           <ConsumerComponent />
        </ThemeProvider>
    </div>
}

export default ProviderComponent
Copy the code

Providers have two functions:

  • The value property passes the context, which the Consumer can use.
  • The value property changes, and ThemeProvider rerenders components that consume the Provider value.

Consumer:

Types of components:

// Class component - contextType mode
class ConsumerComponent extends React.Component{
   render(){
       const { theme } = this.context
       return <div style={{ color: theme } }>consumers</div> 
   }
}

ConsumerComponent.contextType = ThemeContext

export default ConsumerComponent
Copy the code

Function components:

(1) useContext:


export default function ConsumerComponent(){
    const  contextValue = React.useContext(ThemeContext)
    const { theme } = contextValue
    return <div style={{ color: theme}} >consumers</div> 
}
Copy the code

(2) Subscribers:

const ThemeConsumer = ThemeContext.Consumer // Subscription consumers

export default const ConsumerComponent = () = > {
  return (
    <ThemeConsumer>
       { (contextValue)=> // todo }
    </ThemeConsumer>)}Copy the code

Publish and subscribe

Event listening (subscription) and event triggering (publication)

  • On () : The listener responsible for registering the event, specifying the callback function when the event is fired.

  • Emit () : emits the event, which can be sent as a parameter to emit data.

6.1 mapping

The mapping between events and listener functions, mapping, we’re dealing with mapping most of the time with objects. So globally we need to set an object that stores the relationship between events and listener functions:

 constructor() {
   // eventMap is used to store the relationship between events and listener functions
   this.eventMap = {};
 }
Copy the code

6.2 the subscription

Write the event and corresponding listener to the eventMap:

// type represents the name of the event
on(type, handler) {
  // Hanlder must be a function, if not a direct error
  if(! (handlerinstanceof Function)) {
    throw new Error("I need to pass a function.")}// Check whether the queue corresponding to the type event exists
  if(!this.eventMap[type]) {
   // If no queue exists, create a new queue
    this.eventMap[type] = []
  }
  // If so, push handler directly to queue
  this.eventMap[type].push(handler)
}
Copy the code

6.3 release

The publish operation is a “read” operation.

// Don't forget that the trigger can carry data, params is the data carrier
emit(type, params) {
  // Assume that the event is subscribed (the corresponding event queue exists)
  if(this.eventMap[type]) {
    // Unqueue the handlers in the event queue
    this.eventMap[type].forEach((handler, index) = > {
      // Don't forget to read params
      handler(params)
    })
  }
}
Copy the code

6.4 shut down

Closing is an out-of-queue operation.

off(type, handler) {
  if(this.eventMap[type]) {
    this.eventMap[type].splice(this.eventMap[type].indexOf(handler)>>>0.1)}}Copy the code

6.5 test

The complete code

class myEventEmitter {
    constructor() {
        this.eventMap = {};
    }

    on(type, handler) {
        if(! handlerinstanceof Function) {
            throw new Error("Please pass a function");
        }
        if (!this.eventMap[type]) {
            this.eventMap[type] = []
        }
        this.eventMap[type].push(handler)
    }

    emit(type, params) {
        if (this.eventMap[type]) {
            this.eventMap[type].forEach((handler) = >{ handler(params); }}})off(type, handler) {
        if (this.eventMap[type]) {
            // bit arithmetic on negative numbers returns an infinite number, otherwise returns itself
            this.eventMap[type].splice(this.eventMap[type].indexOf(handler) >>> 0.1); }}}const myEvent = new myEventEmitter();
// Write a simple handler
const testHandler = function (params) {
    console.log(The 'test' event was raised, and the testHandler received an input parameter${params}`);
};
// Listen for the test event
myEvent.on("test", testHandler);
// When the test event is triggered, pass in the argument that you want testHandler to feel
myEvent.emit("test"."123");

// myEvent.off("test", testHandler);

console.log(`object`, myEvent.eventMap)
Copy the code

6.6 Application on the React

// index.jsx
import React, { Component } from 'react'
import A from './A'
import B from './B'
import event from './event.js'

class index extends Component {
    render() {
        React.$myEvent = new event()
        return (
            <div>
                <A></A>
                <B></B>
            </div>)}}export default index
Copy the code
// event.js
class myEventEmitter {
    constructor() {
        this.eventMap = {};
    }

    on(type, handler) {
        if(! handlerinstanceof Function) {
            throw new Error("Please pass a function");
        }
        if (!this.eventMap[type]) {
            this.eventMap[type] = []
        }
        this.eventMap[type].push(handler)
    }

    emit(type, params) {
        if (this.eventMap[type]) {
            this.eventMap[type].forEach((handler) = >{ handler(params); }}})off(type, handler) {
        if (this.eventMap[type]) {
            this.eventMap[type].splice(this.eventMap[type].indexOf(handler) >>> 0.1); }}}export default myEventEmitter
Copy the code
// A
import React from "react";

class A extends React.Component {
  state = {
    newParams: ""}; handler =(params) = > {
    this.setState({
      newParams: params,
    });
  };
  bindHandler = () = > {
    React.$myEvent.on("someEvent".this.handler);
  };
  render() {
    return (
      <div>
        <button onClick={this.bindHandler}>Click on me to listen to "A"</button>
        <div>[{this.state.newParams}]</div>
      </div>); }}export default A;
Copy the code
// B
import React from "react";

class B extends React.Component {
  state = {
    infoToB: "Ha ha ha HA I'm from A"}; reportToB =() = > {
    React.$myEvent.emit("someEvent".this.state.infoToB);
  };
  render() {
    return <button onClick={this.reportToB}>Dot I pass state to B</button>; }}export default B;
Copy the code