Comprender el código fuente de Reacción – Renderización inicial (componente de clase) IV

Foto de Joshua Sortino en Unsplash

Comprender el código fuente de Reacción I
Comprender el código fuente de reacción II
Comprender el código fuente de la reacción III
Comprender el código fuente de React IV (este)
Comprender el código fuente de React V
Comprender el código fuente de reacción VI
Comprender el código fuente de React VII
Comprender el código fuente de reacción VIII
Comprender el código fuente de reacción IX

Hemos completado el proceso de renderizado de un componente simple. Esta vez vamos a explorar más ramificaciones de este proceso discutiendo cómo se representa un componente de clase (uno típico que podríamos usar en el desarrollo cotidiano).

Archivos utilizados en este artículo: lo mismo que publicar uno y dos

Utilizo {} para hacer referencia a la publicación anterior que es relevante para los métodos (o procesos lógicos) que se discuten.

El componente llamado App es similar a lo que di al principio del post uno , en el cual lo consideramos demasiado complejo para principiantes. Pero desde que nos nivelamos un poco, ya no se ve tan desalentador.

 importar React, {Component} de 'reaccionar'; 
importar el logo de './logo.svg';
import './App.css';
 la aplicación de clase extiende el componente { 
constructor (accesorios) {
super (apoyos);
this.state = {
desc: 'inicio',
};
}
 render () { 
regreso (
<div className = "App">
<div className = "App-header">
<img src = "main.jpg" className = "App-logo" alt = "logo" />
<h1> "Welcom reacciona" </ h1>
</ div>
<p className = "App-intro">
{this.state.desc}
</ p>
</ div>
);
}
}
 exportar la aplicación predeterminada; 
 App@App.js 

Como se mencionó, el componente anterior se representa usando:

 ReactDOM.render ( 
<App />,
document.getElementById ('root')
);

Ahora el código etiquetado:

 importar React, {Component} de 'reaccionar'; 
importar el logo de './logo.svg';
import './App.css';
 la aplicación de clase extiende el componente { 
constructor (accesorios) {
super (apoyos);
this.state = {
desc: 'inicio',
};
}
 render () { 
devolver React.createElement (
'div',
{className: 'App'},
React.createElement (
'div',
{className: 'App-header'},
React.createElement (
'img',
{src: "main.jpg", className: 'App-logo', alt: 'logo'}
),
React.createElement (
'h1',
nulo,
'' Welcom reacciona ''
)
),
React.createElement (
'pag',
{className: 'App-intro'},
this.state.desc
)
);
}
}
 exportar la aplicación predeterminada; 
 ... 
 ReactDOM.render (React.createElement (App, null), document.getElementById ('root')); 

Aquí consideramos que Component una clase base común, ya que no se usarán otros métodos en esta publicación.

Esta vez podemos adelantar la lógica que se comparte con el componente simple.

Construya the top level wrapper `ReactCompositeComponent[T]`

La estructura de datos designada:

Este paso es casi el mismo que en la representación de componente simple, por lo que daré una breve descripción,

1) crea ReactElement[1] usando ReactElement.createElement(type, config, children) (Esta vez la App se pasa al type , y config , los children son null );

2) crea ReactElement[2] en _renderSubtreeIntoContainer() ;

3) crea el contenedor designado con instantiateReactComponent() .

 ReactElement.createElement(type, // scr: -------------> App 
config, // scr: -------------> null
children // scr: -------------> null
) // scr: ------------------------------------------------------> 1)
 ReactDOM.render 
| = ReactMount.render (nextElement, contenedor, devolución de llamada)
| = ReactMount._renderSubtreeIntoContainer (
parentComponent, // scr: ----> null
nextElement, // scr: ----> ReactElement [1]
contenedor, // scr: ----> document.getElementById ('root')
callback '// scr: ----> undefined
) // scr: ------------------------------------------------------> 2)
| -instantiateReactComponent (// scr: -------------------------> 3)
nodo, // scr: ------> ReactElement [2]
shouldHaveDebugID / * falso * /
)
| -ReactCompositeComponentWrapper (
elemento // scr: ------> ReactElement [2]
);
| = ReactCompositeComponent.construct (element / * same * /)

Esto es lo que cubrimos en { post one }.

Inicializa `ReactCompositeComponent [T]`

La estructura de datos designada:

El paso es el mismo también:

1) ReactDOMContainerInfo[ins] representa el contenedor elemento DOM, document.getElementById('root') ;

2) TopLevelWrapper se TopLevelWrapper una instancia ( TopLevelWrapper[ins] ) y se establece en la ReactCompositeComponent[T]._instance junto con la inicialización de otras propiedades;

3) De nuevo, mountComponentIntoNode es el punto de cruce de la mitad superior e inferior, dentro del cual ReactCompositeComponent[T].mountComponent devuelve un DOMLazyTree completo que puede ser utilizado por ReactMount._mountImageIntoNode , un método de la mitad inferior.

 ReactDOM.render ___ 
| = ReactMount.render (nextElement, contenedor, devolución de llamada) |
| = ReactMount._renderSubtreeIntoContainer () |
| -ReactMount._renderNewRootComponent () |
| -instantiateReactComponent () |
| ~ batchedMountComponentIntoNode () mitad superior
| ~ mountComponentIntoNode () (plataforma independiente)
| -ReactReconciler.mountComponent () // scr -----> 1) |
| -ReactCompositeComponent [T] .mountComponent () scr:> 2) 3)
... _ | _
... mitad inferior
| -_mountImageIntoNode () (HTML DOM específico)

Esto es lo que cubrimos en la primera parte de {post two }.

Excepto por algunas pequeñas diferencias en cuanto a los valores de los argumentos, las operaciones relacionadas con los niveles superiores de envoltura son exactamente las mismas que las que discutimos en las publicaciones anteriores para el componente simple. Después de completar esas operaciones, la lógica procesa la primera ramificación que es específica para el componente de la clase.

`ReactCompositeComponent [T] .performInitialMount ()` – crea un `ReactCompositeComponent` de` ReactElement [1] `

Este paso quita el contenedor y crea otra instancia de ReactCompositeComponent para reflejar el componente de clase, la App .

La estructura de datos designada:

La pila de llamadas en acción:

 ... 
| ~ mountComponentIntoNode () |
| -ReactReconciler.mountComponent () |
| -ReactCompositeComponent [T] .mountComponent () |
/ * estamos aquí * / |
| -ReactCompositeComponent [T] .performInitialMount (|
renderedElement, // scr: -------> undefined |
hostParent, // scr: -------> null mitad superior
hostContainerInfo, // scr : -------> | ReactDOMContainerInfo[ins] |
transacción, // scr: -------> sin interés |
contexto, // scr: -------> sin interés |
) |

El proceso es muy similar al performInitialMount() en { post two }. La única diferencia aquí es que, basado en ReactElement[1].type _instantiateReactComponent , _instantiateReactComponent crea un ReactCompositeComponent para el componente de clase ( App ) en lugar de un ReactDOMComponent . Para decirlo brevemente:

1) llama a _renderValidatedComponent() que a su vez llama a TopLevelWrapper.render() para extraer ReactElement[1] ; 2) ReactCompositeComponent una instancia de un ReactCompositeComponent con _instantiateReactComponent (llamamos al objeto ReactCompositeComponent[ins]) ; y 3) llama a ReactCompositeComponent[ins].mountComponent (recursivamente) a través de ReactReconciler , y pasa al siguiente paso.

 performInitialMount: function ( 
renderedElement,
hostParent,
hostContainerInfo,
transacción,
contexto)
{
var inst = this._instance;
 ... 
 if (inst.componentWillMount) { 
... // scr: no definimos componentWillMount () en la aplicación
}
 // Si no es un componente sin estado, ahora renderizamos 
if (renderedElement === undefined) {
renderedElement = this._renderValidatedComponent (); // scr:> 1)
}
 var nodeType = ReactNodeTypes.getType (renderedElement); // scr: -> el tipo es ReactNodeTypes.Composite esta vez 
 this._renderedNodeType = nodeType; 
 var child = this._instantiateReactComponent (renderedElement, nodeType! == ReactNodeTypes.EMPTY / * shouldHaveDebugID * / 
); // scr: ---------------------------------------------- > 2)
 this._renderedComponent = child; 
 var markup = ReactReconciler.mountComponent (child, transaction, hostParent, hostContainerInfo, this._processChildContext (context), debugID); // scr: ---------------------------------------------- > 3) 
 ... // scr: código DEV 
 marcado de devolución; 
},
 ReactCompositeComponent@renderers/shared/stack/reconciler/ReactCompositeComponent.js 

`ReactCompositeComponent [ins] .mountComponent ()` – initialize `ReactCompositeComponent [ins]`

La estructura de datos designada:

La pila de llamadas en acción:

 ... 
| ~ mountComponentIntoNode () |
| -ReactReconciler.mountComponent () |
| -ReactCompositeComponent [T] .mountComponent () |
| -ReactCompositeComponent [T] .performInitialMount () mitad superior
| -ReactReconciler.mountComponent () |
/ * estamos aquí * / |
| -ReactCompositeComponent [ins] .mountComponent (same) |

Igual que en ReactCompositeComponent[T].mountComponent() {post two }, la tarea más importante de este paso es crear una instancia de la App con ReactCompositeComponent[ins]._currentElement ( ReactElement[1] ).

La línea en el método que hace el trabajo es:

 ... 
var inst = this._constructComponent (
doConstruct,
publicProps,
publicContext,
updateQueue,
);
...
 ReactCompositeComponent@renderers/shared/stack/reconciler/ReactCompositeComponent.js 

en el que se llama al constructor de la App .

 ... 
constructor (accesorios) {
super (apoyos);
this.state = {
desc: 'inicio',
};
}
...
 // copiado desde el comienzo de este texto 

Entonces (lo ReactCompositeComponent[ins]._instance ), la App[ins] está configurada en la ReactCompositeComponent[ins]._instance y también se crea un back-link a través de ReactInstanceMap .

Esta es la instancia de App componente personalizado que responde a su this.setState(…) .

Otras operaciones incluyen: 1) App[ins].props reference ReactElement[1].props ; y 2) ReactCompositeComponent[ins]._mountOrder se establece en 2 debido a que ++ opera en la variable global nextMountID .

Es importante tener en cuenta que App[ins].render() es otro método de App que definimos al principio. A diferencia de TopLevelWrapper[ins].render() que devuelve una instancia concreta de ReactElement , App[ins].render() basa en React.createElement() en el momento en que se invoca. Vamos a visitar este método pronto.

Como este paso es muy similar al que inicializa ReactCompositeComponent[T] {post two }, no examinamos más el método mountComponent() es decir, mountComponent() ).

 mountComponent: function ( 
transacción,
hostParent,
hostContainerInfo,
contexto,
// scr: this ------> ReactCompositeComponent [T]
) {
 ... 
 this._mountOrder = nextMountID ++; // scr: --------------------> 2) 
 ... 
 var publicProps = this._currentElement.props; // scr: -----------> {child: ReactElement [1]} 
 ... 
 // scr: ---------------------------------------------- ---> crítico) 
var inst = this._constructComponent (
doConstruct,
publicProps,
publicContext,
updateQueue,
); // scr: ----------> llamar al constructor de TopLevelWrapper
 var renderedElement; 
 ... 
 // Deben configurarse en el constructor, pero como una conveniencia 
// para abstracciones de clase más simples, los configuramos después del hecho.
// scr: ---------------------------------------------- ----------> 1)
inst.props = publicProps; // scr: ----> {child: ReactElement [1]}
...
 // scr: ---------------------------------------------- -> crítico) 
this._instance = inst; // scr: ---------------------------------> vincula ReactCompositeComponent [T] a la instancia de TopLevelWrapper
 // Almacena una referencia de la instancia a la representación interna 
ReactInstanceMap.set (inst, this); // scr: ----------------------> vincula la instancia de TopLevelWrapper a ReactCompositeComponent [T]
 ... 
 var markup; 
if (inst.unstable_handleError) {// scr: -----------------------> falso, TopLevelWrapper.prototype.unstable_handleError no está definido
...
} else {
 // scr: ---------------------------------------------- ---> siguiente) 
markup = this.performInitialMount (// scr: una inicial al final?
renderedElement,
hostParent,
hostContainerInfo,
transacción,
contexto,
);
}
 ... 
 marcado de devolución; 
}
 ReactCompositeComponent@renderers/shared/stack/reconciler/ReactCompositeComponent.js 

`ReactCompositeComponent [ins] .performInitialMount ()` – crea un `ReactDOMComponent`

 ... 
| ~ mountComponentIntoNode () |
| -ReactReconciler.mountComponent () |
| -ReactCompositeComponent [T] .mountComponent () |
| -ReactCompositeComponent [T] .performInitialMount () mitad superior
| -ReactReconciler.mountComponent () |
/ * estamos aquí * / |
| -ReactCompositeComponent [ins] .mountComponent () |
| -this.performInitialMount () |
| -this._renderValidatedComponent () |
| -instantiateReactComponent () _ | _
| - ReactDOMComponent[6]. mountComponent () mitad inferior

Estamos aquí de nuevo:

 performInitialMount: function ( 
renderedElement,
hostParent,
hostContainerInfo,
transacción,
contexto)
{
var inst = this._instance;
 ... 
 if (inst.componentWillMount) { 
... // scr: no definimos componentWillMount () en la aplicación
}
 // Si no es un componente sin estado, ahora renderizamos 
if (renderedElement === undefined) {
renderedElement = this._renderValidatedComponent (); // scr:> 1)
}
 var nodeType = ReactNodeTypes.getType (renderedElement); // scr: -> el tipo es ReactNodeTypes.Host esta vez 
 this._renderedNodeType = nodeType; 
 var child = this._instantiateReactComponent (renderedElement, nodeType! == ReactNodeTypes.EMPTY / * shouldHaveDebugID * / 
); // scr: ---------------------------------------------- > 2)
 this._renderedComponent = child; 
 var markup = ReactReconciler.mountComponent (child, transaction, hostParent, hostContainerInfo, this._processChildContext (context), debugID); // scr: ---------------------------------------------- > 3) 
 ... // scr: código DEV 
 marcado de devolución; 
},
 ReactCompositeComponent@renderers/shared/stack/reconciler/ReactCompositeComponent.js 

Antes de que se ReactDOMComponent un ReactDOMComponent (sabemos que esta es la clase que maneja las operaciones DOM), se debe ReactElement el ReactElement dentro de la App[ins] . Para hacerlo, se llama a la App[ins].render() por la siguiente línea (en _renderValidatedComponent() ) {post two}

 ... 
renderedElement = this._renderValidatedComponent ();
...

Luego, los desencadenantes de App[ins].render()

1) Las llamadas en cascada de React.createElement()

Para comprender cómo se establece el árbol ReactElement , primero App.render() implementación de App.render() :

 render () { 
return React.createElement (// scr: -----------> 5)
'div',
{className: 'App'},
React.createElement (// scr: -----------> 3)
'div',
{className: 'App-header'},
React.createElement (// scr: -----------> 1)
'img',
{src: "main.jpg", className: 'App-logo', alt: 'logo'}
),
React.createElement (// scr: -----------> 2)
'h1',
nulo,
'' Welcom reacciona ''
)
),
React.createElement (// scr: -----------> 4)
'pag',
{className: 'App-intro'},
this.state.desc
)
);
}
 // copiado desde el comienzo de este texto 

En este fragmento de código también doy el orden de llamada de createElement() s que sigue un principio muy simple: los argumentos deben resolverse (con createElement() ) de izquierda a derecha antes de createElement() una función (de createElement() ).

Luego, podemos examinar la creación de cada ReactElement { post one }.

 React.createElement (// scr: --------------------------------> 1) 
'img',
{src: "main.jpg", className: 'App-logo', alt: 'logo'}
),

crea ReactElement[2] :

; y

 React.createElement (// scr: --------------------------------> 2) 
'h1',
nulo,
'Bienvenido a React'
)

crea ReactElement[3] :

(Ahora se resuelven los dos argumentos para 3).

; y

 React.createElement (// scr: -----------> 3) 
'div',
ReactElement[2] ,
ReactElement[3]
),

crea ReactElement[4] :

; y

 React.createElement (// scr: -----------> 4) 
'pag',
{className: 'App-intro'},
this.state.desc
)

crea ReactElement[5] :

(Ahora los argumentos para 5) están resueltos.)

; y

 return React.createElement (// scr: -----------> 5) 
'div',
{className: 'App'},
ReactElement[4] ,
ReactElement[5]
)

crea ReactElement[6] :

.

Combinados juntos obtuvimos el árbol de elementos al que se hace referencia por renderedElement :

2)` ReactCompositeComponent [ins] ._ instantiateReactComponent ()` – Create `ReactDOMComponent[6]`

La estructura de datos designada:

El árbol de elementos creado en el último paso se usa para crear ReactDOMComponent[6] en la siguiente línea (dentro de _instantiateReactComponent() ) { post two }

 var child = this._instantiateReactComponent ( 
renderedElement,
nodeType! == ReactNodeTypes.EMPTY / * shouldHaveDebugID * /,
);

3) Ahora ReactReconciler.mountComponent() llama al mountComponent() del ReactDOMComponent[6] y los procesos lógicos a la mitad inferior.

continuará…