0

I have a label, which has onClick callback:

<label className="cursor-pointer" onClick={loadExisting}>
  Click me
</label>

loadExisting function, fetch'es data from api, and passses it to parseData function.

 const loadExisting = () => {
    fetch("/api/v.1.0/events", { mode: "no-cors" })
      .then(function(response) {
        if (!response.ok) {
          console.log("Something is wrong");
          return;
        }
        return response.json();
      })
      .then(data => {
        if (!data) {
          return;
        }
        parseEvents(data);
      });
  };

In this function, I am trying to store only those events, which's titles are unique:

const parseEvents = data => {
    if (data) {
      for (let i = 0; i < data.length; i++) {
        if (titlesArray.indexOf(data[i].title) < 0) {
          setTitlesArray([...titlesArray, data[i].title]);
          setEvents([...events, data[i]]);
        }
      }
    }
  };

Basically my idea is to set all the unique titles into titlesArray and if event's title is unique, I add it to both titlesArray and events.

Problem: This only works if I keep clicking on that label. With first click events.length is equal to 0, second click- equal to 1, third click- equal to 2, etc. Why it does not parse all events at once? So after 1 click I would have ~10 events that have unique titles.

Viktor
  • 195
  • 7
  • 1
    You need to elaborate on this question. "This only works if I keep clicking on that `label`.": because the event gets fired `onClick` only. – Akshit Mehra Dec 15 '19 at 12:01
  • @AkshitMehra, yes, but I would expect, it would parse all the events, it got from the API, not a single one. When page loads, `events` is empty, if I click on label, I get back 19 events from API. But if I `console.log(events)`, before render, after I click label, `events` have only 1 object inside it. If I click it for second time- `events` have two objects inside it. I would expect, after clicking a label: 1) Request to API retrieves 19 events 2) ALL the events get parsed 3) I have ~10 events that have unique titles in `events` state. – Viktor Dec 15 '19 at 12:03
  • fetch(url).then(res=> res.json()).then(data => parseEvents(data)) you should try. – Atul Kumar Dec 15 '19 at 12:13
  • Can you post the code of `loadExisting()` function that you're calling? – Akshit Mehra Dec 15 '19 at 12:18
  • @AkshitMehra , updated original post with `loadExisting()` function – Viktor Dec 15 '19 at 15:46
  • What does the data you get back from the API look like? And are you using class based components or functional components? – Matt Oestreich Dec 15 '19 at 15:59
  • @MattOestreich, functional. Data from API is an array consisting of objects. Each object is an event with it's values. – Viktor Dec 15 '19 at 16:24

1 Answers1

0

setState is asynchronous and setTitlesArray and setEvents are likely to call setState.

So in your case, since you don't update titlesArray and events, the most likely is that you will only observe the last elem added (data[data.length-1])

The fix is to simply call setState once.

data.forEach(d => {
  if (titlesArray.includes(d.title) {
    titlesArray.push(d.title)
    events.push(d)
  }
})
//I slice 0 to copy, but maybe it is not necessary if your function does it already
setTitlesArray(titlesArray.slice(0))
setEvents(events.slice(0))


nb: looking for existence in an array (titlesArray) is less efficient than using a Set. You should consider if you have a lot of titles

grodzi
  • 5,633
  • 1
  • 15
  • 15