0

I'm building a function at the moment which receives data from an API call, maps through it (it's an array of objects), and pushes an item to a respective array depending on one of its values.

However, it only seems to be pushing one item into each array and no more.

There are no errors in my code, so not sure what I'm doing wrong - I assume I'm overwriting the array each time but not sure.

Here's the state arrays which the items need to be pushed into:

const [backlog, setBacklog] = useState([])
const [inProgress, setInProgress] = useState([])
const [paused, setPaused] = useState([])
const [completed, setCompleted] = useState([])

And here is the function itself:

const fetchData = async () => {
    await axios.get("/api/fetch/fetchData")
        .then(async (response) => {

            const data = await response.data;

            setAllTasks(data)

            data.map(item => {

                if (item.status === 'backlog') {
                    setBacklog([...backlog, item])
                } else if (item.status === 'in-progress') {
                    console.log(item)
                    setInProgress([...inProgress, item])
                } else if (item.status === 'paused') {
                    setPaused([...paused, item])
                } else if (item.status === 'completed') {
                    setCompleted([...completed, item])
                }
                
            })
        })
    }
Jon Nicholson
  • 927
  • 1
  • 8
  • 29
  • You have to return in the map. Not sure why you are suing map, if you are passing in the data to their respective setters. – Invizi May 06 '21 at 18:09
  • `setBacklog` is asynchronous, so `backlog` is not guaranteed to have the item you added using `setBacklog`. – Heretic Monkey May 06 '21 at 18:10
  • Does this answer your question? [How to set a state in a forEach loop using useState hook](https://stackoverflow.com/questions/65715339/how-to-set-a-state-in-a-foreach-loop-using-usestate-hook) – Heretic Monkey May 06 '21 at 18:11

2 Answers2

1

Because set functions are batched it's likely that the variable has not yet been updated.

To modify your program (as is) use function state updating:

const fetchData = async () => {
    await axios.get("/api/fetch/fetchData")
        .then(async (response) => {

            const data = await response.data;

            setAllTasks(data)

            data.map(item => {

                if (item.status === 'backlog') {
                    setBacklog(prevBackLog => [...prevBackLog, item])
                } else if (item.status === 'in-progress') {
                    console.log(item)
                    setInProgress(prevInProgress => [...prevInProgress, item])
                } else if (item.status === 'paused') {
                    setPaused(prevPaused => [...prevPaused, item])
                } else if (item.status === 'completed') {
                    setCompleted(prevSetCompleted => [...prevSetCompleted, item])
                }

            })
        })
}

Note: it would be much better to come up with a way to use a single set function to update all of these values in one state update.

Maybe with the currently available set functions building a complete state and doing one update per status doing something like:

function groupBy(arr, property) {
    return arr.reduce(function (memo, x) {
        if (!memo[x[property]]) {
            memo[x[property]] = [];
        }
        memo[x[property]].push(x);
        return memo;
    }, {});
}

data = groupBy(data, 'status')

setBacklog(prevBackLog => [...prevBackLog, ...data['backlog']])
setInProgress(prevInProgress => [...prevInProgress, ...data['in-progress']])
setPaused(prevPaused => [...prevPaused, ...data['paused']])
setCompleted(prevSetCompleted => [...prevSetCompleted, ...data['completed']])
Henry Ecker
  • 34,399
  • 18
  • 41
  • 57
  • Thanks Henry - the first suggestion has worked. And I agree, it's repeating myself too much. Would be much better to have it all grouped as you say. I'll have a think, and do like your suggestions. Out of interest, are you able to explain more why I have to use set functions? – Jon Nicholson May 06 '21 at 19:00
  • 1
    See the note in [functional updates](https://reactjs.org/docs/hooks-reference.html#functional-updates), [Why React useState with functional update form is needed?](https://stackoverflow.com/q/57828368/15497888), and [What is the difference between passing an object or a function in setState?](https://reactjs.org/docs/faq-state.html#what-is-the-difference-between-passing-an-object-or-a-function-in-setstate) – Henry Ecker May 06 '21 at 19:03
1

I think your axios call is considerably more complicated than it needs to be. All those extra async/await & then calls may be your issue. Here's a much simpler solution:

const fetchData = async () => {
    const response = await axios.get("/api/fetch/fetchData");
    const data = response.data;

    data.map(item => {

                if (item.status === 'backlog') {
                    setBacklog(prevBackLog => [...prevBackLog, item])
                } else if (item.status === 'in-progress') {
                    console.log(item)
                    setInProgress(prevInProgress => [...prevInProgress, item])
                } else if (item.status === 'paused') {
                    setPaused(prevPaused => [...prevPaused, item])
                } else if (item.status === 'completed') {
                    setCompleted(prevSetCompleted => [...prevSetCompleted, item])
                }

            })
}

akabin
  • 93
  • 8
  • Thanks for the response - this approach would work with the set functions. I need the .then()'s as there's further chaining and actions later on. This isn't the whole function. Appreciate the response! :-) – Jon Nicholson May 06 '21 at 19:02