React这个包目录
1.React.js是入口 外部能使用的api全在这里导出
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// React/React.js
import ReactVersion from 'shared/ReactVersion';
import {
REACT_CONCURRENT_MODE_TYPE,
REACT_FRAGMENT_TYPE,
REACT_PROFILER_TYPE,
REACT_STRICT_MODE_TYPE,
REACT_SUSPENSE_TYPE,
} from 'shared/ReactSymbols';
import {Component, PureComponent} from './ReactBaseClasses';
import {createRef} from './ReactCreateRef';
import {forEach, map, count, toArray, only} from './ReactChildren';
import {
createElement,
createFactory,
cloneElement,
isValidElement,
} from './ReactElement';
import {createContext} from './ReactContext';
import {lazy} from './ReactLazy';
import forwardRef from './forwardRef';
import memo from './memo';
import {
createElementWithValidation,
createFactoryWithValidation,
cloneElementWithValidation,
} from './ReactElementValidator';
import ReactSharedInternals from './ReactSharedInternals';
import {enableStableConcurrentModeAPIs} from 'shared/ReactFeatureFlags';
const React = {
Children: {
map,
forEach,
count,
toArray,
only,
},
createRef,
Component,
PureComponent,
createContext,
forwardRef,
lazy,
memo,
//原生组件
Fragment: REACT_FRAGMENT_TYPE,
StrictMode: REACT_STRICT_MODE_TYPE,
Suspense: REACT_SUSPENSE_TYPE,
createElement: __DEV__ ? createElementWithValidation : createElement,
cloneElement: __DEV__ ? cloneElementWithValidation : cloneElement,
createFactory: __DEV__ ? createFactoryWithValidation : createFactory,
isValidElement: isValidElement,
version: ReactVersion,
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals,
};
if (enableStableConcurrentModeAPIs) {
React.ConcurrentMode = REACT_CONCURRENT_MODE_TYPE;
React.Profiler = REACT_PROFILER_TYPE;
} else {
React.unstable_ConcurrentMode = REACT_CONCURRENT_MODE_TYPE;
React.unstable_Profiler = REACT_PROFILER_TYPE;
}
export default React;
2.ReactElement.js
// React/ReactElement.js
const ReactElement = function(type, key, ref, self, source, owner, props) {
const element = {
// This tag allows us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE, //!标识是React内部element元素 对比React.createProtol 这个 $$typeof 是React的_protoltype_
// 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,
};
return element
}
//React/ReactElement.js
export function createElement(type, config, children) {
let propName;
// Reserved names are extracted
const props = {};
let key = null;
let ref = null;
let self = null;
let source = null;
//!看看有没有合理的ref和key
if (config != null) {
if (hasValidRef(config)) {
ref = config.ref;
}
if (hasValidKey(config)) {
key = '' + config.key;
}
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
// Remaining properties are added to a new props object
//! 再看是不是内嵌的props key/ref/__self/__source 这些都不该出现在this.props里面 放到props这个新对象里面
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
}
// Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
//!处理children 因为传入的参数可以多个除了前两个是type,config 后面都是children 剩下的参数转成children数组放到props上
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];
}
if (__DEV__) {
if (Object.freeze) {
Object.freeze(childArray);
}
}
props.children = childArray;
}
//!看看类组件有没有默认的props 也放到props对象来
//class Cmp extends React.Component Cmp.defaulrProps = {key:value} 相当于给类设置静态属性 有则不设置 没有则设置
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
//!Dev不管
if (__DEV__) {
if (key || ref) {
const displayName =
typeof type === 'function'
? type.displayName || type.name || 'Unknown'
: type;
if (key) {
defineKeyPropWarningGetter(props, displayName);
}
if (ref) {
defineRefPropWarningGetter(props, displayName);
}
}
}
//!返回一个React元素
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
3.ReactBaseClasses.js
component/pureComponent实现
//React/ReactBaseClasses.js
function Component(props, context, updater) {
this.props = props;
this.context = context;
// If a component has string refs, we will assign a different object later.
this.refs = emptyObject; //string形式的ref在这里获取
// We initialize the default updater but the real one gets injected by the
// renderer.
this.updater = updater || ReactNoopUpdateQueue;
}
Component.prototype.isReactComponent = {};
//!类上面的setState原型方法 partialState是对象或者function都行
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.',
);
//!调用初始化时候传入的updater的enqueueSetState函数去更新数据视图 这部分函数是在react-dom里(和平台有关 因为要更新视图) 这就是为什么要传入这门一个更新对象进来 为了区分不同平台的更新函数
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
/**
* Forces an update. This should only be invoked when it is known with
* certainty that we are **not** in a DOM transaction.
*
* You may want to call this when you know that some deeper aspect of the
* component's state has changed but `setState` was not called.
*
* This will not invoke `shouldComponentUpdate`, but it will invoke
* `componentWillUpdate` and `componentDidUpdate`.
*
* @param {?function} callback Called after update is complete.
* @final
* @protected
*/
//!原型方法 去调用pdater对象.enqueueForceUpdate方法去强制刷新组件
Component.prototype.forceUpdate = function(callback) {
this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};
//React/ReactBaseClasses.js
function ComponentDummy() {}
ComponentDummy.prototype = Component.prototype;
/**
* Convenience component with default shallow equality check for sCU.
*/
function PureComponent(props, context, updater) {
this.props = props;
this.context = context;
// If a component has string refs, we will assign a different object later.
this.refs = emptyObject;
this.updater = updater || ReactNoopUpdateQueue;
}
const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy()); //!实现啦继承而已
pureComponentPrototype.constructor = PureComponent;
// Avoid an extra prototype jump for these methods.
Object.assign(pureComponentPrototype, Component.prototype);
pureComponentPrototype.isPureReactComponent = true;//!就加了一个属性标识 继承这个pure类都有这个属性 后续更新时候判断属性是否变化再去更新
export {Component, PureComponent};
4.ref讲解
ref 用来获取节点具体实例,有三种方法使用ref 只有类组件和原生dom标签才能有ref,因为ref拿到的是具体实例,而函数是没有具体实例的(因为没有this),类/原生dom是有实例(比如类是new Cmp()这个实例,dom就是这个dom元素),函数组件没有ref,要想使用这三个方法必须用forwardRef拿到ref。
三种ref:string ref/函数re/f对象ref
//ref三种方式
import React, {Component} from "react";
class Myref extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.objRef= React.createRef() //!只有这个方式的ref是要通过.current形式获取实例的 其他方式都是直接是实例
this.FunRef = null
}
componentDidMount(){
setTimeout(()=>{
this.objRef.current.innerHTML = 'bang'
this.refs.stringRef.innerHTML = 'bang'
this.FunRef.innerHTML = 'bang'
},3000)
}
render() {
return (
<>
<div ref={this.objRef}>objref</div>
<div ref="stringRef">stringRef</div>
<div ref={ele=>{this.FunRef=ele}}>funRef</div>
</>
);
}
}
export default Myref;
这里是createRef形式创建ref 里面就是个对象 不过存在current属性里
//react/ReactCreateRef
export function createRef(): RefObject {
const refObject = {
current: null,
};
if (__DEV__) {
Object.seal(refObject);
}
return refObject;
}
5.forwardRef讲解
React.forwardRef 为了解决函数组件无法用ref的困局 ref传到函数组件内部的真正要获取实例的dom元素上去
//demo用例
import React, {Component} from "react";
class MyforwardRef extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.ref = React.createRef()
}
componentDidMount(){
setTimeout(()=>{
this.ref.current.value = 'bang'
},3000)
}
render() {
return (
<Child ref={this.ref}></Child>
);
}
}
//!ref不属于props
let Child= React.forwardRef((props,ref)=>{
//!ref传到想要的dom上去
return <input ref = {ref}></input>
})
export default MyforwardRef;
//React/forwardRef
//! render就是传进来的函数组件
export default function forwardRef<Props, ElementType: React$ElementType>(
render: (props: Props, ref: React$Ref<ElementType>) => React$Node,
) {
if (__DEV__) {
if (typeof render !== 'function') {
warningWithoutStack(
false,
'forwardRef requires a render function but was given %s.',
render === null ? 'null' : typeof render,
);
} else {
warningWithoutStack(
// Do not warn for 0 arguments because it could be due to usage of the 'arguments' object
render.length === 0 || render.length === 2,
'forwardRef render functions accept exactly two parameters: props and ref. %s',
render.length === 1
? 'Did you forget to use the ref parameter?'
: 'Any additional parameter will be undefined.',
);
}
if (render != null) {
warningWithoutStack(
render.defaultProps == null && render.propTypes == null,
'forwardRef render functions do not support propTypes or defaultProps. ' +
'Did you accidentally pass a React component?',
);
}
}
//返回一个对象
return {
$$typeof: REACT_FORWARD_REF_TYPE, //!给一个特定的ref_type
render,//! render就是传进来的函数组件
};
}
注意一个东西
let Child= React.forwardRef((props,ref)=>{
//!ref传到想要的dom上去
return <input ref = {ref}></input>
})
/*此时child是这个对象:{
$$typeof: REACT_FORWARD_REF_TYPE, //!给一个特定的ref_type
render,//! render就是传进来的函数组件
};
然后*/
<Child ref={this.ref}></Child>//这个代码会执行createElement
const element = {
// This tag allows us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE, //!标识是React内部element元素 对比React.createProtol 这个 $$typeof 是React的_protoltype_
// Built-in properties that belong on the element
type: type, //这个type就是{$$typeof: REACT_FORWARD_REF_TYPE, _typerender}对象
};
}
//而整体元素的$$typeof还是REACT_ELEMENT_TYPE
6.context讲解
React.createContext()
//使用
let userContext = React.createContext()
let userProvider = userContext.Provider
let userConsumer = userContext.Consumer
//React/ReactContextjs
//!defaultValue默认值
//!calculateChangedBits 计算新老context的变化 后面跟新时候用
export function createContext<T>(
defaultValue: T,
calculateChangedBits: ?(a: T, b: T) => number,
): ReactContext<T> {
if (calculateChangedBits === undefined) {
calculateChangedBits = null;
} else {
if (__DEV__) {
warningWithoutStack(
calculateChangedBits === null ||
typeof calculateChangedBits === 'function',
'createContext: Expected the optional second argument to be a ' +
'function. Instead received: %s',
calculateChangedBits,
);
}
}
//!创建一个context对象 type为context_type 这里也是和ref一样 REACT_CONTEXT_TYPE,也是作为createElement元素的的type属性存储的 createElement元素的$$typeof一直是element
const context: ReactContext<T> = {
$$typeof: REACT_CONTEXT_TYPE,
_calculateChangedBits: calculateChangedBits, //!后面更新时候currentValue用
// As a workaround to support multiple concurrent renderers, we categorize
// some renderers as primary and others as secondary. We only expect
// there to be two concurrent renderers at most: React Native (primary) and
// Fabric (secondary); React DOM (primary) and React ART (secondary).
// Secondary renderers store their context values on separate fields.
_currentValue: defaultValue,//!记录最新的context值 这两个只是平台不一样
_currentValue2: defaultValue,
// These are circular
Provider: (null: any),
Consumer: (null: any),
};
//!provider对象
context.Provider = {
$$typeof: REACT_PROVIDER_TYPE,
_context: context,
};
let hasWarnedAboutUsingNestedContextConsumers = false;
let hasWarnedAboutUsingConsumerProvider = false;
if (__DEV__) {
// A separate object, but proxies back to the original context object for
// backwards compatibility. It has a different $$typeof, so we can properly
// warn for the incorrect usage of Context as a Consumer.
//!Consumer对象 有context属性 所以可以拿到context对象里的最新的_currentValue
const Consumer = {
$$typeof: REACT_CONTEXT_TYPE,
_context: context,
_calculateChangedBits: context._calculateChangedBits,//!后面更新时候currentValue用
};
// $FlowFixMe: Flow complains about not setting a value, which is intentional here
//!拿到最新的provider consumber _currentValue(当前值)
Object.defineProperties(Consumer, {
Provider: {
get() {
if (!hasWarnedAboutUsingConsumerProvider) {
hasWarnedAboutUsingConsumerProvider = true;
warning(
false,
'Rendering <Context.Consumer.Provider> is not supported and will be removed in ' +
'a future major release. Did you mean to render <Context.Provider> instead?',
);
}
return context.Provider;
},
set(_Provider) {
context.Provider = _Provider;
},
},
_currentValue: {
get() {
return context._currentValue;
},
set(_currentValue) {
context._currentValue = _currentValue;
},
},
_currentValue2: {
get() {
return context._currentValue2;
},
set(_currentValue2) {
context._currentValue2 = _currentValue2;
},
},
Consumer: {
get() {
if (!hasWarnedAboutUsingNestedContextConsumers) {
hasWarnedAboutUsingNestedContextConsumers = true;
warning(
false,
'Rendering <Context.Consumer.Consumer> is not supported and will be removed in ' +
'a future major release. Did you mean to render <Context.Consumer> instead?',
);
}
return context.Consumer;
},
},
});
// $FlowFixMe: Flow complains about missing properties because it doesn't understand defineProperty
context.Consumer = Consumer;
} else {
context.Consumer = context;
}
if (__DEV__) {
context._currentRenderer = null;
context._currentRenderer2 = null;
}
return context;
}
7.concurrentMode 任务调度
任务过程可以优先级区分 优先级高的执行而且可以中断,实现任务调度,js单线程,如果没有任务调度,那么在更新时候后在执行动画会卡顿
//用例
import React, { ConcurrentMode } from 'react'
import { flushSync } from 'react-dom'//!flushSync:把传入的函数任务提高到最高的优先级
import './index.css'
class Parent extends React.Component {
state = {
async: true,
num: 1,
length: 2000,
}
componentDidMount() {
this.interval = setInterval(() => {
this.updateNum()
}, 200)
}
componentWillUnmount() {
// 别忘了清除interval
if (this.interval) {
clearInterval(this.interval)
}
}
updateNum() {
const newNum = this.state.num === 3 ? 0 : this.state.num + 1
//!动画和updateNum一个优先级 这样就同步进行 数字变化没那么快
if (this.state.async) {
this.setState({
num: newNum,
})
} else {
//!让setState变成优先级高的任务 这样数字变化就不会卡顿 但是动画可能会卡
flushSync(() => {
this.setState({
num: newNum,
})
})
}
}
render() {
const children = []
const { length, num, async } = this.state
for (let i = 0; i < length; i++) {
children.push(
<div className="item" key={i}>
{num}
</div>,
)
}
return (
<div className="main">
async:{' '}
<input
type="checkbox"
checked={async}
onChange={() => flushSync(() => this.setState({ async: !async }))}
/>
<div className="wrapper">{children}</div>
</div>
)
}
export default () => (
<ConcurrentMode>
<Parent />
</ConcurrentMode>
)
}
//React/React.js
if (enableStableConcurrentModeAPIs) {
React.ConcurrentMode = REACT_CONCURRENT_MODE_TYPE;//!ConcurrentMode这个组件就是=REACT_CONCURRENT_MODE_TYPE就是个sysmbol一个标志:<ConcurrentMode><Parent /></ConcurrentMode>那他是如何承载children的呢
React.Profiler = REACT_PROFILER_TYPE;
} else {
React.unstable_ConcurrentMode = REACT_CONCURRENT_MODE_TYPE;
React.unstable_Profiler = REACT_PROFILER_TYPE;
}
8.suspense
原理:Suspense 内部组件会throw一个promise 当promise没有resolve前会显示fallback的内容 当内部的promise的状态resolve后会重新刷新组件,渲染真正的内部组件内容
使用:这里一般就可以用于组件内部异步加载数据 通常和lazy一起用 lazy函数在组件没加载完之前抛出一个promise 加载完之后reslove这个promise
//LazyComp是异步加载的一个组件 当该异步组件加载完成后才会显示组件内容 没加载完成前显示fallback内容
import React, { Suspense, lazy } from 'react'
const LazyComp = lazy(() => import('./lazy.js'))
let data = ''
let promise = ''
function requestData() {
if (data) return data
if (promise) throw promise
promise = new Promise(resolve => {
setTimeout(() => {
data = 'Data resolved'
resolve()
}, 2000)
})
throw promise
}
function SuspenseComp() {
const data = requestData()
return <p>{data}</p>
}
export default () => (
<Suspense fallback="loading data">
<SuspenseComp />
<LazyComp />
</Suspense>
)
Suspense: REACT_SUSPENSE_TYPE, //还是个sysmbol
//React/ReactLazy.js
import {REACT_LAZY_TYPE} from 'shared/ReactSymbols';
//!接受一个函数 这个函数返回Thenable<T, R>对象即.then是个函数 一般就是promise
//!返回一个LazyComponent对象 这个对象的 $$typeof是REACT_LAZY_TYPE和ref和context一样是createElementd的type 而不是$$typr 因为jsx都会调用createElement函数 而标签就是当作type传进去
//!_status 记录这个Thenable的状态(一般就是Promise的状态)
export function lazy<T, R>(ctor: () => Thenable<T, R>): LazyComponent<T> {
return {
$$typeof: REACT_LAZY_TYPE,
_ctor: ctor,
// React uses these fields to store the result.
_status: -1,//!_status 记录这个Thenable的状态(一般就是Promise的状态)
_result: null,//!_result 记录这个promise的resolve结果 即lazy加载的组件
};
}
所以这个_status-1的时候就抛出一个promise让他执行fallback 后面加载成功的时候在去返回result 这部分逻辑应该是在createElement的判断
type.$$typeof=== REACT_LAZY_TYPE 时候做处理
9.hooks
//打包后的文件里
if (enableHooks) {
React.useCallback = useCallback;
React.useContext = useContext;
React.useEffect = useEffect;
React.useImperativeMethods = useImperativeMethods;
React.useLayoutEffect = useLayoutEffect;
React.useMemo = useMemo;
React.useMutationEffect = useMutationEffect;
React.useReducer = useReducer;
React.useRef = useRef;
React.useState = useState;
}
var ReactCurrentOwner = {
/**
* @internal
* @type {ReactComponent}
*/
current: null,//!当前的节点实例
currentDispatcher: null //!实例对应的currentDispatcher对象 和平台有关 后续在react-dom里面传进来 上面有useState等方法
};
function resolveDispatcher() {
var dispatcher = ReactCurrentOwner.currentDispatcher;//!ReactCurrentOwner是个全局的类 这个设置得在react-dom里面传递一个实例进去
!(dispatcher !== null) ? invariant(false, 'Hooks can only be called inside the body of a function component.') : void 0;
return dispatcher;
}
function useContext(Context, observedBits) {
var dispatcher = resolveDispatcher();
{
// TODO: add a more generic warning for invalid values.
if (Context._context !== undefined) {
var realContext = Context._context;
// Don't deduplicate because this legitimately causes bugs
// and nobody should be using this in existing code.
if (realContext.Consumer === Context) {
warning$1(false, 'Calling useContext(Context.Consumer) is not supported, may cause bugs, and will be ' + 'removed in a future major release. Did you mean to call useContext(Context) instead?');
} else if (realContext.Provider === Context) {
warning$1(false, 'Calling useContext(Context.Provider) is not supported. ' + 'Did you mean to call useContext(Context) instead?');
}
}
}
return dispatcher.useContext(Context, observedBits);
}
function useState(initialState) {
var dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
function useReducer(reducer, initialState, initialAction) {
var dispatcher = resolveDispatcher();
return dispatcher.useReducer(reducer, initialState, initialAction);
}
function useRef(initialValue) {
var dispatcher = resolveDispatcher();
return dispatcher.useRef(initialValue);
}
function useEffect(create, inputs) {
var dispatcher = resolveDispatcher();
return dispatcher.useEffect(create, inputs);
}
function useMutationEffect(create, inputs) {
var dispatcher = resolveDispatcher();
return dispatcher.useMutationEffect(create, inputs);
}
function useLayoutEffect(create, inputs) {
var dispatcher = resolveDispatcher();
return dispatcher.useLayoutEffect(create, inputs);
}
function useCallback(callback, inputs) {
var dispatcher = resolveDispatcher();
return dispatcher.useCallback(callback, inputs);
}
function useMemo(create, inputs) {
var dispatcher = resolveDispatcher();
return dispatcher.useMemo(create, inputs);
}
function useImperativeMethods(ref, create, inputs) {
var dispatcher = resolveDispatcher();
return dispatcher.useImperativeMethods(ref, create, inputs);
}
10.Children
React.children 操作子元素 是个对象 有map等方法
Children: {
map,
forEach,
count,
toArray,
only,
},
React.children.map 最后返回的一定是一维数组 即[c1,c1,c1,c2,c2,c2]不会嵌套
console.log(React.Children.map(props.children, c => [c, [c, c]]))
Array(6)
0: {$$typeof: Symbol(react.element), type: "span", key: ".0/.0", ref: null, props: {…}, …}
1: {$$typeof: Symbol(react.element), type: "span", key: ".0/.1:0", ref: null, props: {…}, …}
2: {$$typeof: Symbol(react.element), type: "span", key: ".0/.1:1", ref: null, props: {…}, …}
3: {$$typeof: Symbol(react.element), type: "span", key: ".1/.0", ref: null, props: {…}, …}
4: {$$typeof: Symbol(react.element), type: "span", key: ".1/.1:0", ref: null, props: {…}, …}
5: {$$typeof: Symbol(react.element), type: "span", key: ".1/.1:1", ref: null, props: {…}, …}
length: 6
__proto__: Array(0)
React.children.map就是调用mapchildren的流程
流程
function mapChildren(children, func, context) {
if (children == null) {
return children;
}
const result = [];
//!children 子元素 result结果数组 func就是我们传入的回调函数 context不管
mapIntoWithKeyPrefixInternal(children, result, null, func, context);
return result;
}
//!children 子元素 result结果数组 func就是我们传入的回调函数 context不管
function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
let escapedPrefix = '';
if (prefix != null) {
escapedPrefix = escapeUserProvidedKey(prefix) + '/';
}
//!获取TraverseContext对象
const traverseContext = getPooledTraverseContext(
array,
escapedPrefix,//!就是处理元素的key
func,//!回调函数
context,
);
//!children子元素 mapSingleChildIntoContext内置函数 traverseContext pool里的context
traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
releaseTraverseContext(traverseContext);
}
const POOL_SIZE = 10;
const traverseContextPool = [];
function getPooledTraverseContext(
mapResult,
keyPrefix,
mapFunction,
mapContext,
) {
//!traverseContextPool全局变量 这里面如果有只是有这个属性 这个属性但是为Null 减少的只是创建和删除属性的开销
if (traverseContextPool.length) {
const traverseContext = traverseContextPool.pop();
traverseContext.result = mapResult;
traverseContext.keyPrefix = keyPrefix;
traverseContext.func = mapFunction;
traverseContext.context = mapContext;
traverseContext.count = 0;
return traverseContext;
} else {
return {
result: mapResult,
keyPrefix: keyPrefix,
func: mapFunction,
context: mapContext,
count: 0,
};
}
}
function traverseAllChildren(children, callback, traverseContext) {
if (children == null) {
return 0;
}
return traverseAllChildrenImpl(children, '', callback, traverseContext);
}
//!bookKeeping 就是Pool中的context对象 child就是子元素单个节点 childKey子元素的key
function mapSingleChildIntoContext(bookKeeping, child, childKey) {
const {result, keyPrefix, func, context} = bookKeeping;
let mappedChild = func.call(context, child, bookKeeping.count++);//!func就是传入的回调函数
if (Array.isArray(mappedChild)) { //!判断回调函数是不是个数组 c=>[c,c]
//!这里又会向pool里面添加context 所以数组嵌套层数可以采用这个来减少内存
mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, c => c);//!继续递归调用这个数组但是回调函数不是之前的回调 c=>[c,c] 而是c=>c返回自身的函数
} else if (mappedChild != null) {
if (isValidElement(mappedChild)) {//!单个元素 判断是不是合法的react-element
mappedChild = cloneAndReplaceKey(//!修改下key 其他属性不变
mappedChild,
// Keep both the (mapped) and old keys if they differ, just as
// traverseAllChildren used to do for objects as children
keyPrefix +
(mappedChild.key && (!child || child.key !== mappedChild.key)
? escapeUserProvidedKey(mappedChild.key) + '/'
: '') +
childKey,
);
}
result.push(mappedChild);//!最终单个元素进入结果数组
}
}
function traverseAllChildrenImpl(
children,
nameSoFar,
callback,
traverseContext,
) {
const type = typeof children;
if (type === 'undefined' || type === 'boolean') {
// All of the above are perceived as null.
children = null;
}
let invokeCallback = false;
if (children === null) {
invokeCallback = true;
} else {
switch (type) {
case 'string':
case 'number':
invokeCallback = true;
break;
case 'object':
switch (children.$$typeof) {
case REACT_ELEMENT_TYPE:
case REACT_PORTAL_TYPE:
invokeCallback = true;
}
}
}
//!children是单个节点直接调用callback callback就是maoSingleChildToContxt这个函数
if (invokeCallback) {
callback(
traverseContext,
children,
// If it's the only child, treat the name as if it was wrapped in an array
// so that it's consistent if the number of children grows.
nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar,
);
return 1;
}
let child;
let nextName;
let subtreeCount = 0; // Count of children found in the current subtree.
const nextNamePrefix =
nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
//!children是数组
if (Array.isArray(children)) {
for (let i = 0; i < children.length; i++) {
child = children[i];
nextName = nextNamePrefix + getComponentKey(child, i);
//!遍历递归每个子节点
subtreeCount += traverseAllChildrenImpl(
child,
nextName,//!子元素key值处理
callback,
traverseContext,
);
}
} else {
const iteratorFn = getIteratorFn(children);
if (typeof iteratorFn === 'function') {
if (__DEV__) {
// Warn about using Maps as children
if (iteratorFn === children.entries) {
warning(
didWarnAboutMaps,
'Using Maps as children is unsupported and will likely yield ' +
'unexpected results. Convert it to a sequence/iterable of keyed ' +
'ReactElements instead.',
);
didWarnAboutMaps = true;
}
}
const iterator = iteratorFn.call(children);
let step;
let ii = 0;
while (!(step = iterator.next()).done) {
child = step.value;
nextName = nextNamePrefix + getComponentKey(child, ii++);
subtreeCount += traverseAllChildrenImpl(
child,
nextName,
callback,
traverseContext,
);
}
} else if (type === 'object') {
let addendum = '';
if (__DEV__) {
addendum =
' If you meant to render a collection of children, use an array ' +
'instead.' +
ReactDebugCurrentFrame.getStackAddendum();
}
const childrenString = '' + children;
invariant(
false,
'Objects are not valid as a React child (found: %s).%s',
childrenString === '[object Object]'
? 'object with keys {' + Object.keys(children).join(', ') + '}'
: childrenString,
addendum,
);
}
}
return subtreeCount;
}
11.其他api
1.memo 传入一个组件和新旧props对比方法 返回一个对象$$type是 REACT_MEMO_TYPE 表示后续把这个组件当pureComponent处理
export default function memo<Props>(
type: React$ElementType,
compare?: (oldProps: Props, newProps: Props) => boolean,
) {
if (__DEV__) {
if (!isValidElementType(type)) {
warningWithoutStack(
false,
'memo: The first argument must be a component. Instead ' +
'received: %s',
type === null ? 'null' : typeof type,
);
}
}
return {
$$typeof: REACT_MEMO_TYPE,
type,
compare: compare === undefined ? null : compare,
};
2.Fragment: REACT_FRAGMENT_TYPE,
3.StrictMode: REACT_STRICT_MODE_TYPE, 下面节点都采用一种严格模式,过期的函数会提醒
4.cloneElement
export function cloneElement(element, config, children) {
//!和createelement差不多 只是我们传入啦一个element 我们对他克隆一份 然后config里面有新增的属性加上 有childre也加上 ps 原先得element的props有children将被覆盖
invariant(
!(element === null || element === undefined),
'React.cloneElement(...): The argument must be a React element, but you passed %s.',
element,
);
let propName;
// Original props are copied
const props = Object.assign({}, element.props);
// Reserved names are extracted
let key = element.key;
let ref = element.ref;
// Self is preserved since the owner is preserved.
const self = element._self;
// Source is preserved since cloneElement is unlikely to be targeted by a
// transpiler, and the original source is probably a better indicator of the
// true owner.
const source = element._source;
// Owner will be preserved, unless ref is overridden
let owner = element._owner;
if (config != null) {
if (hasValidRef(config)) {
// Silently steal the ref from the parent.
ref = config.ref;
owner = ReactCurrentOwner.current;
}
if (hasValidKey(config)) {
key = '' + config.key;
}
// Remaining properties override existing props
let defaultProps;
if (element.type && element.type.defaultProps) {
defaultProps = element.type.defaultProps;
}
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
if (config[propName] === undefined && defaultProps !== undefined) {
// Resolve default props
props[propName] = defaultProps[propName];
} else {
props[propName] = config[propName];
}
}
}
}
// Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
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;
}
return ReactElement(element.type, key, ref, self, source, owner, props);
}
4.createfactory type是我们先指定 后面再创建元素时候不需要给type 批量创建type值确定的元素
export function createFactory(type) {
const factory = createElement.bind(null, type);
// Expose the type on the factory and the prototype so that it can be
// easily accessed on elements. E.g. `<Foo />.type === Foo`.
// This should not be named `constructor` since this may not be the function
// that created the element, and it may not even be a constructor.
// Legacy hook: remove it
factory.type = type;
return factory;
}
总结
1.$$typeof这个属性很关键 作为createElement的type 后续会判断这个属性是上面来进行不同的逻辑