'Component re-renders self after API call whilst using useRef

I'm working on a project and wanted to try and implement an infinitely scrollable page to list users. Filtering and such works fine and all but every time the scrolling component reaches the ref element the component makes an API call to append to the list and then the parent component re-renders self completely.

const UsersList = () => {
const [searchString, setSearchString] = useState('')
const [next, setNext] = useState('')
const { userList, error, nextPage, loading, hasMore } = useFetch(next)
const [usersList, setUsersList] = useState([])
const observer = useRef()
const lastElemRef = useCallback(
(node) => {
  if (loading) return
  if (observer.current) observer.current.disconnect()
  observer.current = new IntersectionObserver((entries) => {
    if (entries[0].isIntersecting && hasMore) {
      setNext((prev) => (prev = nextPage))
      setUsersList((prev) => new Set([...prev, ...userList]))
    }
  })
  if (node) {
    observer.current.observe(node)
  }
},
[loading, nextPage, hasMore],
)

useEffect(() => {
setUsersList((prev) => new Set([...prev, ...userList]))
console.log(error)
}, [])

return (
<>
  {loading ? (
    <CSpinner variant="grow"></CSpinner>
  ) : (
    <CContainer
      className="w-100  justify-content-center"
      style={{ maxWidth: 'inherit', overflowY: 'auto', height: 600 }}
    >
      <CFormInput
        className="mt-2 "
        id="userSearchInput"
        value={searchString}
        onChange={(e) => {
          setSearchString(e.target.value)
        }}
      />
      {loading ? (
        <CSpinner variant="grow"></CSpinner>
      ) : (
        <>
          {Array.from(usersList)
            .filter((f) => f.username.includes(searchString) || searchString === '')
            .map((user) => {
              if (Array.from(usersList)[usersList.size - 1] === user) {
                return (
                  <UserCard
                    key={user.id}
                    user={user}
                    parentRef={searchString ? null : lastElemRef}
                  />
                )
              } else {
                return <UserCard key={user.id} user={user} />
              }
            })}
        </>
      )}
    </CContainer>
  )}
</>
)
}

export default UsersList

This is the component entirely.

Here's my useFetch hook;

export const useFetch = (next) => {
const [userList, setUserList] = useState([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState('')
const [nextPage, setNextPage] = useState(next)
const [hasMore, setHasMore] = useState(true)

useEffect(() => {
setLoading(true)
setError('')
axios
  .get(next !== '' ? `${next}` : 'http://localhost:8000/api/getUsers/', {
    headers: {
      Authorization: 'Bearer ' + localStorage.getItem('access_token'),
    },
  })
  .then((res) => {
    setUserList((userList) => new Set([...userList, ...res.data.results]))
    setNextPage((prev) => (prev = res.data.next))
    if (res.data.next === null) setHasMore(false)
    setLoading(false)
  })
  .catch((err) => {
    setError(err)
  })
 }, [next])

  return { userList, error, nextPage, loading, hasMore }
}

export default useFetch

I'm using Limit Offset Pagination provided by Django Rest Framework, next object just points to the next set of objects to fetch parameters include ?limit and ?offset added at the end of base API url. What is it that I'm doing wrong here ? I've tried many different solutions and nothing seems to work.

Solved

Apparently it was just my back-end not cooperating with my front-end so I've changed up the pagination type and now it seems to behave it self.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source