Home > Enterprise >  how to cast/parse Axios REST response field from string to Date within React?
how to cast/parse Axios REST response field from string to Date within React?

Time:07-20

What would be the easiest way to effectively "convert" (parse/cast) the Date strings from a REST API response to true Dates? So that after "setItems" is called the contents of "items" is correctly typed as per the "ItemsType". Is there an easy way to do this in TypeScript/javascript?

Code below. Currently when I call setItems everything remains as strings, even though I have called out the items variable should be of type "ItemsType[]", so I guess it's not as if typing the variable magically makes it happen, and in fact there is no error thrown.

type ItemsType = {
  id: number
  locationName: string
  checkinDate: Date
  checkoutDate: Date   // edited-this shoudl have been Date also
}

function App() {
  const [items, setItems] = useState<ItemsType[]>([])
  useEffect(() => {
    (async () => {
      try {
        const res = await axios.get(`https://localhost:7040/Tripstop`)
        const rawItems = await res.data
        setItems(typedItems)
      }
    )()
  }, [])

Sample incoming REST data:

enter image description here

CodePudding user response:

You can cast something like this

const App = () => {
  const [items, setItems] = useState<Array<ItemsType>>([]);

  const getData = async () => {
    const res = await axios.get(`https://localhost:7040/Tripstop`)
    const rawItems = await res.data as Array<ItemsType>;
    const parsedData = rawItems.map((x) => ({
        ...x,
        checkInDate: new Date(x.checkInDate).toDateString(), 
        checkOutDate: new Date(x.checkOutDate).toDateString()
    }))
    setItems(parsedData)
  }

  useEffect(() => getData(), [])
}

CodePudding user response:

You could always implement a generic Axios response transformer to decode anything that looks like a date.

For example, this creates an Axios instance that transforms any value with the word "date" in the key

const dateKeyRx = /date/i;

export const api = axios.create({
  baseURL: "http://localhost:7040/",
  transformResponse: (data) =>
    JSON.parse(data, (key, value) =>
      dateKeyRx.test(key) ? new Date(value) : value
    ),
});

But I'd be more inclined to create a service for retrieving and transforming specific responses. This would encapsulate your logic without exposing the specific implementation to any consumers.

For example

// svc/tripstop.ts

import axios from "axios";

interface ResponseItem {
  id: number;
  locationName: string;
  checkinDate: string;
  checkoutDate: string;
}

export interface ItemsType extends ResponseItem {
  checkinDate: Date;
  checkoutDate: Date;
}

export const getTripstop = async (): Promise<ItemsType[]> => {
  const { data } = await axios.get<ResponseItem[]>(
    "https://localhost:7040/Tripstop"
  );
  return data.map(({ checkinDate, checkoutDate, ...rest }) => ({
    ...rest,
    checkinDate: new Date(checkinDate),
    checkoutDate: new Date(checkoutDate),
  }));
};

Then you can use it in your React components

import { getTripstop } from "./svc/tripstop";

// ... snip

useEffect(() => {
  getTripstop().then(setItems).catch(console.error);
}, []);

You could still use the response transformer which would save you the extra .map() call

export const getTripstop = async () =>
  (await api.get<ItemsType[]>("/Tripstop")).data;
  • Related