【问题标题】:How can you create a Tree display using dynamic data from an API in React?如何在 React 中使用来自 API 的动态数据创建树形显示?
【发布时间】:2020-02-27 16:13:30
【问题描述】:

所以,我在这里有点笨拙。我已经在 react 中研究了树等,并且我相当有信心在给定正确的数据结构的情况下可以实现一个。我遇到的问题是我从一个 API 获取数据,至少在本机上,它没有树的结构,所以我试图动态创建该结构。

这是我从 API 返回的数据的样子:

const category = //could have children
{
  "object_name":"B2B",
  "data_provider_key":"bluekai",
  "object_key": "bluekai-31",
  "object_type":"category",
};

const segment = //will only be a child
{ 
  "object_name":"B2B > Role/Title > Admin Exec",
  "data_provider_key":"bluekai",
  "object_key": "bluekai-1145",
  "object_type":"segment",
  "cpm_cost":2.500
};

这是我用来尝试和操作来自 API 的数据以添加孩子/创建父母等的逻辑。

const asyncView = async function (segTree: string | undefined) {

  const categoryDataCall = api.getBeeswaxSegmentView(categoryBody);
  const segmentDataCall = api.getBeeswaxSegmentView(segmentBody);

  const data = await Promise.all([categoryDataCall, segmentDataCall]);

  const parent = categoryData.find( (el: any) => el.object_key === segTree);

  const categories = data[0].payload;

  if (categories.length >= 1) {
    for (let i = 0; i < categories.length; i++) {
      categories[i].children = [];
    }
  }

  parent.children = categories.concat(data[1].payload); 

  setCategoryData(parent.children);
  setParent(parent);

}

asyncView(e.currentTarget.dataset.segment_tree);

}

return (
  <>
    <div>PARENT: {parent.object_name}</div>
    {categoryData.length === 0
      ? <div>No category data</div>
      : categoryData.map((e: any) => {
          if (e.object_type === 'segment') {
            return (
              <div data-segment_tree={`${e.object_key || "NULL"}`}
                data-provider_key={`${e.data_provider_key}`}
              >
               {`Name: ${e.object_name} (${e.object_key}, $${parseFloat(e.cpm_cost).toFixed(2)} CPM)`}
              </div>
            )
          }

          return (
            <div data-segment_tree={`${e.object_key || "NULL"}`}
              data-provider_key={`${e.data_provider_key}`}
              onClick={getCategoryAndSegmentData}
            >
              {`Name: ${e.data_provider_name || e.object_name}`}
            </div>
          )
        })
    }
  </>
);
}

我还没有实现树部分,但那是因为我相当有信心我没有在我的逻辑中正确创建元素之间的关系/如果页面上有多个“树”/类别,逻辑就会中断(会有的。)

对不起,如果这有点多,但任何有关从 API 动态修改数据以适应子/父关系的树结构的帮助或想法都将不胜感激!

编辑以回应雷·哈特菲尔德:

类别和细分之间的关​​系是什么?

细分将始终是类别的子级,并且永远不会有自己的子级。类别可以有其他类别作为子类别。

您如何确定细分属于哪个类别?

Category 对象的 object_key 属性被传递给 API 调用(进行了两次调用:一次用于细分,一次用于类别)。这是细分和类别之间的唯一关系——返回数据中没有其他任何东西将它们联系在一起。

什么是e?

我假设您的意思是在e.currentTarget.dataset.segment_tree 行中。

e 是事件对象,我用它来创建查询并在点击事件时触发它们。我将 object_key 存储在 HTML 的数据属性中,然后将其传递给处理程序以生成 asyncView() 函数中使用的 categoryBody 和 segmentBody。

出于某种原因,我必须将 e.currentTarget.dataset.segment_tree 作为参数显式传递给异步函数,即使它们在同一范围内,但它所做的只是让我找到被点击的类别在 state 中的现有数据数组中。

什么是categoryData?

categoryData 是值数组(当前处于状态。因此,每次我点击 API 时,我都会更新类别数据以重新渲染所有内容。

实际上,我找到了父(被点击的类别)触发 API 调用以获取与点击的类别 object_key 关联的所有子类别/细分,然后将 children 道具添加到任何传入类别,然后设置最后点击元素的子元素等于返回的segments + categories,然后渲染。

【问题讨论】:

  • 你能详细说明一下api数据和你的一些变量吗?类别和细分之间的关​​系是什么?您如何确定一个细分市场属于哪个类别? e 是什么? categoryData 是什么?
  • 当然!为我的帖子添加了编辑。 :)
  • 抱歉耽搁了。假设你还没有解决这个问题,我今天会尝试回到它。
  • 不用担心。我还没有解决它,任何帮助将不胜感激。 :)

标签: javascript reactjs


【解决方案1】:

我把 this working demo 放在了 jsfiddle 上。以下是重点:


核心理念

核心思想是一个Category组件,负责加载和呈现自己的段和子类别。子类别使用相同的 Category 组件呈现,从而形成递归树结构。


类别组件

const Category = ({item}) => {

  const [data, setData] = React.useState();
  const onClick = data
    ? () => setData(null) // discard data (collapse) on subsequent click
    : () => load(item.object_key).then(setData);

  return (
    <div className="category">
      <div
        className={`category-name ${data ? 'open' : ''}`}
        onClick={onClick}
      >
        {item.object_name}
      </div>

      {data && (
        <ul>
          { data.map((child, i) => (
            <li key={i}><Node item={child}/></li>
          ))}
        </ul>
      )}
    </div>
  )
}

此组件采用一个表示类别的 item 道具。该组件期望 item 具有 object_keyobject_name 字段,就像您示例中的类别对象一样。

最初,组件除了项目中的内容之外没有其他信息,因此它使用 onClick 处理程序呈现类别名称,该处理程序进行 API 调用以获取类别的子项,然后将结果存储在组件的状态中:

const [data, setData] = React.useState();
const onClick = () => load(item.object_key).then(setData);

在随后的渲染中,Category 组件除了渲染类别名称外,还会渲染其子类别(细分和子类别)。子类别使用相同的 Category 组件呈现,从而形成递归树结构。


段组件

const Segment = ({item: {object_name}}) => (
  <div className="segment">{object_name}</div>
);

用于渲染片段的简单组件。只需在此处返回段名称,但您当然可以扩展它以执行您需要的任何操作。


节点组件

const Node = ({item}) => {
  const Cmp = item.object_type === 'category' ? Category : Segment;
  return <Cmp item={item} />;
};

根据类型为给定项目呈现&lt;Segment /&gt;&lt;Category /&gt; 的便利组件。


示例代码的其余部分只是用来模拟 API 调用并生成模拟数据。


加载函数

const load = async (parentKey) => {
  const [categories, segments] = await Promise.all([
    mockApiRequest('category'),
    mockApiRequest('segment')
  ]);

  return [
    ...categories,
    ...segments
  ];
}

给定一个类别的object_key,这会调用 api 来获取细分和子类别、合并并将结果作为单个数组返回。


mockApiRequest

const mockApiRequest = (type) => (
  new Promise((resolve) => {
    setTimeout(() => resolve(fakeData(type)), 200);
  })
)

模拟 API 请求。在使用模拟数据解析之前等待 200 毫秒。


假数据

// generate mock response data
const fakeData = (type) => {
  // copy the list of names
  const n = [...names];

  // plucks a random name from the list
  const getName = () => (
    n.splice(Math.floor(Math.random() * n.length), 1)[0]
  );

  // generate and return an array of data
  return Array.from(
    {length: Math.floor(Math.random() * 5) + 1},
    (_, i) => ({
      ...samples[type],
      object_name: getName()
    })
  )
};

通过复制样本并选择随机名称来生成模拟类别或细分数据。

【讨论】:

  • 这太不可思议了!非常感谢!我还没有实现它,但我看不出它不应该工作的原因,所以我标记为正确答案。我会在它工作时更新,但无论如何,这是一个非常详细和很棒的答案。我非常感激。 :)
  • 更新:实施和工作就像一个魅力。再次感谢您!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多