1

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.

1 Answer 1

1

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

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! This works if I pass null as initial value for url. js const [data, fetchUrl] = useFetch( null, null, "REPLIES_RECEIVED", "replies" ); If I pass the right url, since it is at the top level, every reply is immediately rendered, however if it is null, then url is set only on click. Thanks again!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.