0

I cannot figure out for the life of me why the first pair of onDoubleClick fires 500 server error but on all subsequent onDoubleClicks, it fires 200 correctly but with inconsistent behavior depicted below.

For illustration purposes, this is what's happening:

  1. double click on image A for user ID 1 -> 500 error
  2. double click on image A for user ID 1 -> 200 success (updates successfully for user ID 1)
  3. double click on image B for user ID 2 -> 200 success (updates successfully for user ID 1 when it should've been updated for user ID 2)
  4. and so on....

I've checked all over SO, even tried all of the suggestions on the most popular question asked on this topic on SO but to no avail. I've hit a brick wall.

I believe it has something to do with the asynchronous nature of hooks and how I'm defining them but I'm not entirely sure.

How can I make it so that upon double clicking any image, the corresponding data gets sent to the server correctly on the first pair of double clicks as well as any subsequent pair double clicks (i.e. on a different image)?

For illustration purposes, this how I'd like it to behave:

  1. double click on image A for user ID 1 -> 200 success (updates successfully for user ID 1)
  2. double click* on image b for user ID 2 -> 200 success (updates successfully for user ID 2)
  3. and so on....
const Images = () => {
  let authToken = localStorage.getItem("token");

  const [uploadsData, setUploadsData] = useState([]);
  const [likedPhotoUserId, setLikedPhotoUsedId] = useState("");
  const [like, setLike] = useState(0);

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

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

  const getUploads = () => {
    const headers = {
      Accept: "application/json",
      Authorization: `Bearer ${authToken}`,
    };

    axios
      .get("http://localhost:8005/api/get-user-uploads-data", { headers })
      .then((resp) => {
        console.log(resp.data);
        setUploadsData(resp.data);
      })
      .catch((error) => {
        console.log(error);
      });
  };

  const handleLikesBasedOnUserId = (e) => {
    setLikedPhotoUsedId(e);
    sendUserLikePost();
  };

  const sendUserLikePost = () => {
    const url = "http://localhost:8005/api/post-user-like";

    const headers = {
      Accept: "application/json",
      Authorization: `Bearer ${authToken}`,
    };

    let data = {
      like: like,
      UserID: likedPhotoUserId,
    };

    console.log(data);

    axios
      .post(url, data, { headers })
      .then((resp) => {
        console.log(resp.data);
      })
      .catch((error) => {
        console.log(error);
      });
  };

  const displayUploadsData = () => {
    return uploadsData.map((photos, index) => {
      return (
        <ImagesGrid
          src={photos.url}
          likes={photos.likes}
          userName={photos.name}
          key={index}
          doubleClick={handleLikesBasedOnUserId}
          value={photos.UserID}
        />
      );
    });
  };

  return <>{displayUploadsData()}</>;
};

const ImagesGrid = (props) => {
  const createUserPhotoNodes = () => {
    return (
      <section className="gallery">
        <div className="container">
          <form method="POST" name="likes">
            <div className="img-container">
              <img
                src={props.src}
                alt="Photo"
                className="gallery-img"
                onDoubleClick={() => props.doubleClick(props.value)}
              />
              <h2 className="userName">{props.userName}</h2>
              <h2 className="likes" onChange={props.onChange}>
                Likes {props.likes}
              </h2>
            </div>
          </form>
        </div>
      </section>
    );
  };

  return <>{createUserPhotoNodes()}</>;
};
AKX
  • 152,115
  • 15
  • 115
  • 172
  • For what it's worth, the general consensus among web usability people is that double-click is a terrible idea, because almost everything people encounter on websites are single-click action elements (buttons etc). – Pointy Feb 17 '22 at 14:51
  • @Pointy For what it's worth, this is clearly emulating Instagram's UX, where double-clicking/double-tapping an image is the like action. – AKX Feb 17 '22 at 14:52
  • @AKX yes that's correct, this is just a project I've created to see how that whole process works. – luvs2spooge Feb 17 '22 at 14:55
  • @AKX that may be true, however double-clicking is objectively problematic on the web because it is pretty rare, also browsers *also* fire the first click action, or at least they used to. Instagram is sort-of special in that it seems almost completely oriented towards touch devices (phones, mainly), not mouse-oriented "click" clients. – Pointy Feb 17 '22 at 14:59

1 Answers1

1

You have hit the nail on the head – it's because setState is asynchronous and you're seeing stale props or state to boot.

With

const handleLikesBasedOnUserId = (e) => {
    setLikedPhotoUsedId(e);
    sendUserLikePost();
};

const sendUserLikePost = () => {
    // ...
    let data = {
        'like': like,
        'UserID': likedPhotoUserId
    };
    // ...

likedPhotoUserId will not have updated before you call sendUserLikePost, so likedPhotoUserId is ''. (By the time you call handleLikes... again it will have the "previous" value.)

I don't see a reason why likedPhotoUserId should be a state atom anyway, since all you use it for is sendUserLikePost – just make it a regular ol' parameter:

const handleLikesBasedOnUserId = (e) => {
    sendUserLikePost(e);
};

const sendUserLikePost = (likedPhotoUserId) => {
    // ...
    let data = {
        'like': like,
        'UserID': likedPhotoUserId
    };
    // ...
AKX
  • 152,115
  • 15
  • 115
  • 172