簡體   English   中英

如何從不可變樹中獲取更改的樹,最大化節點的重用

[英]How to get altered tree from Immutable tree, maximising reuse of nodes

我有這樣的樹結構數據:

[{
    id: 54,
    name:123,
    children: [{
        id: 54,
        name:123,
        children: [{
            id: 154,
            name:1234,
            children []...
        }]
    }]
}, {
 ...
}]

我正在使用Angular 2.據我所知,無論何時輸入發生變化且您的變化檢測策略為onPush更改檢測都會onPush

為了優化樹結構更新(例如,在嵌套級別切換節點或更改此類節點的任何屬性),我使用了Immutable

Immutable如何幫助我優化我的更新? 我讀到,當數據發生變化時, Immutable會重用舊數據中的引用來構造新對象。

如何有效地使用不可變數據結構來更新嵌套級別的節點?

假設

  1. 我沒有任何節點的keyPath
  2. 每個節點都有一個唯一的id屬性值,可用於查詢樹數據(但是如何?)

問題

  1. 如何在嵌套級別更新某個節點? 什么是最有效的方式來接觸該節點?
  2. 如何更新多個節點? 我聽說過withMutations API,但還有其他有效方法嗎?

我的方法

  1. 深層復制所有內容,然后修改新構造的對象:

     var newState = deepClone(oldState) // deep copy everything and construct a new object newState.nodes.forEach(node => { if(node.id == 54) { node.id = 789; } }) 
  2. 我想要實現的內容:

     var newState = Immutable.fromJS(oldState) // create an immutable object newState = newState.find(node => { node.set("id", 123); }); // any changes to object will return new object 

使用第二個解決方案,我希望實現節點的重用,如下圖所示:

在immutablejs的樹視圖

意識到當你對樹結構使用Immutable時,你不能指望在不改變對該節點的內部引用的情況下替換節點,這意味着這樣的更改需要冒泡到樹的根,也改變根。

更詳細:只要使用方法更改某個屬性值,就會獲得為該特定節點返回的新對象。 然后,要在樹中重新注入該新節點,即在父項的列表中,您將使用將創建新列表的方法(因為children屬性也是不可變的)。 現在的問題是將列表附加到父節點,這將導致新的父節點,...等。 您將最終重新創建要更改的節點的所有祖先節點,從而為您提供一個新的樹實例,該實例將重用一些不在根節點路徑中的節點。

要重新使用您的圖片,您將獲得以下內容:

在此輸入圖像描述

Immutable API可以使用updateIn方法為您執行此操作(如果您的更新僅涉及目標節點的一個屬性, setIn )。 您需要將密鑰路徑傳遞給它以標識要修改的(嵌套)節點。

因此,如果您知道要更改的節點的id ,則可以使用一個小輔助函數來查找該特定節點的關鍵路徑。

function findKeyPathOf(tree, childrenKey, predicate) {
    var path;
    if (Immutable.List.isList(tree)) {
        tree.some(function (child, i) {
            path = findKeyPathOf(child, childrenKey, predicate);
            if (path) return path.unshift(i); // always returns truthy
        });
        return path;
    } 
    if (predicate(tree)) return [];
    path = findKeyPathOf(tree.get(childrenKey), childrenKey, predicate);
    if (path) return [childrenKey].concat(path);
}

您需要將它的樹,有孩子(所以屬性名children在你的情況下),並且,將確定你正在尋找該節點的功能。 假設您想要id為4的節點的路徑,那么您可以像這樣調用它:

var keyPath = findKeyPathOf(tree, 'children', node => node.get('id') == 4);

該關鍵路徑可能看起來像這樣 - 數組中索引的更改,以及提供更深層數組的children屬性:

[0, 'children', 0, 'children', 1]

然后要修改該路徑上的節點,您可以執行以下操作:

var newTree = tree.updateIn(keyPath, node => node.set('name', 'Hello'));

這是一個包含一些示例數據的演示:

 // Function to get the path to a certain node in the tree function findKeyPathOf(tree, childrenKey, predicate) { var path; if (Immutable.List.isList(tree)) childrenKey = tree.findKey(child => path = findKeyPathOf(child, childrenKey, predicate)); else if (predicate(tree)) return []; else path = findKeyPathOf(tree.get(childrenKey), childrenKey, predicate); return path && [childrenKey].concat(path); } // Function to compare two trees function differences(tree1, tree2, childrenKey) { if (Immutable.List.isList(tree1)) { return tree1.reduce(function (diffs, child, i) { return diffs.concat(differences(child, tree2.get(i), childrenKey)); }, []); } return (tree1 !== tree2 ? [tree1] : []) .concat(differences(tree1.get(childrenKey), tree2.get(childrenKey), childrenKey)); } // Sample data var tree = [{ id: 1, name: 'Mike', children: [{ id: 2, name: 'Helen', children: [{ id: 3, name: 'John', children: [] },{ id: 4, name: 'Sarah', children: [{ id: 5, name: 'Joy', children: [] }] }] }] }, { id: 6, name: 'Jack', children: [{ id: 7, name: 'Irene', children: [] },{ id: 8, name: 'Peter', children: [] }] }]; // Create immutable tree from above plain object: var tree = Immutable.fromJS(tree); // Use the function to find the node with id == 4: var keyPath = findKeyPathOf(tree, 'children', node => node.get('id') == 4); // Found it? if (keyPath) { // Set 'name' to 'Hello' in that node: var newTree = tree.updateIn(keyPath, node => node.set('name', 'Hello')); // Print the new tree: console.log(newTree.toJS()); // Compare all nodes to see which ones were altered: var altered = differences(tree, newTree, 'children').map(x => x.get('id')); console.log('IDs of nodes that were replaced: ', altered); } else { console.log('Not found!'); } 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.1/immutable.min.js"></script> 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM