Most of the people working in the frontend in one way or another came across a reactor. This JavaScript library, which helps to create cool interfaces, has gained great popularity in recent years. At the same time, not many people know how it works inside.
In this series of articles, we read the code and try to figure out what the packages that lie near the reactor under the hood are responsible for, what they are used for and how they work. The most basic ones that we use in the browser are react
, react-dom
, events
and react-reconciler
.
We will move in order and today we have an article about the react
package. Who cares what is in this package - go under the cat.
First of all, we will make a small example on the basis of which we will consider this package. Our gadget will look like this:
function App() { const [text, changeText] = React.useState('Initial'); return ( <div className="app"> <span>{text}</span> <input type="text" value={text} onInput={(e) => changeText(e.target.value)} /> </div> ); } ReactDOM.render( <App />, document.getElementById('root') ) ;
Let's take a quick look at this piece of code. Here we see a call to the hook via React.useState('Initial')
, a bit of JSX and a call to the render method so that it all gets to the page.
In fact, as many know, this is not the final code that the browser handles. Before he gets into execution, he will get caught, for example, by Babel. In this case, what the function returns will turn into the following:
return React.createElement( "div", { className: "app" }, React.createElement("span", null, text), React.createElement("input", { type: "text", value: text, onInput: function onInput(e) { return changeText(e.target.value); } }) );
Who cares to experiment and see what your babel code turns into - babel repl .
So we got a lot of calls to React.createElement()
and time to see what this function does. We describe in words (or you can look at the file - ReactElement.js ).
First of all, it checks if we have props (in the code, the object with the props that we passed is called config
).
Next, we check if we have key
and ref
props which are not undefined
, and save them, if any.
if (hasValidKey(config)) { key = '' + config.key; }
An interesting point is that config.key
is config.key
to a string, which means that you can transfer any data type as a key, as config.key
as it implements the .toString()
or .valueOf()
method and returns a unique value for a particular set.
Next are the following steps:
children
field there, if we passed them not by a PSM, but as a nested element;defaultProps
for those properties that we have not previously defined.When we have prepared all the data, we call an internal function that creates an object that describes our component. This object looks like this:
{ // This tag allows us to uniquely identify this as a React Element $$typeof: REACT_ELEMENT_TYPE, // Symbol // Built-in properties that belong on the element type: type, key: key, ref: ref, props: props, // Record the component responsible for creating this element. _owner: owner, }
Here we have the $$typeof
property, which is a symbol, so we will not be able to slip what object we have.
The type
property stores the type of the element being created. In the case of our example, this will be the App()
function and the strings 'div'
, 'span'
and 'input'
.
The key
property will contain the same key that causes the warnings to fly to the console.
Props will contain what we passed, children
and what was specified in defaultProps
. The _owner
property _owner
necessary for correct work with ref
.
If we translate into our example, the result of React.createElement(App, null)
look like this:
{ $$typeof: REACT_ELEMENT_TYPE, type: App, key: null, ref: null, props: {}, _owner: null, }
In addition, in dev mode, we will have an additional field that will be used to display a beautiful stack with the file name and line:
_source: { fileName: "/Users/appleseed/react-example/src/index.js", lineNumber: 7 }
Let's summarize a little of what we saw above. The react
package acts as a translator between us and the rest of the packages that work further on our application, translating our calls into words that are understandable, for example, to a reconsiler.
In the version of the reactor 16.8 appeared hooks. What is it and how to use it you can read the link , and now we will take a look at what lies in the react
package.
In fact, there is not much to say here. In essence, a package is a facade through which our calls go to internal entities.
So, useState
is nothing more than two lines of code:
export function useState<S>(initialState: (() => S) | S) { const dispatcher = resolveDispatcher(); return dispatcher.useState(initialState); }
The rest of the hooks look almost identical. Here we get the current dispatcher, which is an object and contains fields, for example, useState
. This dispatcher changes depending on whether our first render is now or we just want to update the component.
The actual implementation of the hooks is stored in the react-reconciler
package, which we will discuss in one of the following articles.
One more thing. After reading this article, you can understand why we always import the package reak, even if we do not directly use it. This is necessary so that after Babel digests our jsx, we have the React
variable.
The guys from the team reacted to this (and not only this) and are now working on replacing createElement
.
Trying to explain in two words: there is a desire to replace the current method of creating elements with two - jsx
and jsxs
. This is necessary for several reasons:
createElement
works. He constantly copies props and adds a children
field to the object, into which he saves children, which we passed as function arguments (3 arguments and more). Now it is proposed to do this at the stage of converting jsx
to javascript
calls, because creating an element is a frequently called function and performing the modification of the props at runtime every time is not free;React
object and import only specific functions ( import { jsx } from 'react'
, for example) and, accordingly, have the opportunity not to add to the assembly what we do not use. In addition, it is not necessary to rezolvit the createElement
field of the React
object every time, because this is also not free;key
from the props and forward it further. Now it is proposed at the transpiling stage to take the key
from jsx
and pass it by the third parameter to the function of creating the element.You can read more here . The react
package now has methods jsx
and jsxs
. If you want to play around with it, you can clone the reactor repository, set the enableJSXTransformAPI
flag to true
in the ReactFeatureFlags.js
shared
package file ReactFeatureFlags.js
set your version of the reactor ( yarn build
) with the new API enabled.
At this react
I’ll finish today's story about the react
package and next time we will talk about how the react-dom
package uses what it creates react
and which methods and how it implements.
Thank you for reading to the end!
Source: https://habr.com/ru/post/448122/
All Articles