使用自定义数据获取 React Hook 时编辑数据?
Edit data when using a custom data fetching React Hook?
我一直在尝试使用从 API returns 数据中获取的数据制作图表,如下所示:
{
"totalAmount": 230,
"reportDate": "2020-03-05"
},
{
"totalAmount": 310,
"reportDate": "2020-03-06"
}
...
显示为图表时日期字符串太长,所以我想通过删除年份部分来缩短它。
2020-03-06
变为 03/06
在 a great tutorial by Robin Wieruch 之后,我现在有一个自定义 Hook 来获取数据:
const useDataApi = (initialUrl, initialData) => {
const [data, setData] = useState(initialData);
const [url, setUrl] = useState(initialUrl);
const [isLoading, setIsLoading] = useState(true);
const [isError, setIsError] = useState(false);
useEffect(() => {
const fetchData = async () => {
setIsError(false);
setIsLoading(true);
try {
const response = await fetch(url);
const data = await response.json();
setData(data);
} catch (error) {
setIsError(true);
}
setIsLoading(false);
};
fetchData();
}, [url]);
return [{ data, isLoading, isError }];
};
连同我用 React Hooks 编写的图表组件:
const MyChart = () => {
const [{ data, isLoading, isError }] = useDataApi(
"https://some/api/domain",
[]
);
useEffect(() => {
// I'm using useEffect to replace every date strings before rendering
if (data) {
data.forEach(
d =>
(d.reportDate = d.reportDate
.replace(/-/g, "/")
.replace(/^\d{4}\//g, ""))
);
}
}, [data]);
return (
<>
<h1>My Chart</h1>
{isError && <div>Something went wrong</div>}
{isLoading ? (
. . .
) : (
<>
. . .
<div className="line-chart">
<MyLineChart data={data} x="reportDate" y="totalAmount" />
</div>
</>
)}
</>
);
};
以上作品。但我觉得这可能不是 最佳实践,因为 useEffect
在渲染过程中会被调用两次。当我尝试在我的自定义 Hook 中采用 useReducer
时,代码 不再 工作。
所以我想知道在这种情况下编辑数据的最佳方式是什么?
您可以为您的数据创建一个映射函数,然后由挂钩使用。这可以在你的钩子函数之外完成。
const mapChartDataItem = (dataItem) => ({
...dataItem,
reportDate: dataItem.reportDate.replace(/-/g, "/").replace(/^\d{4}\//g, ""))
});
reportDate 映射与您在 useEffect 中使用的代码相同。
然后在你的钩子函数中:
const data = await response.json();
// this is the new code
const mappedData = data.map(mapChartDataItem);
// change setData to use the new mapped data
setData(mappedData);
在这里这样做意味着您只映射一次对象(当它们被获取时),而不是每次数据值更改时。
更新 - 将函数注入钩子
你的钩子现在看起来像这样:
const useDataApi = (initialUrl, initialData, transformFn) => {
const [data, setData] = useState(initialData);
const [url, setUrl] = useState(initialUrl);
const [isLoading, setIsLoading] = useState(true);
const [isError, setIsError] = useState(false);
useEffect(() => {
const fetchData = async () => {
setIsError(false);
setIsLoading(true);
try {
const response = await fetch(url);
const data = await response.json();
// if transformFn isn't provided, then just set the data as-is
setData((transformFn && transformFn(data)) || data);
} catch (error) {
setIsError(true);
}
setIsLoading(false);
};
fetchData();
}, [url, transformFn]);
return [{ data, isLoading, isError }];
};
然后调用它,你可以使用如下:
const mapChartDataItem = (dataItem) => ({
...dataItem,
reportDate: dataItem.reportDate.replace(/-/g, "/").replace(/^\d{4}\//g, ""))
});
const transformFn = useCallback(data => data.map(mapChartDataItem), []);
const [{ data, isLoading, isError }] = useDataApi(
"https://some/api/domain",
[],
transformFn
);
为简单起见,因为 transformFn 参数是函数的最后一个参数,所以您可以选择在没有它的情况下调用钩子,它只会 return 数据原样 return从获取调用中编辑。
const [{ data, isLoading, isError }] = useDataApi(
"https://some/api/domain",
[]
);
会像以前一样工作。
此外,如果您不想在代码中使用 (transformFn && transformFn(data)) || data
,您可以在挂钩中为 transformFn 指定一个默认值,更多的是:
const useDataApi = (
initialUrl,
initialData,
transformFn = data => data) => {
// then the rest of the hook in here
// and call setData with the transformFn
setData(transformFn(data));
}
我一直在尝试使用从 API returns 数据中获取的数据制作图表,如下所示:
{
"totalAmount": 230,
"reportDate": "2020-03-05"
},
{
"totalAmount": 310,
"reportDate": "2020-03-06"
}
...
显示为图表时日期字符串太长,所以我想通过删除年份部分来缩短它。
2020-03-06
变为 03/06
在 a great tutorial by Robin Wieruch 之后,我现在有一个自定义 Hook 来获取数据:
const useDataApi = (initialUrl, initialData) => {
const [data, setData] = useState(initialData);
const [url, setUrl] = useState(initialUrl);
const [isLoading, setIsLoading] = useState(true);
const [isError, setIsError] = useState(false);
useEffect(() => {
const fetchData = async () => {
setIsError(false);
setIsLoading(true);
try {
const response = await fetch(url);
const data = await response.json();
setData(data);
} catch (error) {
setIsError(true);
}
setIsLoading(false);
};
fetchData();
}, [url]);
return [{ data, isLoading, isError }];
};
连同我用 React Hooks 编写的图表组件:
const MyChart = () => {
const [{ data, isLoading, isError }] = useDataApi(
"https://some/api/domain",
[]
);
useEffect(() => {
// I'm using useEffect to replace every date strings before rendering
if (data) {
data.forEach(
d =>
(d.reportDate = d.reportDate
.replace(/-/g, "/")
.replace(/^\d{4}\//g, ""))
);
}
}, [data]);
return (
<>
<h1>My Chart</h1>
{isError && <div>Something went wrong</div>}
{isLoading ? (
. . .
) : (
<>
. . .
<div className="line-chart">
<MyLineChart data={data} x="reportDate" y="totalAmount" />
</div>
</>
)}
</>
);
};
以上作品。但我觉得这可能不是 最佳实践,因为 useEffect
在渲染过程中会被调用两次。当我尝试在我的自定义 Hook 中采用 useReducer
时,代码 不再 工作。
所以我想知道在这种情况下编辑数据的最佳方式是什么?
您可以为您的数据创建一个映射函数,然后由挂钩使用。这可以在你的钩子函数之外完成。
const mapChartDataItem = (dataItem) => ({
...dataItem,
reportDate: dataItem.reportDate.replace(/-/g, "/").replace(/^\d{4}\//g, ""))
});
reportDate 映射与您在 useEffect 中使用的代码相同。
然后在你的钩子函数中:
const data = await response.json();
// this is the new code
const mappedData = data.map(mapChartDataItem);
// change setData to use the new mapped data
setData(mappedData);
在这里这样做意味着您只映射一次对象(当它们被获取时),而不是每次数据值更改时。
更新 - 将函数注入钩子
你的钩子现在看起来像这样:
const useDataApi = (initialUrl, initialData, transformFn) => {
const [data, setData] = useState(initialData);
const [url, setUrl] = useState(initialUrl);
const [isLoading, setIsLoading] = useState(true);
const [isError, setIsError] = useState(false);
useEffect(() => {
const fetchData = async () => {
setIsError(false);
setIsLoading(true);
try {
const response = await fetch(url);
const data = await response.json();
// if transformFn isn't provided, then just set the data as-is
setData((transformFn && transformFn(data)) || data);
} catch (error) {
setIsError(true);
}
setIsLoading(false);
};
fetchData();
}, [url, transformFn]);
return [{ data, isLoading, isError }];
};
然后调用它,你可以使用如下:
const mapChartDataItem = (dataItem) => ({
...dataItem,
reportDate: dataItem.reportDate.replace(/-/g, "/").replace(/^\d{4}\//g, ""))
});
const transformFn = useCallback(data => data.map(mapChartDataItem), []);
const [{ data, isLoading, isError }] = useDataApi(
"https://some/api/domain",
[],
transformFn
);
为简单起见,因为 transformFn 参数是函数的最后一个参数,所以您可以选择在没有它的情况下调用钩子,它只会 return 数据原样 return从获取调用中编辑。
const [{ data, isLoading, isError }] = useDataApi(
"https://some/api/domain",
[]
);
会像以前一样工作。
此外,如果您不想在代码中使用 (transformFn && transformFn(data)) || data
,您可以在挂钩中为 transformFn 指定一个默认值,更多的是:
const useDataApi = (
initialUrl,
initialData,
transformFn = data => data) => {
// then the rest of the hook in here
// and call setData with the transformFn
setData(transformFn(data));
}