Original http://blog.51yuekan.com/2015/07/23/2015-07-19-react-component-communicate/ThemesReactObjective
Today, there are a lot of questions about how to communicate between React components, and I have encountered such problems when I wrote them. Here is a good English version of my translation, read my blog people know that my translation may not be step-by-step, as far as possible in Chinese meaning, to the author to tell the technical description clearly. English ability is limited, if there is wrong place please leave a message with me, must revise ... ^_^
Original sequence
Dealing with the communication between React components depends primarily on the relationship between the components, but it is you who are the ones who deal with these relationships.
I'm not going to talk too much about Data-stores, data-adapters or data-helpers. I'm only focusing on how the React component itself communicates.
The way of communication between React components can be divided into the following 3 kinds:
- "Parent component" to "sub-component" value;
- "Subcomponent" to "parent component" value;
- Transfer values between components that do not have any nested relationships (PS: For example: Passing values between sibling components)
One, "parent component" to "sub-component" Pass value
Preliminary use
This is fairly easy to use in the process of using React development, mainly using props to communicate. Examples are as follows:
// parent component
var MyContainer = React.createClass ({
getInitialState: function () {
return {
checked: true
};
},
render: function () {
return (
<ToggleButton text = "Toggle me" checked = {this.state.checked} />
);
}
});
// Subassembly
var ToggleButton = React.createClass ({
render: function () {
// value obtained from [parent component]
var checked = this.props.checked,
text = this.props.text;
return (
<label> {text}: <input type = "checkbox" checked = {checked} /> </ label>
);
}
});
Further discussion
If the component nesting level is too deep, then the communication cost from the outside to the inside component becomes very high, the advantage of passing the value by props is not so obvious. (PS: So I recommend reducing the level of components as much as possible, like writing HTML, simple and clear structure to make love)
// parent component
var MyContainer = React.createClass ({
render: function () {
return (
<Intermediate text = "where is my son?" />
);
}
});
// Subcomponent 1: Nested component
var Intermediate = React.createClass ({
render: function () {
return (
<Child text = {this.props.text} />
);
}
});
// child component 2: child component of child component 1
var Child = React.createClass ({
render: function () {
return (
<span> {this.props.text} </ span>
);
}
});
Second, "subcomponent" to "Parent component" value
Next, we describe "subcomponents" that control their state and then tell the "parent component" the click States, and then show it in the parent component. Therefore, we add a change event to do the interaction.
// parent component
var MyContainer = React.createClass ({
getInitialState: function () {
return {
checked: false
};
},
onChildChanged: function (newState) {
this.setState ({
checked: newState
});
},
render: function () {
var isChecked = this.state.checked? ‘yes’: ‘no’;
return (
<div>
<div> Are you checked: {isChecked} </ div>
<ToggleButton text = "Toggle me"
initialChecked = {this.state.checked}
callbackParent = {this.onChildChanged}
/>
</ div>
);
}
});
// Subassembly
var ToggleButton = React.createClass ({
getInitialState: function () {
return {
checked: this.props.initialChecked
};
},
onTextChange: function () {
var newState =! this.state.checked;
this.setState ({
checked: newState
});
// Note here: setState is an asynchronous method, so you need to manipulate the current value of the cache
this.props.callbackParent (newState);
},
render: function () {
// value obtained from [parent component]
var text = this.props.text;
// state data of the component itself
var checked = this.state.checked;
return (
<label> {text}: <input type = "checkbox" checked = {checked} onChange = {this.onTextChange} /> </ label>
);
}
});
I think the original author of the code is not very intuitive, then I say a flow to a schematic diagram to visually describe the process:
This is actually relying on props to pass a reference to the event and is implemented in a callback way, which is not particularly good, but is a simple implementation without any tools.
Here is a question we discussed earlier, that is, when a component has multiple layers of nesting, you have to pass in a callback function once to props to implement a child component to pass values or operations to the parent component.
Tiny-tip:react Event System
In OnChange events or other React events, you can get the following:
- "This": point to your component
- "One parameter": This parameter is a React synthesis event, syntheticevent.
React management of all events is self-fulfilling, unlike the onclick, onchange events we used before. Fundamentally, they are all bound to the body.
document.on(‘change‘, ‘input[data-reactid=".0.2"]‘, function () {...});
The above code is not from the React, just a metaphor.
If I'm not mistaken, the code that React really handles an event is as follows:
var listenTo = ReactBrowserEventEmitter.listenTo;
...
function putListener (id, registrationName, listener, transaction) {
...
var container = ReactMount.findReactContainerForID (id);
if (container) {
var doc = container.nodeType === ELEMENT_NODE_TYPE? container.ownerDocument: container;
listenTo (registrationName, doc);
}
...
}
// Inside the listening event, we can find the following:
target.addEventListener (eventType, callback, false);
Here are all the events supported by React: Chinese documentation-Event System
Cases where multiple subcomponents use the same callback
// parent component
var MyContainer = React.createClass ({
getInitialState: function () {
return {
totalChecked: 0
};
},
onChildChanged: function (newState) {
var newToral = this.state.totalChecked
+ (newState? 1: -1);
this.setState ({
totalChecked: newToral
});
},
render: function () {
var totalChecked = this.state.totalChecked;
return (
<div>
<div> How many are checked: {totalChecked} </ div>
<ToggleButton text = "Toggle me"
initialChecked = {this.state.checked}
callbackParent = {this.onChildChanged}
/>
<ToggleButton text = "Toggle me too"
initialChecked = {this.state.checked}
callbackParent = {this.onChildChanged}
/>
<ToggleButton text = "And me"
initialChecked = {this.state.checked}
callbackParent = {this.onChildChanged}
/>
</ div>
);
}
});
// Subassembly
var ToggleButton = React.createClass ({
getInitialState: function () {
return {
checked: this.props.initialChecked
};
},
onTextChange: function () {
var newState =! this.state.checked;
this.setState ({
checked: newState
});
// Note here: setState is an asynchronous method, so you need to manipulate the current value of the cache
this.props.callbackParent (newState);
},
render: function () {
// value obtained from [parent component]
var text = this.props.text;
// state data of the component itself
var checked = this.state.checked;
return (
<label> {text}: <input type = "checkbox" checked = {checked} onChange = {this.onTextChange} /> </ label>
);
}
});
This is very easy to understand, in the parent component we add a "totalchecked" to replace the "checked" in the previous example, when the subassembly changes, use the same child component callback function to the parent component return value.
Iii. passing values between components that do not have any nested relationships
If there is no relationship between components, the component nesting level is deep (individuals think that the level of 2 is already deep), or you can subscribe to some components, write some signals, do not want to let the components into a component, so that two components in a separate relationship. For the event system, there are 2 basic steps: Subscribe (subscribe)/Listen (listen) an event notification and send (send)/trigger (trigger)/publish (publish)/Send (Dispatch) an event to notify those desired components.
Here are some 3 modes to handle events, you can click here to compare them.
Briefly summarize:
(1) Event Emitter/target/dispatcher
Feature: A specified feed is required
// to subscribe
otherObject.addEventListener(‘click’, function() { alert(‘click!’); });
// to dispatch
this.dispatchEvent(‘click’);
(2) Publish/subscribe
Feature: When triggering an event, you do not need to specify a specific source because it uses a global object to handle events (in fact, it is a global broadcast way to handle events)
// to subscribe
globalBroadcaster.subscribe(‘click’, function() { alert(‘click!’); });
// to dispatch
globalBroadcaster.publish(‘click’);
(3) Signals
Features: Similar to event emitter/target/dispatcher, but you do not use random strings as references to event firings. Each object that triggers an event requires an exact name (that is, a write-down event name similar to a hard-coded class), and the exact event must be specified at the time of the trigger. (see example, well understood)
// to subscribe
otherObject.clicked.add(function() { alert(‘click’); });
// to dispatch
this.clicked.dispatch();
If you just want to use it simply, you don't need to do anything else, it can be done in a simple way:
// Simple implementation of subscribe and dispatch
var EventEmitter = {
_events: {},
dispatch: function (event, data) {
if (! this._events [event]) {// No event listener
return;
}
for (var i = 0; i <this._events [event] .length; i ++) {
this._events [event] [i] (data);
}
},
subscribe: function (event, callback) {
// create a new event array
if (! this._events [event]) {
this._events [event] = [];
}
this._events [event] .push (callback);
}
};
otherObject.subscribe (‘namechanged‘, function (data) {alert (data.name);});
this.dispatch (‘namechanged’, {name: ‘John’});
If you want to use the Publish/subscribe model, you can use: PUBSUBJS
The React team is using: Js-signals It is based on signals mode and is quite good to use.
Events in React
When using the React event, the following two methods must be followed:
componentDidMountcomponentWillUnmount
When dealing with events, be aware of:
In the Componentdidmount event, if the component mount (mounted) completes, the subscription event is cancelled, and the subscription to the event is canceled in the Componentwillunmount event when the component is unloaded (unmounted).
(If you are not sure you can refer to the documentation for the life cycle introduction of React, there are also descriptions.) The original text describes is Componentwillmount personally think should be mounted after the completion of the subscription event, such as animation This must be mounted, and can not dynamically add, more cautious point better)
Because the rendering and destruction of components is controlled by React, we do not know how to refer to them, so the Eventemitter mode is not very useful when dealing with components.
Pub/sub mode can be used, you do not need to know the reference.
Here's an example: the implementation has multiple product components, and when you click on them, the product name is displayed.
(I introduced in the example of the previously recommended PUBSUBJS library, if you think the introduction of the cost is too large, you can also write a simple version, or relatively easy, very useful ha, we can also experience, but I still do not recommend the way of global Broadcasting)
// define a container
var ProductList = React.createClass ({
render: function () {
return (
<div>
<ProductSelection />
<Product name = "product 1" />
<Product name = "product 2" />
<Product name = "product 3" />
</ div>
);
}
});
// Product information container used to display clicks
var ProductSelection = React.createClass ({
getInitialState: function () {
return {
selection: ‘none’
};
},
componentDidMount: function () {
this.pubsub_token = PubSub.subscribe (‘products’, function (topic, product) {
this.setState ({
selection: product
});
} .bind (this));
},
componentWillUnmount: function () {
PubSub.unsubscribe (this.pubsub_token);
},
render: function () {
return (
<p> You have selected the product: {this.state.selection} </ p>
);
}
});
var Product = React.createClass ({
onclick: function () {
PubSub.publish (‘products’, this.props.name);
},
render: function () {
return <div onClick = {this.onclick}> {this.props.name} </ div>;
}
});
Es6:yield and JS-CSP
There is a way to pass information in ES6, using the Generate function (generators) and the yield keyword. You can take a look at HTTPS://GITHUB.COM/UBOLONTON/JS-CSP.
(Here I write a simple DEMO to introduce this new way of delivery, in fact, very similar)
function* list() {
for(var i = 0; i < arguments.length; i++) {
yield arguments[i];
}
return "done.";
}
var o = list(1, 2, 3);
var cur = o.next;
while(!cur.done) {
cur = o.next();
console.log(cur);
}
The above example is from a blog in Cucques: ES6 in the generator function Cucques is a Daniel, you can always pay attention to his blog.
Generally speaking, you have a queue where the object can find a reference, lock it when it is defined, and immediately open the lock execution when it occurs. JS-CSP is a solution, and there may be other solutions in the future.
End
In the actual application, according to the actual needs to solve the need to choose the solution. For small applications, you can use the props and callback methods to exchange data between components. You can pass the Pub/sub mode to avoid polluting your components. Here, we are not talking about data, just components. For data requests, data changes and other scenarios, you can use the Facebook Flux, Relay, graphql to handle, are very useful.
Each of the examples I have verified, mainly using the most original introduction file method, create the service using the Http-server package, you can also try to do it yourself.
How to communicate between React components