0

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?

Jesse Winton
  • 568
  • 10
  • 33

1 Answers1

1

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;
      }
    });
  }; 
Soufiane Boutahlil
  • 2,554
  • 1
  • 7
  • 13
  • This mostly worked, however it's not actually sorting them when I update the payload in `initialState.data`. I sorts them once the page is refreshed, but I need this function to run every time the `data` changes. – Jesse Winton May 10 '21 at 17:17
  • Well can't you just apply the function inside the reducer? Sadly your example doesn't show us where you are calling the function. – mleister May 10 '21 at 17:39
  • You could call the sorting function inside your component. So every time you update your state, the component gets re-rendered and you re-sort your data. – Soufiane Boutahlil May 10 '21 at 20:24
  • @mleister it's not a good practice to apply a function inside a reducer. The reducer has one main job is getting action and updating the state. – Soufiane Boutahlil May 10 '21 at 20:26
  • thanks soufiane, that ended up working, and I moved it out of the reducer. – Jesse Winton May 11 '21 at 16:14