Search code examples
reactjsaxiosreact-hooksreducersuse-context

Sort data in Axios response and set as useReducer payload


I'm calling data from an api into my react app using axios, like so:

const adapter = axios.create({
  baseURL: "http://localhost:4000",
});

const getData = async () => {
  const response = await adapter.get("/test-api");
  return response.data;
};

This runs in a context, and I have a basic reducer function that I pass to the context:

const initialState = {
  loading: true,
  error: false,
  data: [],
  errorMessage: "",
};

const reducer = (state, action) => {
  switch (action.type) {
    case ACTIONS.FETCH_SUCCESS:
      return {
        ...state,
        loading: false,
        data: action.payload,
      };
    case ACTIONS.FETCH_ERROR:
      return {
        ...state,
        error: true,
        errorMessage: "Error loading data",
      };
    default:
      return state;
  }
};

The data I'm returning from my api is shaped like this:

{
  "data": [
    {
      "id": 1,
      "name": "Name 1",
      "items": [
        {
          "id": "klqo1gnh",
          "name": "Item 1",
          "date": "2019-05-12"
        }
      ]
    },
    {
      "id": 2,
      "name": "Name 2",
      "items": [
        {
          "id": "klqo2fho",
          "name": "Item 1",
          "date": "2021-05-05"
        },
        {
          "id": "klro8wip",
          "name": "Item 2",
          "date": "2012-05-05"
        }
      ]
    }
  ]
}

And I've written a simple function that finds the item whose nested array, items here, has the earliest date, using moment:

const sortDataByDate = (items) => {
  return items.sort((first, second) => {
    if (moment(first.items.date).isSame(second.items.date)) {
      return -1;
    } else if (moment(first.items.date).isBefore(second.items.date)) {
      return -1;
    } else {
      return 1;
    }
  });
}; 

I then fetch everything in this function:

const fetchData = useCallback(async () => {
    try {
      await getData().then((response) => {
        dispatch({
          type: ACTIONS.FETCH_SUCCESS,
          payload: response,
        });
      });
    } catch (error) {
      dispatch({ type: ACTIONS.FETCH_ERROR });
    }
  }, []);

I then run fetchData() inside a useEffect within my context:

useEffect(() => {
  fetchData();
}, [fetchData]);

All this to say, here's the problem. My sortDataByDate function works sporadically; sometimes the data is ordered correctly, other times it's not. What I'd like to do is fetch my data, sort it with sortDataByDate, and then set the payload with that sorted data, so it's sorted globally rather than on a component level. Inside my App it seems to work consistently, so I think that I have missed something on a context level. Any suggestions?


Solution

  • You need to sort inner items first and get the earliest date:

      const sortDataByDate = (items) => {
        return items.sort((first, second) => {
          if (moment(first.items[0].date).isSame(second.items[0].date)) {
            return -1;
          } else if (moment(first.items[0].date).isBefore(second.items[0].date)) {
            return -1;
          } else {
            return 1;
          }
        });
      };