Редактировать данные при использовании React Hook для получения пользовательских данных?
Я пытался создать диаграмму с данными, полученными из API, который возвращает данные следующим образом:
{
"totalAmount": 230,
"reportDate": "2020-03-05"
},
{
"totalAmount": 310,
"reportDate": "2020-03-06"
}
...
Строка даты слишком длинная при отображении в виде диаграммы, поэтому я хочу сократить ее, удалив часть года.
2020-03-06
становится 03/06
Следуя отличному руководству Робина Веруха, у меня теперь есть собственный хук для извлечения данных:
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
будет вызываться дважды во время рендеринга. И когда я пытаюсь усыновитьuseReducer
в моем пользовательском хуке код больше не работает.
Итак, мне интересно, как лучше всего редактировать данные в этой ситуации?
1 ответ
Вы можете создать функцию сопоставления для своих данных, которая затем будет использоваться ловушкой. Это можно сделать вне функции ловушки.
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 является последним параметром функции, вы можете выбрать вызов вашего хука без него, и он просто вернет данные, как они были возвращены из вызова fetch.
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));
}