antd 基礎組件 React Component RC-Tree 數據整理

antd 基礎組件 React Component RC-Tree

源碼

rc-treenode

src/Tree.tsxreact

處理數據 入口

getDerivedStateFromPropsgit

Tree Node 處理邏輯

判斷是否須要更新數據

needSync

function needSync(name: string) {
    // 1. props前面沒值,而且 props裏面有值(第一次) 
    // 2. 數據 直接比較 相同否 第一次是 fieldNames 有可能變化的狀況 treeData 變化的狀況
    // children的提示
      return (!prevProps && name in props) || (prevProps && prevProps[name] !== props[name]);
    }
複製代碼

convertDataToEntities

image.png 轉化爲github

image.png

函數解析

/** * Convert `treeData` into entity records. * 生成一個 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);
       
       // Pos 的key Map 映射
      posEntities[pos] = entity;
       // key 的key Map 映射
      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;
}
複製代碼

traverseDataNodes => processNode 遞歸處理節點

其中各類key 的複寫或者使用用戶的key 的這些操做能夠忽略 只關注邏輯處理部分api

處理默認展開的 expandedKeys

主要看 這個方法 conductExpandParent 傳入兩個數據數組

  1. keyList : 對應默認展開的key 數組
  2. keyEntities: 由以前 convertDataToEntities 生成的key 對應數據的映射。這裏以前這麼作的緣由在於樹形數據的查詢會變成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];
}
複製代碼

展開節點,扁平化數據 flattenNodes

flattenTreeData

遞歸調用 dig 函數markdown

把數據從antd

轉成app

dig 函數解析

getPosition 函數用來 給節點添加 pos 用於表示節點的具體位置 以 - 做爲鏈接符

兩個參數,parent 若是有的話。做爲pos 的前半部分,index 做爲表示節點在數組中的位置函數

目的:
  1. 標識層級
  2. 標識位置。後續提供給查找節點等提供便利

getKey 函數做用

  1. 獲取key ,若是節點沒有key 默認以上面getPosition 函數結果做爲Key

flattenList

做爲扁平化後的數據存儲。每次遍歷都會往 flattenList push 一個元素

FlattenNode

const flattenNode: FlattenNode = {
        ...omit(treeNode, [fieldTitle, fieldKey, fieldChildren] as any),
        title: treeNode[fieldTitle],// 節點顯示的label
        key: mergedKey, // getKey 結果
        parent, // 父級節點
        pos, // getPosition 結果
        children: null,
        data: treeNode, // 原始數據
        // 標識是否爲數組的起始位置
        isStart: [...(parent ? parent.isStart : []), index === 0],
        // 標識是否爲當前數組的結尾
        isEnd: [...(parent ? parent.isEnd : []), index === list.length - 1], 
      };
複製代碼

判斷 expandedKeySet

這個值有兩種狀況,一種是 boolean的 true|false 一種是 數組形式,函數上面 new Set是爲了去重 若是 expandedKeySet 有值,便繼續往下遞歸 // 爲何 expandedKeySet 沒有值。不繼續往下遞歸。稍等看下

其餘的key處理相對相似。主要是基於前面處理樹狀結構數據和節點數據生成的 keymap 和 list等。如下就不作解析了,具體主要代碼貼在這裏

selectedKeys 已選key

主要處理方法是 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;
}
複製代碼

checkedKeys 處理 主要處理函數

/** * 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;
}
複製代碼

至此,rc-tree 的數據整理部分就分析到這裏 主要要理解的事情是,如何把數據的處理的複雜度下降到最小。理解前面的節點處理 convertDataToEntities 函數對後面函數的做用就理解了大半.

相關文章
相關標籤/搜索