Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

React fetch more data and append to state

I have a problem with a classic infinite scroll, when loading more data and appending to the state.

I have a page that displays, among other things, a custom infinite scrollable list of cities.
The cities are fetched from an external library.

import { City, Country, CitiesQuery } from "an-external-library";

export const CountryComponent: FC<Props> = ({ country: Country }) => {
  const [cities, setCities] = useState<City[]>([]);
  const [citiesQuery, setCitiesQuery] = useState<CitiesQuery>();

  const loadMoreCities = useCallback(() => {
    console.log("loadMoreCities", "cities", cities);

    citiesQuery
      ?.load((newCities) => {
        console.log("loadMoreCities", "newCities", newCities);
        const updatedCities = [...cities, ...newCities];
        console.log("loadMoreCities", "updatedCities", updatedCities);
        setCities(updatedCities);
      })
      .then(() => setCitiesQuery(citiesQuery))
      .catch(console.error)
  }, [cities]); // I've tried adding more dependencies here

  useEffect(() => {
    const query = country.createCitiesQuery()
    query.limit = 3; // example
    query
      .load(setCities)
      .then(() => setCitiesQuery(query)) // so that I can load more from where I left
      .catch(console.error)
  }, []);

  useEffect(() => {
    // so that we know what's actually being set
    console.log("cities", cities);
  }, [cities])

  return (
    <>
      // ...
      <CitiesList cities={cities} onScrollBottom={loadMoreCities}/>
    </>
  )
}

My problem is that the loadMoreCities function doesn’t seem to be updating when cities changes so, inside loadMoreCities, cities is always the same and doesn’t reflect previous appends.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Here’s an example run through logs:

// initial render
cities: Array(0) []

// after first useEffect runs
cities: Array(3) ["A", "B", "C"]

// scroll bottom
loadMoreCities, cities: Array(3) ["A", "B", "C"]
loadMoreCities, newCities: Array(3) ["D", "E", "F"]
loadMoreCities, updatedCities: Array(6) ["A", "B", "C", "D", "E", "F"]
cities: Array(6) ["A", "B", "C", "D", "E", "F"]

// scroll bottom again
loadMoreCities, cities: Array(3) ["A", "B", "C"]  // <---- this is a problem
loadMoreCities, newCities: Array(3) ["G", "H", "I"]
loadMoreCities, updatedCities: Array(6) ["A", "B", "C", "G", "H", "I"]
cities: Array(6) ["A", "B", "C", "G", "H", "I"] // <---- as consequence, we lost ["D", "E", "F"]

I’ve tried adding all possible combinations of dependencies in the useCallback, but nothing seemed to work.
I’m sure I’m missing something really simple, but can’t see what.

>Solution :

Solution

Try:

.load((newCities) => {
  setCities(cities => [...citiesm, ...newCities]);
})

Instead of:

.load((newCities) => {
  setCities([...citiesm, ...newCities]);
})

Explanation

setState can take a value or a setter function

Consecutive calls with a value will result in only the last applied

const [state, setState] = useState([])

setState([...state, 'a']) // current state = []
setState([...state, 'a']) // current state = []
setState([...state, 'c']) // current state = []
// the result will be `['c']`

Using setter callback will make react remember each one and then run each with updated data

const [state, setState] = useState([])

setState(state => [...state, 'a']) // current state = []
setState(state => [...state, 'a']) // current state = ['a']
setState(state => [...state, 'c']) // current state = ['a', 'b']
// the result will be `['a', 'b', 'c']`
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading