SrdjaNo1
SrdjaNo1

Reputation: 899

How to use a custom hook with event handler?

I have created a custom Hook which fetches data from the server, sends dispatch to the store and returns data. It is usable if I want to list all comments in my app, however, I wanted to reuse it in the component where I need to fetch all comment replies, and that should happen only when certain button is clicked.

This is the hook down below.

import { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";

const useFetch = (url, options, actionType, dataType) => {
  const [response, setResponse] = useState([]);

  const dispatch = useDispatch();
  useEffect(() => {
    (async () => {
      const res = await fetch(url);
      const json = await res.json();
      setResponse(json);
    })();
  }, []);

  useEffect(() => {
    dispatch({ payload: response, type: actionType });
  }, [response]);
  const data = useSelector(state => state[dataType]);

  return data;
};

export default useFetch;

Inside of my component I need to fetch replies when a button is clicked

const ParentComment = ({ comment }) => {
  const handleShowMoreReplies = (e) => {
    e.preventDefault();

}
  let replies = useFetch(
    `/api/comment_replies?comment_id=${comment.id}`,
    null,
    "REPLIES_RECEIVED",
    "replies"
  );
  return (
    <div>
      <Comment comment={comment} />
      <div className="replies">
        {replies.map(reply => (
          <Comment key={reply.id} comment={reply} />
        ))}
          <a href="#" className="show_more" onClick={handleShowMoreReplies}>
            Show More Replies ({comment.replies_count - 1})
          </a>
      </div>
    </div>
  );
};

If I put useFetch call inside of the handler I hget an error that Hooks can't be called there, but I need to call it only when the button is clicked so I don't know if there is a way to implement that.

Upvotes: 1

Views: 2756

Answers (1)

Aspirin  Wang
Aspirin Wang

Reputation: 181

I think you have subtle problems in your useFetch hook

1.your useEffect is having dep of ${url} and ${actionType } which you need to define.

2.In order to call this hook by clicking the button, you need to expose the setUrl as follows

const useFetch = ( initialUrl, options, actionType, dataType) => {
  const [url, setUrl ] = useState(initialUrl);

  const dispatch = useDispatch();
  useEffect(() => {
    const fetchData = async () => {
      try {
        const res = await fetch(url);
        const data = await res.json();
        dispatch({ payload: data, type: actionType });
      } catch (error) {
       console.log(error);
      }
    };
    fetchData();
  }, [url, actionType]);

  const data = useSelector(state => state[dataType]);
  return [ data, setUrl ];
};

export default useFetch;

Then when you are trying to use this hook, you can

const [data, fetchUrl] = useFetch(
  `/api/comment_replies?comment_id=${comment.id}`,
  null,
  "REPLIES_RECEIVED",
  "replies"
);

Then every time you have a button you can simply call

fetchUrl(${yourUrl}). 

your hook will receive the new URL, which is the dep of your hook and rerender it.

Here is an related article https://www.robinwieruch.de/react-hooks-fetch-data

Upvotes: 1

Related Questions