javascript - How to build a good data structure for a react state, that allows me to easily update deeply nested array of object

I am trying to implement a property (real estate) chainEach chain item should know which chain item is

I am trying to implement a property (real estate) chain

Each chain item should know which chain item is above/below it

A chain item can have its own sub-chain, and this in turn applies to its sub-chain items as well and so on

Visually like this

[
{ id: 1, belowId:0 },
{
  id: 0,
  belowId:2,
  aboveId:1,
  subChain: [
    { id:3, subChain: [{ id:4}] }
  ]
},
{ id: 2, aboveId:0 }
]

I tried this above structure but I started getting confused how to update nested items in state and ui

I am trying to implement a property (real estate) chain

Each chain item should know which chain item is above/below it

A chain item can have its own sub-chain, and this in turn applies to its sub-chain items as well and so on

Visually like this

[
{ id: 1, belowId:0 },
{
  id: 0,
  belowId:2,
  aboveId:1,
  subChain: [
    { id:3, subChain: [{ id:4}] }
  ]
},
{ id: 2, aboveId:0 }
]

I tried this above structure but I started getting confused how to update nested items in state and ui

Share Improve this question edited Jan 6 at 16:25 Hitmands 14.2k4 gold badges40 silver badges77 bronze badges asked Nov 18, 2024 at 23:18 RaffiRaffi 7571 gold badge9 silver badges22 bronze badges 15
  • What are you struggling with at the moment with your update logic, and how are you trying to update? Do you want to update a certain property given its id? – Nick Parsons Commented Nov 19, 2024 at 0:34
  • you prefer a manual approach or just looking for a library? – Erfan Ta Commented Nov 19, 2024 at 0:47
  • I prefer a manual approach and struggling to find the right array/object to update. – Raffi Commented Nov 19, 2024 at 1:17
  • 2 You'll probably want to store and access all items by id. The subChain arrays should not contain object references but just ids. This means you won't have to traverse nested objects (or even search through them) to update anything; although you'll have to look up the structure when consuming the tree. – Bergi Commented Nov 19, 2024 at 2:18
  • 2 @JaredSmith It asks to design a data structure for a react state - a purely functional data structure since react states are immutable. – Bergi Commented Nov 21, 2024 at 21:59
 |  Show 10 more comments

3 Answers 3

Reset to default 1

As no one has answered my question, maybe I wasn't clear enough too to be fair. I will answer it here so that if someone stumbles on a similar problem.

So the main idea is that you want to flatten the array and make sure that subChain only consist of ids not references.

Here is how the ChainItem look like (state structure)

type ChainItem = {
    id: string;
    root: boolean;
    subChain: string[];
    aboveId?: string;
    belowId?: string;
    parentId?: string;
};

I create an item with this utility method

const createItem = (id: number, root: boolean = false): ChainItem => {
    return { id: `${id}`, root, aboveId: null, belowId: null, parentId: null, subChain: [] };
};

How it looks in the UI

interface ChainTreeItem extends Omit<ChainItem, "subChain"> {
    subChain: ChainTreeItem[];
}

I build the UI tree like this

const buildTree = (chain: ChainItem[], id: string) => {
    const itemMap = new Map(chain.map(item => [item.id, item]));
    const build = (id: string): ChainTreeItem | null => {
        const item = itemMap.get(id);
        if (!item) return null;

        return {
            ...item,
            subChain: item.subChain.map(subId => build(subId))
        };
    };

    return build(id);
};

React state

const [chain, setChain] = useState<ChainItem[]>([createItem(0, true)]);
const chainTree: ChainTreeItem[] = useMemo(() => {
    return chain.filter(property => property.root).map(property => buildTree(chain, property.id));
}, [chain]);

Create a sibling

const addSibling = useCallback((id: string, position: "above" | "below") => {
        const isAddAbove = position === "above";
        setChain(prev => {
            let parent: PropertyItem | null = null;
            const newItem = createItem(prev.length);
            let updated = prev.map(item => {
                if (item.id === id) {
                    parent = prev.find(itm => itm.id === item.parentId);
                    if (isAddAbove) {
                        newItem.belowId = item.id;
                        return { ...item, aboveId: newItem.id };
                    } else {
                        newItem.aboveId = item.id;
                        return { ...item, belowId: newItem.id };
                    }
                }
                return item;
            });
            if (parent) {
                newItem.parentId = parent.id;
                updated = updated.map(item => {
                    if (item.id === parent.id) {
                        return {
                            ...item,
                            subChain: isAddAbove ? [newItem.id, ...item.subChain] : [...item.subChain, newItem.id]
                        };
                    }
                    return item;
                });
            } else {
                newItem.root = true;
            }
            return isAddAbove ? [newItem, ...updated] : [...updated, newItem];
        });
    }, []);

Create a sub chain

 const addRight = useCallback((id: string) => {
        setChain(prev => {
            const newItem = createItem(prev.length);
            const updated = prev.map(item => {
                if (item.id === id) {
                    newItem.parentId = item.id;
                    return { ...item, subChain: [...item.subChain, newItem.id] };
                }
                return item;
            });
            return [...updated, newItem];
        });
    }, []);

You should look into a state management library like Redux. With Redux you'll have a global state store that all of your components can pull from.

Listing below the two key characteristics of the requirements:

a. Each chain item should know which chain item is above/below it

b. A chain item can have its own sub-chain, and this in turn applies to its sub-chain items as well and so on.

To avoid mutation of objects in React, the code needs to work as below:

a. For a change in any object in a sub-chain, the whole containing object must be rebuilt. However, the objects nested inside the changed object will be unaffected. Similarly, the other containing objects if any in the same sub-chain will also be unaffected. This point is further explained below with an example.

Example

subChain: [{ id:3, subChain: [{ id:4, subChain : [{ id: 5 }]}]}, {id : 6}]

When the object { id:4, subChain : [{ id: 5 }]} changes, then the whole containing object { id:3, subChain: [{ id:4, subChain : [{ id: 5 }]}]} must be rebuilt. However the nested object in the changed object { id: 5 } will remain unaffected. Similarly the other containing in the same sub-chain object{id : 6} will also be unaffected.

As the whole containing object in a sub-chain is always changed here, this change will cascade to the main-chain. Point b states the same.

b. For a change in any object either in the main chain or in a sub-chain , the whole main chain needs to be rebuilt.

case 1 : a change in a sub-chain as explained in point a.

For a change in the object { id:4, subChain : [{ id: 5 }]}, the object { id : 0, … } must be rebuilt since a containing object in its sub-chain has been changed. The newly rebuilt object will also call for re-building the objects above and below which will result a whole rebuilding process for the entire main chain.

 {
      id: 1,
      belowId: 0,
    },
    {
      id: 0,
      belowId: 2,
      aboveId: 1,
      subChain: { id: 3, subChain: { id: 4, subChain: { id: 5 } } },
    },
    {
      id: 2,
      aboveId: 0,
    },

case 2 : a change in the main-chain

When the object { id : 0, …} changes, this is a direct change in the object which is separate from the changes in its sub-chain. let us say, its id is changing from 0 to 100. Therefore the same object must be newly built. Now the objects above and below will also be affected by this change. And it will call for the whole main-chain to be rebuilt. However the sub-chains in all objects in the main chain will remain unaffected.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745589239a4634731.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信