0

I am trying to use a custom hook to fetch data and pass it to a component. However, the component ends up rendering multiple times (going beyond 100), as shown in the image below

enter image description here

This is the component that consumes the custom hook (userId) is a prop that is handed down from the parent component

export default function AuthorDetails({ userId }) {
  const { articleAuthor, authorError, authorLoading } = useGetAuthor(userId);

  return (
    <div className="authordetails__container">
      <div className="authorDetails__content">
        {console.log(articleAuthor)}
        {authorLoading && <p>Loading...</p>}
        {articleAuthor && (
          <p>
            <span>{articleAuthor.author}</span>
            {articleAuthor.twitter && (
              <span>
                <img src={twittericon} alt="twitter" />
              </span>
            )}
            {articleAuthor.facebook && (
              <span>
                <img src={facebookicon} alt="facebook" />
              </span>
            )}
            {articleAuthor.website && (
              <span>
                <img src={websiteicon} alt="website" />
              </span>
            )}
          </p>
        )}
        {authorError && <p>{authorError}</p>}
      </div>
    </div>
  );
}

This is the code for the custom hook

export default function useGetAuthor(userId) {
  const { currentUser } = useAuth();
  const [articleAuthor, setArticleAuthor] = useState();
  const [authorError, setAuthorError] = useState();
  const [authorLoading, setAuthorLoading] = useState(false);

  useEffect(() => {
    async function getAuthor() {
      try {
        setAuthorLoading(true);
        if (currentUser && currentUser.id === userId) {
          setArticleAuthor(currentUser);
          setAuthorLoading(false);
        } else {
          const data = await database.users.doc(userId).get();
          setArticleAuthor(database.formatDocument(data));
          setAuthorLoading(false);
        }
      } catch (err) {
        setAuthorLoading(false);
        console.log(err);
        setAuthorError("Something went wrong! Please refresh to try again");
      }
    }
    getAuthor();
  });

  return { articleAuthor, authorLoading, authorError };
}
2
  • First off, you are console logging in the render return as an unintentional side-effect, the render is to be a pure function, so any number of console logs you are seeing isn't an accurate measure of component rendering. You first need to console log correctly within the React component lifecycle, i.e. move the console.log(articleAuthor) into a useEffect without the dependency so you log once per useEffect call per render cycle, then track how many unexpected rerenders occur. Commented Jul 13, 2021 at 22:11
  • @DrewReese I have placed the console.log(articleAuthor) in the useEffect without the dependency. It does indeed show that the component is rendering multiple times Commented Jul 13, 2021 at 22:32

1 Answer 1

1

Because your useEffect call doesn't specify any dependencies it will run every time the component renders, and every state update will trigger a new render. Add currentUser as a dependency:

useEffect(() => {
    async function getAuthor() {
      try {
        setAuthorLoading(true);
        if (currentUser && currentUser.id === userId) {
          setArticleAuthor(currentUser);
          setAuthorLoading(false);
        } else {
          const data = await database.users.doc(userId).get();
          setArticleAuthor(database.formatDocument(data));
          setAuthorLoading(false);
        }
      } catch (err) {
        setAuthorLoading(false);
        console.log(err);
        setAuthorError("Something went wrong! Please refresh to try again");
      }
    }
    getAuthor();
  }, [currentUser]);
Sign up to request clarification or add additional context in comments.

4 Comments

The hook runs every render and should be fixed as shown above but, it's not causing the 100 renders. I think there's something up the tree causing AuthorDetails to re-render over and over.
Tried it, but I am still getting multiple renders with the currentUser as a dependency as well as when I include both currentUser and userId in the dependency array.
@nlta I managed to trace the problem high up in the component tree as indicated. Turns out one of the parent components using hooks needed me to include a dependency array, but I didn't, which caused it to render multiple times causing the child components to rerender as well. Thank you
@AlexMasinde Nice! It's worth trying out a linter like ESLint (rule is called exhaustive-deps). This particular issue is super easy for a linter to spot and on the flip side can be very hard to track down in a large project.

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.