Concept paper
There are a lot of concepts in React. The most common ones we deal with are JSX, virtual DOM, Fiber, React Compenent, React Element, etc. But we may never have a deep understanding of the relationship between them?
- Are JSX and Fiber the same thing?
- Are JSX and the virtual DOM the same thing?
- React Compenent, React Element what is the relationship between them?
Let’s start with these basic concepts in this chapter.
JSX
As React developers we use JSX a lot. If you’re not familiar with JSX, please read the description on the React website. JSX is compiled to React. CreateElement when compiled with Babel
function App() {
// JSX
return (
<div>
<p>Hellp world</p>
</div>
);
}
function App() {
// react.createElement
return React.createElement("div".null, React.createElement("p".null."You clicked ", count, " times"));
}
Copy the code
This is why every file must be realistically imported before React17. Otherwise, the React variable will be undefined at runtime. There was no need to display React after React17.
JSX will eventually compile to react. CreateElement. What does react. CreateElement do?
react.createElement
export function createElement(type, config, children) {
let propName;
const props = {};
let key = null;
let ref = null;
// Can be ignored
let self = null;
let source = null;
if(config ! =null) {
if (hasValidRef(config)) {
ref = config.ref;
if(__DEV__) { warnIfStringRefCannotBeAutoConverted(config); }}if (hasValidKey(config)) {
key = ' ' + config.key;
}
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
for (propName in config) {
if( hasOwnProperty.call(config, propName) && ! RESERVED_PROPS.hasOwnProperty(propName) ) { props[propName] = config[propName]; }}}const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
props.children = childArray;
}
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) { props[propName] = defaultProps[propName]; }}}return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
Copy the code
- Type: There are three types: string, function, and class.
- String represents the DOM of the current type, such as ‘div’.
- Function: represents function component
- Class: indicates class compnent
- Config: JSX binding property
- Children: Children in JSX
The react.createElement function does a few things:
- Extract key and ref from config for separate processing
- Assign the properties of config to props except key, ref, self, and source
- If type (typically class Component) has the defaultProps property, handle the defaultProps property
- Call ReactElement
From the above code, we can see that react.createElement will eventually call ReactElement.
ReactElement
const ReactElement = function(type, key, ref, self, source, owner, props) {
const element = {
$$typeof: REACT_ELEMENT_TYPE,
type: type,
key: key,
ref: ref,
props: props,
_owner: owner,
};
return element;
};
Copy the code
ReactElement returns an object whose $$typeof is assigned to REACT_ELEMENT_TYPE. We all know that React has a function called isValidElement to check whether it is a Reacrt Element:
export function isValidElement(object) {
return (
typeof object === 'object'&& object ! = =null &&
object.$$typeof === REACT_ELEMENT_TYPE
);
}
Copy the code
Object.$$typeof === REACT_ELEMENT_TYPE {React Element} React Elements are the result of the JSX runtime, which means that every JSX will eventually be converted to React Elements.
What about React Compenent?
React Compenent
React has two types of Compenent: ClassCompenent and FunctionCompenet. Including ClassCompenent inheritance React.Com ponent/React. PureComponent, so now let’s look at two objects:
// Component
function Component(props, context, updater) {
this.props = props;
this.context = context;
this.refs = emptyObject;
this.updater = updater || ReactNoopUpdateQueue;
}
Component.prototype.isReactComponent = {};
Component.prototype.setState = function(partialState, callback) {
invariant(
typeof partialState === 'object' ||
typeof partialState === 'function' ||
partialState == null.'setState(...) : takes an object of state variables to update or a ' +
'function which returns an object of state variables.',);this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
Component.prototype.forceUpdate = function(callback) {
this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};
// PureComponent
function PureComponent(props, context, updater) {
this.props = props;
this.context = context;
this.refs = emptyObject;
this.updater = updater || ReactNoopUpdateQueue;
}
const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());
pureComponentPrototype.constructor = PureComponent;
Object.assign(pureComponentPrototype, Component.prototype);
During the update phase, isPureReactComponent is true, and if true, props and state are compared to determine whether to update
pureComponentPrototype.isPureReactComponent = true;
export {Component, PureComponent};
Copy the code
The Component object contains only props, context, refs, updater, isReactComponent, which is used to determine whether it is ClassCompenent. And setState and forceUpdate functions to trigger updates. The PureComponent object, on the other hand, inherits Component with an isPureReactComponent property. This property, if true, compares props and state to determine whether the Component is updated before Compenent updates it.
if (ctor.prototype && ctor.prototype.isPureReactComponent) {
return(! shallowEqual(oldProps, newProps) || ! shallowEqual(oldState, newState)); }Copy the code
React. createElement we know that both ClassCompenent and FunctionCompenent will eventually be mounted to the type of the ReactElement object, forming the following object:
{
$$typeof: Symbol(react.element),
key: null.props: {},
ref: null.type: ƒ App (),_owner: null._store: {validated: false},
_self: null._source: null
}
Copy the code
The React Element does not contain schedule, schedule, and render information, which should be stored in Fiber.
Fiber
export type Fiber = {
// The current Fiber type, such as FunctionComponent
tag: WorkTag
// key
key: null | string.ClassComponent FunctionCompoent element.tagName, which is the same type as the following
elementType: any.// Same as elementType above, but with LazyComponent, type=null
type: any.// satteNode has four cases
// 1. functionCoponent stateNode = null
// 2. classComponent stateNode = instance
// 3. The stateNode is the current Dom instance
// 4. rootFiber stateNode = fiberRoot
stateNode: any.// parent Fibers
return: Fiber | null.// The first child of the current fiber
child: Fiber | null.// Sibling of the current node
sibling: Fiber | null.// The location of the current node
index: number.// ref
ref:
| null
| (((handle: mixed) = > void) & {_stringRef:?string. }) | RefObject,// Props to and from the parent node
/ / object
pendingProps: any.// Save this update for next update and assign after beiginWork is complete
memoizedProps: any.// Attribute update queue
updateQueue: mixed,
// Save this status
memoizedState: any.dependencies: Dependencies | null./** NoMode = 0b00000; StrictMode = 0b00001; BlockingMode = 0b00010; ConcurrentMode = 0b00100; ProfileMode = 0b01000; DebugTracingMode = 0b10000; * * /
mode: TypeOfMode,
/ / Effect
flags: Flags,
subtreeFlags: Flags,
deletions: Array<Fiber> | null.// Next effect
nextEffect: Fiber | null.// The first effect of this update
firstEffect: Fiber | null.// Update the last effec
lastEffect: Fiber | null.// Priority of the Lane model
lanes: Lanes,
childLanes: Lanes,
// In a double cache, a node points to another tree
alternate: Fiber | null.// The time taken to render the current node and its descendants is calculated only when enableProfilerTimer is enabledactualDuration? :number.// indicates the time to start renderingactualStartTime? :number.// The render timeselfBaseDuration? :number.// Total time, calculated in commite phasetreeBaseDuration? :number.// Conceptual aliases
// workInProgress : Fiber -> alternate The alternate used for reuse happens
// to be the same as work in progress.
// __DEV__ only_debugID? :number, _debugSource? : Source |null, _debugOwner? : Fiber |null, _debugIsCurrentlyTiming? :boolean, _debugNeedsRemount? :boolean.// Used to verify that the order of hooks does not change between renders._debugHookTypes? :Array<HookType> | null|};Copy the code
React In the mount phase, the JSX object is calculated as Fiber object and stored in The pendingProps. After beignWork, the JSX object is also stored in memoizedProps and exported to the child nodes.
When Reconciler is updated, the attributes on the JSX and Fiber are compared to determine whether the current node needs to be updated. The comparison result is marked on the flag.
Now that we know the main data structures in the render phase, the next chapter will begin in earnest
Part of this article is react.iamkasong.com/
React source code