Редактировать данные при использовании выборочной выборки данных React Hook? - PullRequest
1 голос
/ 07 апреля 2020

Я пытался создать диаграмму с данными, полученными из 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 Ответ

2 голосов
/ 07 апреля 2020

Вы можете создать функцию отображения для ваших данных, которая затем будет использоваться ловушкой. Это может быть сделано вне вашей функции подключения.

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 является последним параметром функции, затем вы можете выбрать вызов хука без него, и он просто вернет данные, которые были возвращены из вызова выборки.

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));
}
...