Socket read

React is a componentized framework. Based on the location distribution of components in the component tree, the relationships between components can be roughly divided into four types.

  • Parent and child: The parent wraps around the child, and the parent passes data to the child.
  • Child and parent: The child component exists in the parent component, and the child needs to pass data to the parent
  • Sibling: Two components exist side by side in a parent component and require data to pass to each other.
  • No direct relationship: Two components are not directly related and are far apart in the tree, but need to share and transfer data.

Based on the above classification, there is the following knowledge map:

Starting with

Father and son

Communication between father and child is a common scenario, which is often used in work

Props


const Son = ({ msg }) => {

   return (<span>{msg}</span>)

}



class Father extends React.Component {

    constructor(props) {

        super(props);

        this.state = {

            msg: 'Parent component message'

        }

    }



    render() {

        return (

            <div>

                <Son msg={this.state.msg}/>

            </div>

        )

    }

}



Copy the code

This example code shows that the parent passes the value to the child, and the child component (Son) displays the “parent message”, which is what we want.

Child the parent

The callback function


class Son extends React.Component {

    handleSend = () => {

        this.props.handleSend('i am you son')

    }

    render() {

        return (

            <div>

                <button onClick={this.handleSend}>send msg to my father</button>

            </div>

        )

    }

}



class Father extends React.Component {

    constructor(props) {

        super(props);

        this.state = {

            msg: ' '

        }

    }

    handeReceive = (msg) => {

        this.setState({

            msg

        })

    }

    render() {

        return (

            <div>

                {this.state.msg}

                <Son handleSend={this.handeReceive}/>

            </div>

        )

    }

}

Copy the code

This example code shows that the child passes the value to the parent, and the Father component will say “I am you son”, which achieves the desired effect. If you know about the Vue framework, you will find that the first two parent-child component communication methods are similar to Vue. After you touch one framework, you can look at the other framework and see if there are many similarities.

brother

State of ascension








const ChildB = ({ msg }) => {

   return (<span>{msg}</span>)

}



class ChildA extends React.Component {

    handleClick = () =>{

        this.props.handleSend('I'm coming to visit you tonight.')

    }

    render() {

        return (

            <div>

                <button onClick={this.handleClick}>click Me</button>

            </div>

        )

    }

}



class Father extends React.Component {

    constructor(props) {

        super(props);

        this.state = {

            msg: ' '

        }

    }

    

    handleSend = (msg) => {

        this.setState({

            msg

        })

    }

    

    render() {

        return (

            <div>

                <ChildB msg={this.state.msg}/>

                <ChildA handleSend={this.handleSend}/>

            </div>

        )

    }

}

Copy the code

This example code demonstrates sibling transfer via state promotion. Run the discovery child (ChildB) and say “I’ll come and play with you tonight”. Think again, if you find that you can achieve a two-way data binding effect similar to the Vue framework through state promotion.

No direct relation

Non-direct relationship means that the direct correlation between two components is not large. They are in a multi-level nested relationship, neither parent-child relationship, nor adjacent relationship, and relatively distant, but they still need to share data to complete communication. So how do we do that? Let’s start with the Context communication scheme provided by React.

Context
import { createContext } from 'react';



const I18nContext = createContext({

    translate: () => ' '.

    getLocale: () => { },

    setLocale: () => { },

});



export default I18nContext;

Copy the code

We create the initial Context with React. CreateContext, which contains three functions:

  • Translate: Used to translate the specified key value
  • GetLocale: Gets the current language type
  • SetLocale: Sets the display of a language type

We have the Context, and then we need a Provider (I18nProvider) to wrap around the Context:

import React, { useState } from 'react';

import I18nContext from './I18nContext';



class I18nProvider extends React.Component {

    constructor(props) {

        super(props);

        this.state = {

            locale: 'en-US'.

        }

    }

    

    render() {

        const i18n = {

            translate: key => this.props.languages[this.state.locale][key],

            getLocale: () => this.state.locale,

            setLocale: locale => this.setState({ locale, })

        }

        

        return (

            <I18nContext.Provider value={i18n}>

                {this.props.children}

            </I18nContext.Provider>

        )

    }

}



export default I18nProvider;



Copy the code

Since there is a provider, there must be a consumer. We create two new components, one is Content (display Content) and one is Footer (switch language), as follows:

  import I18nContext from '.. /Context/I18nContext';



  class Footer extends React.Component {

    render() {

        return (

            <I18nContext.Consumer>

                {({setLocale}) => {

                    return (

                        <select onChange={(e) => setLocale(e.target.value)}>

                            <option value="en-US"> English < / option >

                            <option value="zh-CN"> < / option in Chinese >

                        </select>

                     )

                }}

            </I18nContext.Consumer>

        )

    }

}



  class Title extends React.Component {

      render() {

          return (

              <I18nContext.Consumer>

                  {({translate}) => {

                      return (<div>{translate('hello')}{translate('name')}</div>)

                  }}

              </I18nContext.Consumer>

          )

      }

  }



Copy the code

(You also need to create folder locals and include two language files before doing this)

// Path: locals/ en-us.js

const en_US = {

    hello: 'Hello, world! '.

    name: 'my name is mary'

}    



export default en_US; 



// Path: locals/ zh_cn.js

const zh_CN = {

    hello: 'Hello world! '.

    name: 'My name is Mary'

  }

  export default zh_CN; 

  

Copy the code

The last step requires injecting the Provider at the top level, as follows:

// Path: locals/ en-us.js

import React from 'react';

import ReactDOM from 'react-dom';

import App from './App';

import I18nProvider from './Context/I18nProvider';

import enUS from './locales/en-US';

import zhCN from './locales/zh-CN';



const locales = [ 'en-US'.'zh-CN' ];

const languages = {

  'en-US': enUS,

  'zh-CN': zhCN,

}



ReactDOM.render(

  <I18nProvider locales={locales} languages={languages}>

    <App />

  </I18nProvider>,



  document.getElementById('root')



);

Copy the code

So now we have the effect of switching between different languages and displaying different languages. The effect is as follows:

Sometimes we need to see if the code can be optimized. When we take a closer look, we find that the Content and Footer components look similar. Can we optimize the code? Create withi18n.js (withi18n.js, withi18n.js)

import React from 'react';

import I18nContext from './I18nContext';

// First edition

function withI18n(WrapperComponent) {

  return class LogoProps extends React.Component {

    render() { 

      return (

        <I18nContext.Consumer>

{i18n => <WrapperComponent {... i18n} {... this.props} />}

        </I18nContext.Consumer>

      )

    }

  }

}

// First edition (recommended)

const withI18n = (WrappedComponent) => {

    return (props) => (

      <I18nContext.Consumer>

{i18n => <WrappedComponent {... i18n} {... props} />}

      </I18nContext.Consumer>

    )

};

export default withI18n;



Copy the code

At this point, we will modify the Content and Footer components as follows:

import withI18n from '.. /Context/withI18n';



const Title = withI18n(

    ({ translate }) => { 

      return ( <div>{translate('hello')}</div> )

    }

  )

  

const Footer = withI18n(

    ({ setLocale }) => {

      return (<button onClick={() => {setLocale('zh-CN')}} >Click Me</button> )

    }

  )

Copy the code

Does the code look much cleaner after the modification?

What other way is there to pass values from unrelated components? Let’s move on:

Global variables and events

Global variables: As the name implies, variables placed on the Window. However, it is worth noting that modifying variables on the Window does not cause the React component to re-render.

Therefore, global variables are more recommended for temporary data storage in use scenarios. For example, after clicking the CallPage button, you need to collect a callId and report the callId in ReportPage. The following code looks like this:

class CallPage extends React.Component { 

    render() {

        return <Button onClick={() => {

              window.callId = this.props.callId         

}} / >

}



class ReportPage extends React.Component {

    render() {

        return <Button onClick={() => {

              fetch('/api/report', { id: window.callId })          

}} / >

    }

}



Copy the code

Disadvantages: If you have a business and need to show and hide components when callId changes, you can’t do this because changing variables on Window will not cause the React component to re-render.

Global events:

class CallPage extends React.Component {

    dispatchEvent = () => {

        document.dispatchEvent(new CustomEvent('callEvent', {

          detail: {

             callId: this.props.callId

          }

        }))

    }



    render() {

        return <Button onClick={this.dispatchEvent} />

}



class ReportPage extends React.Component {

    state = {

      callId: null,

    }



    changeCallId = (e) => {

      this.setState({

        callId: e.detail.callId

      })

    } 



    componentDidMount() {

        document.addEventListener('callEvent', this.changeCallId)

    }



    componentWillUnmount() {

        document.removeEventListener('callEvent', this.changeCallId)

    }



    render() {

        return <Button onClick={() => {

              fetch('/api/report', { id: this.state.callId })          

}} / >

    }

}



Copy the code

As a result, the page is rendered again, which makes up for the problem of global events. However, we all know that events are registered when the component is loaded, so this means that both components are reportPages. Callpages must be placed on the same page.

END

Finally, we can summarize and have the following knowledge map: