递归地找到一个节点并递归地改变它的访问

Recursively find a node and recursively change it's access

我有这个树数据:

type DataType = {
  id: string;
  access: 'view' | 'none';
  isDisabled: boolean;
  children: DataType[];
};

export const Data: DataType = {
  id: '1',
  access: 'view',
  isDisabled: false,
  children: [
    {
      id: '2',
      access: 'view',
      isDisabled: false,
      children: [
        {
          id: '3',
          access: 'view',
          isDisabled: false,
          children: [],
        },
      ],
    },
    {
      id: '4',
      access: 'view',
      isDisabled: false,
      children: [],
    },
    {
      id: '5',
      access: 'view',
      isDisabled: false,
      children: [],
    },
  ],
};

我想做的是当节点的访问权限发生变化时,isDisabled 布尔值将根据以下规则发生变化:

access为'view'时isDisabled为false,access为'none'时isDisabled为false,当前节点isDisabled为false,子节点isDisabled为true

我有这些功能:(没有像我希望的那样工作)

// this will find the node
const find = (
  data: DataType,
  selectedId: string,
  selectedAccess: 'view' | 'none'
) => {
  if (selectedId && selectedAccess) {
    if (data.id === selectedId) {
      data = changeAccess(data, selectedId, selectedAccess);
    } else {
      data.children.forEach((child) => find(child, selectedId, selectedAccess));
    }
  }
};

// this will change the access and isDisabled
const changeAccess = (
  data: DataType,
  selectedId: string,
  selectedAccess: 'view' | 'none'
): DataType => ({
  id: data.id,
  access: selectedAccess,
  isDisabled:
    data.id !== selectedId && selectedAccess !== 'view' ? true : false, // condition to toggle the isDisabled
  children: data.children?.map((child) =>
    changeAccess(child, selectedId, selectedAccess)
  ),
});

所以如果我调用查找函数:

find(Data, '2', 'none');

结果将是:

const DataAfter: DataType = {
  id: '1',
  access: 'view',
  isDisabled: false,
  children: [
    {
      id: '2',
      access: 'none', // changed from 'view' to 'none'
      isDisabled: false, // remain false
      children: [
        {
          id: '3',
          access: 'none', // changed from 'view' to 'none'
          isDisabled: true, // changed from false to true
          children: [],
        },
      ],
    },
    {
      id: '4',
      access: 'view',
      isDisabled: false,
      children: [
        {
          id: '5',
          access: 'view',
          isDisabled: false,
          children: [],
        },
      ],
    },
  ],
};

如果我再次调用查找:

find(Data, '4', 'none');

结果将是:(注意之前的调用改变了数组并在此处反映了 id 2 和 3)

const DataAfter2: DataType = {
  id: '1',
  access: 'view',
  isDisabled: false,
  children: [
    { // id 2 & 3 remains changed from the pervious call.
      id: '2',
      access: 'none',
      isDisabled: false,
      children: [
        {
          id: '3',
          access: 'none',
          isDisabled: true,
          children: [],
        },
      ],
    },
    {
      id: '4',
      access: 'none', // changed from 'view' to 'none'
      isDisabled: false, // remain false
      children: [
        {
          id: '5',
          access: 'none', // changed from 'view' to 'none'
          isDisabled: true, // changed from false to true
          children: [],
        },
      ],
    },
  ],
};

如果我调用查找:

find(Data, '1', 'none');

所有访问权限都是 'none',只有 id:1 的 isDisabled 布尔值等于 false。

最后,如果我调用查找:

find(Data, '1', 'view');

所有访问权限都将更改为 'view' 并且全部禁用并设置为 false。

我的功能没有像我希望的那样工作。非常感谢任何帮助。

很抱歉,如果您看到了类似的问题,我对递归和树数据还很陌生,想以不同的方式练习使用它。

这一行:

data = changeAccess(data, selectedId, selectedAccess)

将在 data 变量中获取一个新对象,但这并没有改变原始 data...这是一个重新分配,丢弃了之前 data 的引用有,并且(因为 JavaScript 没有按引用调用)调用者将看不到新对象。

因为你想改变给定的数据结构,你不应该 changeAccess return 一个新对象。它应该改变它:

const find = (
  data: DataType,
  selectedId: string,
  selectedAccess: 'view' | 'none'
) => {
  if (selectedId && selectedAccess) {
    if (data.id === selectedId) {
      changeAccess(data, selectedId, selectedAccess); // No return value
    } else {
      data.children.forEach((child) => find(child, selectedId, selectedAccess));
    }
  }
};

const changeAccess = (
  data: DataType,
  selectedId: string,
  selectedAccess: 'view' | 'none'
) => Object.assign(data, { // Mutate `data`
  access: selectedAccess,
  isDisabled:
    data.id !== selectedId && selectedAccess !== 'view', // already boolean
  children: data.children?.map((child) =>
    changeAccess(child, selectedId, selectedAccess)
  ),
});