如何从自定义反应挂钩中充实数据

时间:2020-11-02 21:25:04

标签: javascript reactjs react-hooks

在我的React应用程序中,我创建了一个自定义钩子,该钩子会在使用时返回一组数据。

它的使用方式类似于const projects = useProjects();

这是一个对象数组,我们可以假设它看起来像这样:

 [{project_id : 123 , name : 'p1'} , {project_id : 1234 , name : 'p2'} ]

现在,我需要使用API​​中的数据来丰富此数据。因此,我必须遍历项目,并基本上向每个对象添加一个新字段,以便新的对象数组如下所示:

[{project_id : 123 , name : 'p1' field3: 'api data'} , {project_id : 1234 , name : 'p2' , field3: 'api data1' } ]

我该如何实现?

我所做的是遍历项目数据,并将字段直接添加到循环中。但是我不知道我是否应该像这样突变这些数据?我希望看看这是否是个好习惯。

1 个答案:

答案 0 :(得分:1)

有几种方法可以解决此问题-这完全取决于您如何从API取回数据。如果您希望将其注入到挂钩中,则可以执行以下操作-

const DEFAULT_PROJECTS = [
    { project_id : 123, name: 'p1' }, 
    { project_id : 1234, name: 'p2' },
];

const useProjects = (dataFromApi) => {
    // Assuming that dataFromApi is what you got back from your API call, 
    // and it's a dictionary keyed on the project_id.
    return useMemo(() => {
        return DEFAULT_PROJECTS.map(proj => {
            const extraData = dataFromApi.get(proj.project_id) || {};
            
            return {
                ...proj,
                ...extraData,
            };
        });
    }, [dataFromApi]);
};

如果useMemo总是在变化,这里的dataFromApi并没有超级帮助-每次都会重建返回的对象。

如果您希望将数据作为挂钩的一部分,请执行以下操作-

import { useEffect, useMemo, useState } from 'react';

const DEFAULT_PROJECTS = [
  { project_id : 123, name: 'p1' },
  { project_id : 1234, name: 'p2' },
];

const useProjects = () => {
  const [dataFromApi, setDataFromApi] = useState(null);

  useEffect(() => {
    if (!!dataFromApi) return;

    // Simulate the data fetch
    const fetchData = async () => {
      return new Promise(resolve => {
        setTimeout(() => {
          const map = new Map();
          map.set(123, { 
            field3: 'api data',
            field4: 'other data',
          });

          setDataFromApi(map);
        }, 2000);
      });
    };

    fetchData();
  }, [dataFromApi]);

  return useMemo(() => {
    let extraData = dataFromApi || new Map();

    return DEFAULT_PROJECTS.map(proj => {
      const extraFields = extraData.get(proj.project_id) || {};

      return {
          ...proj,
          ...extraFields,
      };
    });
  }, [dataFromApi]);
}

export default useProjects;

Here's a code sandbox that shows it in action.