React functional component with useSelector and useDispatch creates loop

Viewed 7073

I'm sure this is a case of my brain just not getting it... BUT...

I'm used to using class Components and not functional components in general and with React Redux I'm trying to code a component that dispatches an action. The action of course causes a reducer to update the Redux state (store) as you probably know. Trying to replace mapStateToProps and mapDispatchToProps with useSelector and useDispatch however has me creating a loop... I'm guessing that I'm using useSelector incorrectly.

import { fetchPostsByTerm } from "../../_actions/_postActions";
import { useSelector, useDispatch } from "react-redux";

const payload = { vocabulary: "tags", term: "thiphif" };

export const PostsByTerm = () => {
  const dispatch = useDispatch();
  dispatch(fetchPostsByTerm(payload));
  const posts = useSelector((state) => state.postsByTerm);

  return (
    <div>
      <h1 className="post_heading">Posts</h1>
      {posts ? posts.map((post) => <h1>{post.entityLable}</h1>) : <h1>no posts</h1>}
    </div>
  );
};

maybe I am using it correctly? there are other components updating state on the same page

2 Answers

You must not dispatch directly in the functional component. Instead use a useEffect hook to perform a dispatch. If your objective is to only dispatch the action on initial render, pass on the dependency to useEffect as an empty array

export const PostsByTerm = () => {
  const dispatch = useDispatch();

  useEffect(() => {
    dispatch(fetchPostsByTerm(payload));  
  }, [])

  const posts = useSelector((state) => state.postsByTerm);

  return (
    <div>
      <h1 className="post_heading">Posts</h1>
      {posts ? posts.map((post) => <h1>{post.entityLable}</h1>) : <h1>no posts</h1>}
    </div>
  );
};

FYI - Commenting here because it was an "aha" moment for me to understand the purpose of the array [] in the useEffect utility. The array is used to house state variables which, when changed, will force execution of the function listed. So in my case actually since I wanted fetchPostsByTerm to execute when the selected term changed (not obvious since my example shows it hardcoded)

  useEffect(() => {
    dispatch(fetchPostsByTerm(payload));  
  }, [term])

... was what I eventually went with. And now it's working great! The articles for the selected term get fetch when a new term is selected.

Related