Antd Base Component React Component RC-tree

The source code

rc-tree

src/Tree.tsx

Processing data entry

getDerivedStateFromProps

Tree Node Processes logic

Determine whether the data needs to be updated

needSync

function needSync(name: string) {
    // 1. Props have no value before them, and props have a value inside them (the first time)
    // 2. The data directly compares the same as the first time fieldNames are possible to change the case of treeData changes
    // Children
      return(! prevProps && nameinprops) || (prevProps && prevProps[name] ! == props[name]); }Copy the code

convertDataToEntities

into

Function analysis

/** * Convert 'treeData' into entity records. * Generate a mapping of key entries */
export function convertDataToEntities(dataNodes: DataNode[], { initWrapper, processEntity, onProcessFinished, externalGetKey, childrenPropName, fieldNames, }: { initWrapper? : (wrapper: Wrapper) => Wrapper; processEntity? : (entity: DataEntity, wrapper: Wrapper) =>void; onProcessFinished? : (wrapper: Wrapper) =>void; externalGetKey? : ExternalGetKey; childrenPropName? :string; fieldNames? : FieldNames; } = {},/ * *@deprecated Use `config.externalGetKey` instead */legacyExternalGetKey? : ExternalGetKey,) {
  // Init config
  const mergedExternalGetKey = externalGetKey || legacyExternalGetKey;

  const posEntities = {};
  const keyEntities = {};
  let wrapper = {
    posEntities,
    keyEntities,
  };

  if (initWrapper) {
    wrapper = initWrapper(wrapper) || wrapper;
  }

  traverseDataNodes(
    dataNodes,
    item= > {
      const { node, index, pos, key, parentPos, level } = item;
      const entity: DataEntity = { node, index, key, pos, level };

      const mergedKey = getKey(key, pos);
       
       // Map the key of Pos
      posEntities[pos] = entity;
       // Key Map mapping of key
      keyEntities[mergedKey] = entity;

      // Fill children
      entity.parent = posEntities[parentPos];
      if (entity.parent) {
        entity.parent.children = entity.parent.children || [];
        entity.parent.children.push(entity);
      }

      if(processEntity) { processEntity(entity, wrapper); }}, {externalGetKey: mergedExternalGetKey, childrenPropName, fieldNames },
  );

  if (onProcessFinished) {
    onProcessFinished(wrapper);
  }

  return wrapper;
}
Copy the code

TraverseDataNodes => processNode Recursively processes the node

The copying of various keys, or the use of the user’s key, can be ignored and only focus on the logical processing part

Handle expandedKeys by default

The main thing to look at is this method conductExpandParent passes in two data

  1. KeyList: corresponds to the default expanded key array
  2. KeyEntities: Mapping of key data generated by previous convertDataToEntities. The reason why I did this before is because the query for tree data becomes O(1)

/**
 * If user use `autoExpandParent` we should get the list of parent node
 * @param keyList
 * @param keyEntities* /
export function conductExpandParent(keyList: Key[], keyEntities: Record<Key, DataEntity>) :Key[] {
  const expandedKeys = new Set<Key>();

  function conductUp(key: Key) {
    if (expandedKeys.has(key)) return;

    const entity = keyEntities[key];
    if(! entity)return;

    expandedKeys.add(key);

    const { parent, node } = entity;

    if (node.disabled) return;

    if (parent) {
      conductUp(parent.key);
    }
  }

  (keyList || []).forEach((key) = > {
    conductUp(key);
  });

  return [...expandedKeys];
}
Copy the code

Spread a node, flattening a data flattener

flattenTreeData

Call the dig function recursively

The data from

into

Dig function analysis

getPositionThe pos () function is used to add pos to a node to indicate the location of the node with – as the hyphen

Two parameters, parent, if any. As the first half of pos, index is used to represent the node’s position in the array

Objective:
  1. Identity hierarchy
  2. Identify the location. It provides convenience for searching nodes

getKeyFunction are

  1. GetPosition () getPosition () getPosition () getPosition ()

flattenList

As a flat data store. Each walk pushed an element on the flattener list

FlattenNode

constflattenNode: FlattenNode = { ... omit(treeNode, [fieldTitle, fieldKey, fieldChildren]as any),
        title: treeNode[fieldTitle],// The label displayed by the node
        key: mergedKey, / / getKey results
        parent, // Parent node
        pos, / / getPosition results
        children: null.data: treeNode, // Raw data
        // Specifies whether to start the array
        isStart: [...(parent ? parent.isStart : []), index === 0].// Indicates whether it is the end of the current array
        isEnd: [...(parent ? parent.isEnd : []), index === list.length - 1]};Copy the code

judgeexpandedKeySet

This value has two cases, one kind is a Boolean true | false array is a kind of form, function, new Set above is to heavy If expandedKeySet have value, then move on recursive / / why expandedKeySet no value. I’m not going to recurse any further. Wait for a look

Other key handling is relatively similar. Mainly based on the previous processing of tree structure data and node data generated keymap and list, etc. The following will not do parsing, the specific main code is posted here

SelectedKeys the selected key

The main treatment is calcSelectedKeys

/**
 * Return selectedKeys according with multiple prop
 * @param selectedKeys
 * @param props
 * @returns [string]
 */
export function calcSelectedKeys(selectedKeys: Key[], props: TreeProps) {
  if(! selectedKeys)return undefined;

  const { multiple } = props;
  if (multiple) {
    return selectedKeys.slice();
  }

  if (selectedKeys.length) {
    return [selectedKeys[0]];
  }
  return selectedKeys;
}
Copy the code

CheckedKeys handles the main handler

/** * Parse `checkedKeys` to { checkedKeys, halfCheckedKeys } style */
export function parseCheckedKeys(keys: Key[] | { checked: Key[]; halfChecked: Key[] }) {
  if(! keys) {return null;
  }

  // Convert keys to object format
  let keyProps;
  if (Array.isArray(keys)) {
    // [Legacy] Follow the api doc
    keyProps = {
      checkedKeys: keys,
      halfCheckedKeys: undefined}; }else if (typeof keys === 'object') {
    keyProps = {
      checkedKeys: keys.checked || undefined.halfCheckedKeys: keys.halfChecked || undefined}; }else {
    warning(false.'`checkedKeys` is not an array or an object');
    return null;
  }

  return keyProps;
}
Copy the code

So far, the rC-Tree data sorting part of the analysis to the main thing to understand here is how to minimize the complexity of data processing. Understanding how the previous node handles the convertDataToEntities function for the latter function is mostly understood.