1

I try to do Load More on a list of data as written below:

import React, { useState, useEffect } from "react";
import { render } from "react-dom";
import axios from "axios";
import "./style.css";

const App = () => {
  const LIMIT = 2;
  const [data, setData] = useState([]);
  const [isLoading, setLoading] = useState(false);
  const [page, setPage] = useState(1);

  const loadData = async (skip = 1, limit = LIMIT) => {
    const URL = "https://reqres.in/api/users";
    const headers = {
      "Content-Type": "application/json",
      Accept: "application/json"
    };

    const params = {
      page: skip,
      per_page: limit
    };

    const a = await axios.get(URL, { params, headers });
    // const b = [...new Set([...data, ...a.data.data])]; <-- setting this will thrown error
    setData(a.data.data);
    setLoading(false);
  };

  useEffect(() => {
    setLoading(true);
    loadData(page);
  }, [page]);

  useEffect(() => {
    console.log("page", page, "data", data.length);
  }, [page, data]);

  const doReset = evt => {
    evt.preventDefault();
    setPage(1);
  };

  const doLoadMore = evt => {
    evt.preventDefault();
    setPage(page + 1);
  };

  return (
    <div className="container">
      <h1>Listing</h1>
      <button className="btn text-primary" onClick={evt => doReset(evt)}>
        Reset
      </button>
      <button className="btn text-primary" onClick={evt => doLoadMore(evt)}>
        Load More
      </button>
      {isLoading && <p>Loading..</p>}
      {!isLoading && (
        <ul>
          {data.map(a => (
            <li key={a.id}>
              {a.id}. {a.email}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
};

render(<App />, document.getElementById("root"));

a fully working example in here. i think this code should be working, but is not.

const a = await axios.get(URL, { params, headers });
const b = [...new Set([...data, ...a.data.data])];
setData(b);

so please help, how to do Load More in React Hooks?

hbinduni
  • 1,170
  • 17
  • 27
  • 1
    What exactly isn't working? The example seems to load the next page correctly – Brian Thompson Oct 07 '19 at 14:18
  • this example only load 2 data item at a time. what i want basically add more data into the render list. for example, 1st click add 2 data, 2nd click add 2 more data into the list so the total become 4 data at the list, etc – hbinduni Oct 07 '19 at 14:22
  • What is the error when using the commented code? – Brian Thompson Oct 07 '19 at 14:26
  • it will show warning: `React Hook useEffect has a missing dependency: 'loadData'. Either include it or remove the dependency array` but if i add `loadData` into dependency, it will say `loadData should be inside useEffect callback or in its own useCallback hooks` – hbinduni Oct 07 '19 at 14:36

2 Answers2

1

after a few try, i think this is the best thing i can do. make the code working but also not let the compiler warning:

import React, { useState, useEffect, useCallback } from "react";
import axios from "axios";

import Navbar from "./Navbar";

const App = () => {
  const LIMIT = 2;
  const [tube, setTube] = useState([]);
  const [data, setData] = useState([]);
  const [isLoading, setLoading] = useState(false);
  const [page, setPage] = useState(1);

  const loadData = useCallback(
    async (limit = LIMIT) => {
      setLoading(true);
      const URL = "https://reqres.in/api/users";
      const headers = {
        "Content-Type": "application/json",
        Accept: "application/json"
      };

      const params = {
        page,
        per_page: limit
      };

      const a = await axios.get(URL, { params, headers });
      if (!a.data.data) {
        return;
      }

      setData(a.data.data);
      setLoading(false);
    },
    [page]
  );

  useEffect(() => {
    if (!isLoading) {
      return;
    }
    setTube([...new Set([...tube, ...data])]);
  }, [data, isLoading, tube]);

  useEffect(() => {
    loadData();
  }, [loadData]);

  useEffect(() => {
    console.log("page", page, "data", data.length);
  }, [page, data]);

  const doLoadMore = evt => {
    evt.preventDefault();
    setPage(page + 1);
  };

  return (
    <>
      <Navbar />

      <main role="main" className="container">
        <div className="starter-template text-left">
          <h1>Listing</h1>
          <button className="btn text-primary" onClick={evt => doLoadMore(evt)}>
            Load More
          </button>
          <ul>
            {tube &&
              tube.map(a => (
                <li key={a.id}>
                  {a.id}. {a.email}
                </li>
              ))}
          </ul>
          {isLoading && <p>Loading..</p>}
        </div>
      </main>
    </>
  );
};

export default App;

also i found, it could be much easier just apply this eslint-disable-next-line react-hooks/exhaustive-deps to let the compiler ignore the warning. something like this.

useEffect(() => {
    setConfig({...config, params: {...params, skip}});
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [skip]);

for information can be found on this:

hbinduni
  • 1,170
  • 17
  • 27
0

I got your example to work by changing to this:

const b = [...data, ...a.data.data];
setData(b);
Brian Thompson
  • 13,263
  • 4
  • 23
  • 43
  • it is the same effect with `const b = [...new Set([...data, ...a.data.data])]`. it will show a warning in browser/compiler. i can get rid the compiler warning with `// eslint-disable-next-line` but this is not the correct way to do it i think. – hbinduni Oct 07 '19 at 14:44
  • 1
    I just changed my answer. I believe this is your desired behavior. It shows 2, then 4, etc – Brian Thompson Oct 07 '19 at 14:58
  • yes, it work using your code or my comment code. but my issue was, it still give me compiler warning. i believe the correct solution was to `useCallback` but i am not sure how to do it. see it [react #6903](https://github.com/facebook/create-react-app/issues/6903) – hbinduni Oct 07 '19 at 15:22