React 源码阅读1

Fork最新版的 React 源码地址

刚开始看源码,先过一遍.先看最顶层暴露出来的 API,再具体看实现的源码.
保持学习.

React 入口

img


const React = {
  Children: {
    map,
    forEach,
    count,
    toArray,
    only,
  },

  createRef,
  Component,
  PureComponent,

  createContext,
  forwardRef,
  lazy,
  memo,

  useCallback,
  useContext,
  useEffect,
  useImperativeHandle,
  useDebugValue,
  useLayoutEffect,
  useMemo,
  useReducer,
  useRef,
  useState,

  Fragment: REACT_FRAGMENT_TYPE,
  Profiler: REACT_PROFILER_TYPE,
  StrictMode: REACT_STRICT_MODE_TYPE,
  Suspense: REACT_SUSPENSE_TYPE,
  unstable_SuspenseList: REACT_SUSPENSE_LIST_TYPE,

  createElement: __DEV__ ? createElementWithValidation : createElement,
  cloneElement: __DEV__ ? cloneElementWithValidation : cloneElement,
  createFactory: __DEV__ ? createFactoryWithValidation : createFactory,
  isValidElement: isValidElement,

  version: ReactVersion,

  unstable_withSuspenseConfig: withSuspenseConfig,

  __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals,
};

export default React;

Children

React.Children 提供了用于处理 this.props.children 不透明数据结构的实用方法。

// React.js

import {forEach, map, count, toArray, only} from './ReactChildren';
...
...
  Children: {
    map,
    forEach,
    count,
    toArray,
    only,
  },

//ReactChildren,js
export {
  forEachChildren as forEach,
  mapChildren as map,
  countChildren as count,
  onlyChild as only,
  toArray,
};

React.Children.map

children 里的每个直接子节点上调用一个函数,并将 this 设置为 thisArg。如果 children 是一个数组,它将被遍历并为数组中的每个子节点调用该函数。
如果子节点为null 或是 undefined,则此方法将返回 null 或是 undefined,而不会返回数组。

如果 children 是一个 Fragment 对象,它将被视为单一子节点的情况处理,而不会被遍历。
render() {
  return (
    <React.Fragment>
      <ChildA />
      <ChildB />
      <ChildC />
    </React.Fragment>
  );
}

map源码

function mapChildren(children, func, context) {
  if (children == null) {
    return children;
  }
  // 返回 result 数组
  const result = [];
  //result 进去走了一圈 这边就不再细说
  mapIntoWithKeyPrefixInternal(children, result, null, func, context);
  return result;
  }
https://segmentfault.com/a/11... 写的比较详细

React.Children.forEach

React.Children.forEach(children, function[(thisArg)])

React.Children.map() 类似,但它不会返回一个数组。

forEach源码

/**
 * Iterates through children that are typically specified as `props.children`.
 *
 * See https://reactjs.org/docs/react-api.html#reactchildrenforeach
 *
 * The provided forEachFunc(child, index) will be called for each
 * leaf child.
 *
 * @param {?*} children Children tree container.
 * @param {function(*, int)} forEachFunc
 * @param {*} forEachContext Context for forEachContext.
 */
function forEachChildren(children, forEachFunc, forEachContext) {
  if (children == null) {
    return children;
  }
  const traverseContext = getPooledTraverseContext(
    null,
    null,
    forEachFunc,
    forEachContext,
  );
  traverseAllChildren(children, forEachSingleChild, traverseContext);
  releaseTraverseContext(traverseContext);
}

React.Children.count

React.Children.count(children)

返回 children 中的组件总数量,等同于通过mapforEach调用回调函数的次数。

count源码

function countChildren(children) {
  return traverseAllChildren(children, () => null, null);
}

React.Children.only

React.Children.only(children)

验证 children 是否只有一个子节点(一个 React 元素),如果有则返回它,否则此方法会抛出错误。

only源码

function onlyChild(children) {
  invariant(
    isValidElement(children),
    'React.Children.only expected to receive a single React element child.',
  );
  return children;
}
React.Children.only() 不接受 React.Children.map()的返回值,因为它是一个数组而并不是 React 元素。

React.Children.toArray

React.Children.toArray(children)

children 这个复杂的数据结构以数组的方式扁平展开并返回,并为每个子节点分配一个 key
当你想要在渲染函数中操作子节点的集合时,它会非常实用,特别是当你想要在向下传递 this.props.children 之前对内容重新排序或获取子集时。

React.Children.toArray() 在拉平展开子节点列表时,更改 key 值以保留嵌套数组的语义。
也就是说,toArray 会为返回数组中的每个 key添加前缀,以使得每个元素 key 的范围都限定在此函数入参数组的对象内。

toArray源码

function toArray(children) {
  const result = [];
  mapIntoWithKeyPrefixInternal(children, result, null, child => child);
  return result;
}

xiaoping
337 声望12 粉丝

保持学习,记一下自己的学习经历