skip to Main Content

I was expecting just 10 pictures on initial load, but ended up getting 20 because of the functional update of the state.

Result:

if in use the normal approach the app will show the expected 10,i want to add if statement to the state so i need to use the functional update

app code below

import React, { useState, useEffect } from "react";
import { FaSearch } from "react-icons/fa";
import Photo from "./Photo";
const clientID = `? 
client_id=${import.meta.env.VITE_ACCESS_KEY}`;
const mainUrl = `https://api.unsplash.com/photos/`;
const searchUrl = `https://api.unsplash.com/search/photos/`;
function App() {
  const [loading, setLoading] = useState(false);
  const [photos, setPhotos] = useState([]);
  const [page, setPage] = useState(1);
  const [query, setQuery] = useState("");

  const fetchImages = async () => {
    setLoading(true);
    let url;
    // const urlPage = `&page=${page}`;
    const urlPage = `&page=${page}`;
    const urlQuery = `&query=${query}`;
    url = `${mainUrl}${clientID}${urlPage}`;

    try {
      const response = await fetch(url);
      const data = await response.json();
      console.log(data);
      setPhotos((oldPhoto) => {
        return [...oldPhoto, ...data];
      });
      setLoading(false);
    } catch (error) {
      console.log(error);
      setLoading(false);
    }
  };

  useEffect(() => {
    fetchImages();
  }, [page]);

  useEffect(() => {
    const event = window.addEventListener("scroll", () => {
      if (
        !loading &&
        window.innerHeight + window.scrollY >= document.body.scrollHeight - 2
      ) {
        setPage((oldPage) => {
          return oldPage + 1;
        });
      }
    });
    return () => window.removeEventListener("scroll", event);
  }, []);

  const handleSubmit = (e) => {
    e.preventDefault();
  };

  return (
    <main>
      <section className="search">
        <form className="search-form">
          <input
            type="text"
            placeholder="search"
            className="form-input"
            value={query}
            onChange={(e) => setQuery(e.target.value)}
          />
          <button type="submit" className="submit-btn" onClick={handleSubmit}>
            <FaSearch />
          </button>
        </form>
      </section>
      <section className="photos">
        <div className="photos-center">
          {photos.map((image) => {
            return <Photo key={image.id} {...image} />;
          })}
        </div>
        {loading && <h2 className="loading">loading...</h2>}
      </section>
    </main>
  );
}

export default App;

2

Answers


  1. What was causing the duplicated image was the react strict mode, when in development React executes all useEffects twice, to help catch some bugs. You could disable it (by removing the strictmode tags) to prevent this, but it’s not recommended.

    <StrictMode>
        <App />
    </StrictMode>
    

    So to stop this i added logic to the fetch, the param isFetchingMore, in the useEffect it’s used with the default value (false)

    Other thing that was necessary to make it work was to create refs for the loading and page, when you add a manual event listener, it doesn’t get the updated state, it get’s only the initial value. For that it’s necessary to use refs.

    Here are the changes:

    https://codesandbox.io/s/cocky-mopsa-5ho92v?file=/src/App.js

    Login or Signup to reply.
  2. I saw your code. In line 22, you add new photos to the old ones, that’s why if it runs twice, your state will contains twice of each photo. I changed it to normal

    setPhotos(data)
    

    I understand that you did it, because you want to lazyLoad the photos, by each fetch, new photos will be added. I would do it differently.
    First of all, you can easily use a native html attribute for images to lazy load:
    <img src="image.jpg" alt="..." loading="lazy" />

    check: https://developer.mozilla.org/en-US/docs/Web/Performance/Lazy_loading#images_and_iframes`

    This way you won’t have to deal with the problem inside your React logic. I saw that you have an <img/> tag inside your Photos component.

    With all respect to the other answers, I wouldn’t take my development environment out of strictMode only to solve an issue. In my opinion an app should work inside and outside strictMode, and that’s why I appreciate you asking this question here.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search