This section is a collection of implementation notes for the Stack Reconciler program.
This article is technical, with a deep understanding of the React public API and how it is divided into core, renderer and Reconciler programs. If you’re not familiar with the React codebase, read the overview of the codebase first.
It also assumes that you understand the differences between instances and elements of the React component.
The Stack Reconciler is used in version 15 and earlier. Its code in the SRC/renderers/Shared/stack/reconciler.
Video: Building React from scratch
Paul O ‘Shannessy talked about building React from scratch, which largely inspired this document.
This document and his talk are simplifications of the actual code base, so you can gain a better understanding by familiarizing yourself with them.
An overview of the
There is no public API for the Reconciler itself. Renderers like React DOM and React Native use it to effectively update the user interface based on user-written React components.
Mounting as a recursive process
Let’s consider mounting a component for the first time:
ReactDOM.render(<App />, rootEl);
Copy the code
The React DOM passes the
console.log(<App />);
// { type: App, props: {} }
Copy the code
The mediator checks whether the App is a class or a function (see how to know if it’s a function or a class for this implementation).
If App is a function, the mediator calls App(props) to get the render element.
If App is a class, the mediator instantiates the App with the new App(props), calls the componentWillMount lifecycle method, and then calls the Render method to get the rendered elements.
Either way, the mediator will know what elements the App has “rendered to.”
The process is recursive. Your App might render
Think of this process as pseudocode:
function isClass(type) {
// The class below react.componenthas this tag
return (
Boolean(type.prototype) &&
Boolean(type.prototype.isReactComponent)
);
}
// This function accepts a React element (e.g.
)
// And returns a DOM or native node with the tree already mounted
function mount(element) {
var type = element.type;
var props = element.props;
// We will determine the type of render element
// The function is called directly
// The class calls render() after instantiation.
var renderedElement;
if (isClass(type)) {
/ / class components
var publicInstance = new type(props);
/ / set the props
publicInstance.props = props;
// Call the lifecycle method if necessary
if (publicInstance.componentWillMount) {
publicInstance.componentWillMount();
}
// Get rendered elements by calling render()
renderedElement = publicInstance.render();
} else {
// Function components
renderedElement = type(props);
}
// The process is recursive because one component may return an element of the same type as another component
return mount(renderedElement);
// Note: this implementation is incomplete and will repeat indefinitely
// It only handles elements like
or .
// It does not yet handle elements like
or .
}
var rootEl = document.getElementById('root');
var node = mount(<App />);
rootEl.appendChild(node);
Copy the code
Note: This is really just pseudocode, and it doesn’t look like the real implementation. It also causes a stack overflow, because we haven’t discussed when to stop recursion.
Let’s review some key ideas from the example above:
- React Elements is just a pure object that describes the type of component (e.g.
App
) and his props. - User-defined components (e.g.
App
) can be functions or classes, but they both render these elements. - Mounting is a recursive process that determines whether a given React element (e.g
<App />
) to create a DOM or Native tree.
Mounting The computer (Host) element
This process is useless if we don’t have something on the screen.
In addition to user-defined (” composite “) components, React elements can also represent platform-specific (” computer “) components. For example, Button might return
from its Render method.
If element’s type attribute is a string, we think we’re dealing with a computer element:
console.log(<div />);
// { type: 'div', props: {} }
Copy the code
There is no user-defined code associated with the computer element.
When the coordinator encounters these computer elements, it makes the renderer responsible for mounting them. For example, React DOM creates a DOM node.
If a computer element has child nodes, the coordinator mounts them recursively with the same algorithm as above. It doesn’t matter whether the child node is a computer element (
) or a user-generated component (
).
The DOM node generated by the child component is attached to the parent DOM node and the complete DOM structure is recursively assembled.
Note: The mediator itself is DOM independent. The exact result of mounting the mounting(sometimes called the “mount image” in source code) depends on the renderer, and can be a React DOM node, a string, or a React Native view.
If we were to extend the code to handle computer elements, it would look like this:
function isClass(type) {
// The react.componentclass inherits a tag isReactComponent
return (
Boolean(type.prototype) &&
Boolean(type.prototype.isReactComponent)
);
}
// This function only handles composite elements
// Such as
, , but not
function mountComposite(element) {
var type = element.type;
var props = element.props;
var renderedElement;
if (isClass(type)) {
// If the component is a class, instantiate it
var publicInstance = new type(props);
/ / set the props
publicInstance.props = props;
// Call lifecycle methods when necessary
if (publicInstance.componentWillMount) {
publicInstance.componentWillMount();
}
renderedElement = publicInstance.render();
} else if (typeof type === 'function') {
// Component is a function
renderedElement = type(props);
}
// This is recursive
// But when the element is the host (e.g.
) instead of the compound (e.g.
), we end up with recursion:
return mount(renderedElement);
}
// This function only deals with computer elements
For example, it handles
and , but not
.
function mountHost(element) {
var type = element.type;
var props = element.props;
var children = props.children || [];
if (!Array.isArray(children)) {
children = [children];
}
children = children.filter(Boolean);
// This code should not be in the coordinator.
// Different renderers may initialize nodes differently.
// For example, React Native will create iOS or Android views.
var node = document.createElement(type);
Object.keys(props).forEach(propName= > {
if(propName ! = ='children') { node.setAttribute(propName, props[propName]); }});// Install the child element
children.forEach(childElement= > {
// The child can be a computer element (such as
) or a composite component (such as ).
// We all mount recursively
var childNode = mount(childElement);
// The following one is also platform-specific
// It will be handled by different renderers, but this is just an assumption that it is a DOM renderer
node.appendChild(childNode);
});
// Returns the DOM node as the result of the installation
// This is where the recursion ends
return node;
}
function mount(element) {
var type = element.type;
if (typeof type === 'function') {
// User-defined components (composite components)
return mountComposite(element);
} else if (typeof type === 'string') {
// Computer components (e.g.
)
returnmountHost(element); }}var rootEl = document.getElementById('root');
var node = mount(<App />);
rootEl.appendChild(node);
Copy the code
This is effective, but still far from the way the coordinator actually operates. The key missing piece is support for updates.
Introducing Internal Examples
The key feature of React is that you can re-render everything; it doesn’t recreate the DOM or reset the state.
ReactDOM.render(<App />, rootEl); // The existing DOM should be reused: reactdom.render (<App />, rootEl);
Copy the code
However, our implementation above only knows how to mount the initial tree. It cannot update it because it does not store all the necessary information, such as all publicInstances, or which DOM nodes correspond to which components.
The stack coordinator code base solves this problem by making the mount function a method above a class. But there are some drawbacks to this approach, and we’re going in the opposite direction in our ongoing coordination rewrite task (Author: Fiber is out so far). But that’s how it works now.
Instead of the separate mountHost and mountComposite functions, we will create two classes: DOMComponent and CompositeComponent.
Both classes have a constructor that accepts elements and a mount() method that returns installed nodes. We’ll replace the top-level mount() function with a factory that instantiates the class:
function instantiateComponent(element) {
var type = element.type;
if (typeof type === 'function') {
// User-defined component
return new CompositeComponent(element);
} else if (typeof type === 'string') {
// Platform-specific components, such as computer components (
)
return newDOMComponent(element); }}Copy the code
First, let’s consider the CompositeComponent implementation:
class CompositeComponent {
constructor(element) {
this.currentElement = element;
this.renderedComponent = null;
this.publicInstance = null;
}
getPublicInstance() {
// For composite components, expose instances of the class
return this.publicInstance;
}
mount() {
var element = this.currentElement;
var type = element.type;
var props = element.props;
var publicInstance;
var renderedElement;
if (isClass(type)) {
// Component class
publicInstance = new type(props);
// Set the props
publicInstance.props = props;
// Call the lifecycle if necessary
if (publicInstance.componentWillMount) {
publicInstance.componentWillMount();
}
renderedElement = publicInstance.render();
} else if (typeof type === 'function') {
// Component function
publicInstance = null;
renderedElement = type(props);
}
// Save the public instance
this.publicInstance = publicInstance;
// Instantiate the inner instance of the child according to the element
// It will be DOMComponent, e.g.
,
CompositeComponent, e.g.
,
var renderedComponent = instantiateComponent(renderedElement);
this.renderedComponent = renderedComponent;
// Mount the rendered output
returnrenderedComponent.mount(); }}Copy the code
This and our previous mountComposite () implementation is no different, but now we can store some information, such as this. CurrentElement, enclosing renderedComponent and enclosing publicInstance, used during the update.
Note that the CompositeComponent instance is different from the user-provided instance of Element.type. CompositeComponent is the implementation details of our CompositeComponent that are never made public to users. The CompositeComponent creates an instance of the user-defined class that we read from Element. type.
To avoid confusion, we call the CompositeComponent and DOMComponent instances “internal instances.” They exist, so we can relate some long-standing data to them. Only the renderer and mediator know they exist.
Instead, we call instances of user-defined classes “public instances.” The public instance is the this you see in render() and other component methods.
For the mountHost() method, refactored to the mount() method on the DOMComponent class, which looks like this:
class DOMComponent {
constructor(element) {
this.currentElement = element;
this.renderedChildren = [];
this.node = null;
}
getPublicInstance() {
// For DOM components, only expose the DOM node.
return this.node;
}
mount() {
var element = this.currentElement;
var type = element.type;
var props = element.props;
var children = props.children || [];
if (!Array.isArray(children)) {
children = [children];
}
// Create and save the node
var node = document.createElement(type);
this.node = node;
// Set the attributes
Object.keys(props).forEach(propName= > {
if(propName ! = ='children') { node.setAttribute(propName, props[propName]); }});// Create and save the contained child elements
// Each of these child elements can be a DOMComponent or CompositeComponent
// These matches are dependent on the return value of the element type (string or function)
var renderedChildren = children.map(instantiateComponent);
this.renderedChildren = renderedChildren;
// Collect DOM nodes they return on mount
var childNodes = renderedChildren.map(child= > child.mount());
childNodes.forEach(childNode= > node.appendChild(childNode));
// the DOM node is returned as the mount node
returnnode; }}Copy the code
The main difference from the mountHost() refactoring is that this.node and this.renderedChildren are now associated with internal DOM component instances. We’ll use it for non-destructive updates later.
Therefore, each internal instance (composite or host) now points to its child internal instance. To aid visualization, if the function
component renders the
[object CompositeComponent] {
currentElement: <App />,
publicInstance: null,
renderedComponent: [object CompositeComponent] {
currentElement: <Button />,
publicInstance: [object Button],
renderedComponent: [object DOMComponent] {
currentElement: <div />,
node: [object HTMLDivElement],
renderedChildren: []
}
}
}
Copy the code
In the DOM, all you see is
Compound internal instances need to store:
- The current element
- Public instance if the current element type is a class
- A single rendered internal instance. It can be
DOMComponent
orCompositeComponent
.
Internal computer instances need to be stored:
- The current element
- DOM node
- Internal instances of all children, each of which can be
DOMComponent
orCompositeComponent
.
If you’re struggling to imagine how to build an internal instance tree in a more complex application, React DevTools can give you an approximation because it highlights computer instances in gray, as well as composite instances in purple:
To accomplish this refactoring, we’ll introduce a function that installs the entire tree into the container node, like reactdom.render (). It returns a public instance, also like reactdom.render ():
function mountTree(element, containerNode) {
// Create an internal instance of the top layer
var rootComponent = instantiateComponent(element);
// Mount the top-level component to the container
var node = rootComponent.mount();
containerNode.appendChild(node);
// Returns the public instance he provides
var publicInstance = rootComponent.getPublicInstance();
return publicInstance;
}
var rootEl = document.getElementById('root');
mountTree(<App />, rootEl);
Copy the code
uninstall
Now that we have internal instances to hold their child nodes and DOM nodes, we can implement unload. For composite components, unload invokes lifecycle methods and recurses.
class CompositeComponent {
// ...
unmount() {var publicInstance = this.publicinstance;if (publicInstance) {
if(publicInstance.componentWillUnmount) { publicInstance.componentWillUnmount(); } } // Unmount the single rendered component var renderedComponent = this.renderedComponent; renderedComponent.unmount(); }}Copy the code
For DOMComponent, unload tells each child node to unload:
class DOMComponent {
// ...
unmount() {
// Unmount all children
var renderedChildren = this.renderedChildren;
renderedChildren.forEach(child= >child.unmount()); }}Copy the code
In fact, unloading the DOM component also removes the event listener and clears some of the cache, but we’ll skip over those details.
We can now add a named unmountTree (containerNode) new top function, it is similar to ReactDOM. UnmountComponentAtNode () :
function unmountTree(containerNode) {
// Read the internal instance from the DOM node
// (this doesn't work right now, we'll need to change the mountTree() method to store it)
var node = containerNode.firstChild;
var rootComponent = node._internalInstance;
// Clear the container and unload the tree
rootComponent.unmount();
containerNode.innerHTML = ' ';
}
Copy the code
For this to work, we need to read the internal root instance from the DOM node. We will modify mountTree() to add the _internalInstance attribute to the DOM root node. We’ll also have mountTree() destroy any existing tree so that it can be called multiple times:
function mountTree(element, containerNode) {
// Destroy existing trees
if (containerNode.firstChild) {
unmountTree(containerNode);
}
// Create an internal instance of the top layer
var rootComponent = instantiateComponent(element);
// Mount the top-level component to the container
var node = rootComponent.mount();
containerNode.appendChild(node);
// Save a reference to the internal instance
node._internalInstance = rootComponent;
// Returns the public instance he provides
var publicInstance = rootComponent.getPublicInstance();
return publicInstance;
}
Copy the code
Now, repeat unmountTree() or mountTree() to remove the old tree and run the componentWillUnmount() lifecycle method on the component.
update
In the previous section, we implemented uninstallation. However, if each prop change causes the entire tree to be uninstalled and installed, React will not look very useful. The goal of the coordinator is to reuse existing instances as much as possible to preserve DOM and state:
var rootEl = document.getElementById('root');
mountTree(<App />, rootEl); // The existing DOM mountTree should be reused.<App />, rootEl);
Copy the code
We will extend our internal instance in another way. In addition to mount() and unmount(), both DOMComponent and CompositeComponent implement a new method called Receive (nextElement) :
class CompositeComponent {
// ...
receive(nextElement) {
// ...}}class DOMComponent {
// ...
receive(nextElement) {
// ...}}Copy the code
Its job is to do everything possible to keep the component (and any of its children) in sync with the description provided by nextElement.
This is what is often described as the “virtual DOM distinction,” although what really happens is that we recursively traverse the internal tree and make each internal instance receive updates.
Updating composite components
When a composite component receives a new element, we run the componentWillUpdate() lifecycle method.
We then re-render the component using the new props and get the next render element:
class CompositeComponent {
// ...
receive(nextElement) {
var prevProps = this.currentElement.props;
var publicInstance = this.publicInstance;
var prevRenderedComponent = this.renderedComponent;
var prevRenderedElement = prevRenderedComponent.currentElement;
// Update the own element
this.currentElement = nextElement;
var type = nextElement.type;
var nextProps = nextElement.props;
// What is the output of the next render()
var nextRenderedElement;
if (isClass(type)) {
/ / class components
// Call the lifecycle when necessary
if (publicInstance.componentWillUpdate) {
publicInstance.componentWillUpdate(nextProps);
}
/ / update the props
publicInstance.props = nextProps;
// Re-render
nextRenderedElement = publicInstance.render();
} else if (typeof type === 'function') {
// Functional components
nextRenderedElement = type(nextProps);
}
// ...
Copy the code
Next, we can look at the type of the render element. If type has not changed since the last render, the following components can also be updated from the previous ones.
For example, if is returned the first time and the second time, we can tell the corresponding internal instance to receive() the next element:
// ...
// If the element type is not changed,
// Reuse existing component instances
if (prevRenderedElement.type === nextRenderedElement.type) {
prevRenderedComponent.receive(nextRenderedElement);
return;
}
// ...
Copy the code
However, if the next rendered element is of a different type than the previously rendered element, we cannot update the internal instance. cannot become .
Instead, we must unmount the existing internal instance and mount the new instance corresponding to the element type being rendered. For example, this happens when a component that previously rendered renders :
// ...
// If we reach this point, then we need to unmount the previously mounted components
// Mount the new ones and swap their nodes
// Find the old node, because we need to replace it
var prevNode = prevRenderedComponent.getHostNode();
// Unmount the old child and mount the new child
prevRenderedComponent.unmount();
var nextRenderedComponent = instantiateComponent(nextRenderedElement);
var nextNode = nextRenderedComponent.mount();
// Replace references to children
this.renderedComponent = nextRenderedComponent;
// The new node replaces the old one
// Remember: the following code is platform-specific, ideally outside of the CompositeComponentprevNode.parentNode.replaceChild(nextNode, prevNode); }}Copy the code
In summary, when a composite component receives a new element, it can delegate the update to the internal instance it renders, or uninstall it and install the new instance in its place.
Under another condition, the component will be reinstalled instead of receiving the element, that is, the element’s key has changed. We won’t discuss key handling in this document because it adds more complexity to an already complex tutorial.
Note that we need to add a method called getHostNode() to the internal instance protocol so that the platform-specific node can be found and replaced during the update. Its implementation is simple for both classes:
class CompositeComponent {
// ...
getHostNode() {
// Request the render component to provide it
// This will recursively compound components downward
return this.renderedComponent.getHostNode(); }}class DOMComponent {
// ...
getHostNode() {
return this.node; }}Copy the code
Replacement of computer components
Computer component implementations, such as DOMComponent, are updated in different ways. When they receive the element, they need to update the underlying platform-specific view. In the React DOM case, this means updating the DOM attribute:
class DOMComponent {
// ...
receive(nextElement) {
var node = this.node;
var prevElement = this.currentElement;
var prevProps = prevElement.props;
var nextProps = nextElement.props;
this.currentElement = nextElement;
// Remove the old attributes
Object.keys(prevProps).forEach(propName= > {
if(propName ! = ='children'&&! nextProps.hasOwnProperty(propName)) { node.removeAttribute(propName); }});// Set the following properties
Object.keys(nextProps).forEach(propName= > {
if(propName ! = ='children') { node.setAttribute(propName, nextProps[propName]); }});// ...
Copy the code
Then, the computer components need to update their child components. Unlike composite components, they may contain multiple child components.
In this simplified example, we take an array of internal instances and iterate over it, updating or replacing the internal instance based on whether the received type matches the previous type. In addition to inserting and deleting, a real coordinator would use the element’s keys to track movement, but we’ll omit this logic.
We collect sub-DOM operations in a list to execute them in batches:
// ...
// React Elements array
var prevChildren = prevProps.children || [];
if (!Array.isArray(prevChildren)) {
prevChildren = [prevChildren];
}
var nextChildren = nextProps.children || [];
if (!Array.isArray(nextChildren)) {
nextChildren = [nextChildren];
}
// This is an array of internal instances:
var prevRenderedChildren = this.renderedChildren;
var nextRenderedChildren = [];
// As we iterate over the children, we will add operations to the array
var operationQueue = [];
// Note: The following part is very simple!
// It exists only to illustrate the process, not the details.
for (var i = 0; i < nextChildren.length; i++) {
// Try to get an existing internal instance of this child
var prevChild = prevRenderedChildren[i];
// If there is no internal instance under the index, append the child to the end.
// Create a new internal instance, mount it and use its nodes
if(! prevChild) {var nextChild = instantiateComponent(nextChildren[i]);
var node = nextChild.mount();
// Record the nodes we need to append
operationQueue.push({type: 'ADD', node});
nextRenderedChildren.push(nextChild);
continue;
}
// We can update only instances where the element type matches.
Can be updated to
// But can not be updated to
(i.e. element type mismatch)
var canUpdate = prevChildren[i].type === nextChildren[i].type;
// If the existing instance cannot be updated, we must remove it
// And mount a new one to replace it
if(! canUpdate) {var prevNode = prevChild.getHostNode();
prevChild.unmount();
var nextChild = instantiateComponent(nextChildren[i]);
var nextNode = nextChild.mount();
// Record the nodes we need to swap
operationQueue.push({type: 'REPLACE', prevNode, nextNode});
nextRenderedChildren.push(nextChild);
continue;
}
If we can update an existing internal instance
// Just let him receive the next element and handle his own updates
prevChild.receive(nextChildren[i]);
nextRenderedChildren.push(prevChild);
}
// Finally unload the child of the element that does not exist
for (var j = nextChildren.length; j < prevChildren.length; j++) {
var prevChild = prevRenderedChildren[j];
var node = prevChild.getHostNode();
prevChild.unmount();
// Record the nodes we need to remove
operationQueue.push({type: 'REMOVE', node});
}
// Point the rendered child list to the updated version
this.renderedChildren = nextRenderedChildren;
// ...
Copy the code
As a final step, we perform DOM operations. Again, the real coordination code is more complex because it also handles movement:
// ...
// Process the operation queue.
while (operationQueue.length > 0) {
var operation = operationQueue.shift();
switch (operation.type) {
case 'ADD':
this.node.appendChild(operation.node);
break;
case 'REPLACE':
this.node.replaceChild(operation.nextNode, operation.prevNode);
break;
case 'REMOVE':
this.node.removeChild(operation.node);
break; }}}}Copy the code
This is updating the DOMComponent
The top update
Now that both CompositeComponent and DOMComponent implement the receive(nextElement) method, we can change the top-level mountTree() function to use it when the element type is the same as last time:
function mountTree(element, containerNode) {
// Check for existing trees
if (containerNode.firstChild) {
var prevNode = containerNode.firstChild;
var prevRootComponent = prevNode._internalInstance;
var prevElement = prevRootComponent.currentElement;
// If we can, reuse the existing root component
if (prevElement.type === element.type) {
prevRootComponent.receive(element);
return;
}
// Unmount existing trees in other cases
unmountTree(containerNode);
}
// ...
}
Copy the code
Now call mountTree() twice with the same type without damaging updates:
var rootEl = document.getElementById('root');
mountTree(<App />, rootEl);
// Reuses the existing DOM:
mountTree(<App />, rootEl);
Copy the code
These are the basics of the inner workings of React.
What are we missing
This document is simplified compared to the real code base. We have not addressed several important aspects:
- Components can be rendered
null
And the coordinator can handle the “empty” in the array and render the output. - The coordinator also reads from the element
key
And use it to determine which internal instance corresponds to which element in the array. Much of the complexity in the actual React implementation is related to this. - In addition to composite and internal computer instance classes, there are classes for “text” and “empty” components. They represent text nodes and pass rendering
null
Obtain “empty slots”. - Renderer useinjectionPasses computer internal classes to the coordinator. For example,
React DOM
Tell the coordinator to useReactDOMComponent
Implemented as an internal instance of the computer. - The logic for updating the subitem list is extracted to name
ReactMultiChild
themixin
It is composed ofReact DOM
andReact Native
In the computer internal instance class implementation. - The coordinator also implements pairs in composite components
setState()
Support. Multiple updates within an event handler are batched as a single update. - The coordinator is also responsible for attaching and detaching references to composite components and computer nodes.
- Lifecycle methods that are invoked after the DOM is ready (e.g
componentDidMount()
andcomponentDidUpdate()
) will be collected in a “callback queue” and executed in a single batch. - React puts information about the current update into an internal object called “Transaction.” Transaction is useful for tracking the queue of life-cycle methods to be processed, for warning about the nesting of the current DOM, and for anything else that is “global” for a particular update. The transaction also ensures React “cleans up everything” after updates. For example,
React DOM
The provided transaction class restores the input selection after any updates.
Enter the code
- ReactMountIt’s in this tutorial
mountTree()
andunmountTree()
Code like that. He is responsible for installing and unloading components from the top layer.ReactNativeMount
It’s a React Native simulation. ReactDOMComponent
Equivalent to that in this tutorialDOMComponent
. It implements the React DOM renderer’s computer component classes.ReactNativeBaseComponent
React Native simulation.ReactCompositeComponent
Is equivalent to that in this tutorialCompositeComponent
. It handles user-defined components and maintains state.instantiateReactComponent
Use to select the inner instance class to construct for the element. It is equivalent to the one in this tutorialinstantiateComponent()
.ReactReconciler
Is in themountComponent()
.receiveComponent()
.unmountComponent()
Methods. It calls the underlying implementation on the internal instance, but also includes some code shared by all internal instance implementations.- The ReactChildReconciler implementation handles the queue of insert, delete, and move operations of the child levels independently of the renderer.
- For legacy reasons,
mount()
.receive()
andunmount()
It’s actually called in the React code basemountComponent()
.receiveComponent()
andunmountComponent()
, but they receive elements. - Attributes on internal instances begin with an underscore, for example
_currentElement
. They are considered read-only public fields throughout the code base.
Future development direction
The Stack Reconciler has inherent limitations, such as synchronization and the inability to interrupt work or break it into chunks. The new Fiber Reconciler is under way (PEN: Of course, as everyone knows, it has been completed by now), and they have completely different structures. In the future, we intend to replace the stack coordinator with this, but for now it is far from functional verification.
The next step
Read the next section for our guidelines for React development.
译 文 : Implementation Notes
React implementation records