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: