Home > Enterprise >  "Unhandled Rejection (Error): Too many re-renders..." because I'm setting state withi
"Unhandled Rejection (Error): Too many re-renders..." because I'm setting state withi

Time:10-29

I'm getting a "Unhandled Rejection (Error): Too many re-renders. React limits the number of renders to prevent an infinite loop." message for the following code. Not sure what is causing this issue.

I think it's because I'm calling the setNewNotifications(combineLikesCommentsNotifications) within the users.map loop. But if I move setNewNotifications(combineLikesCommentsNotifications) outside of the loop, it can no longer read likeNewNotifications / commentNewNotifications. What is the best approach to this?

Code below, for context, users returns:

const users = [
  {
    handle: "BEAR6",
    posts: undefined,
    uid: "ckB4dhBkWfXIfI6M7npIPvhWYwq1"    
  },
  {
    handle: "BEAR5",
    posts: [
      {
        comment: false,
        handle: "BEAR5",
        key: "-Mmx7w7cTl-x2yGMi9uS",
        like: {
          Mn4QEBNhiPOUJPBCwWO: {
            like_notification: false,
            postId: "-Mmx7w7cTl-x2yGMi9uS",
            postUserId: "rFomhOCGJFV8OcvwDGH6v9pIXIE3",
            uid: "ckB4dhBkWfXIfI6M7npIPvhWYwq1",
            userLikeHandle: "BEAR6"
        }},
        post_date: 1635260810805,
        title: "hello"
      },
      {
        comment: false,
        comments_text: {0: {
          comment_date: 1635399828675,
          comment_notification: false,
          commenter_comment: "hi1",
          commenter_handle: "BEAR6",
          commenter_uid: "ckB4dhBkWfXIfI6M7npIPvhWYwq1",
          key: "-Mn4QF1zT5O_pLRPqi8q"
        }},
        handle: "BEAR5",
        key: "-MmxOs0qmFiU9gpspEPb",
        like: {
          Mn4QDCOrObhcefvFhwP: {
            like_notification: false,
            postId: "-MmxOs0qmFiU9gpspEPb",
            postUserId: "rFomhOCGJFV8OcvwDGH6v9pIXIE3",
            uid: "ckB4dhBkWfXIfI6M7npIPvhWYwq1",
            userLikeHandle: "BEAR6"}, 
          Mn4QKEk95YG73qkFsWc: {
            postId: "-MmxOs0qmFiU9gpspEPb",
            postUserId: "rFomhOCGJFV8OcvwDGH6v9pIXIE3",
            uid: "rFomhOCGJFV8OcvwDGH6v9pIXIE3",
            userLikeHandle: "BEAR5"
           }},
        post_date: 1635265250442,
        title: "hi" 
      }
    ],
    uid: "rFomhOCGJFV8OcvwDGH6v9pIXIE3"    
  }
]

Code

import React, { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';


export default function Notifications() {
  const [newNotifications, setNewNotifications] = useState('')

  const users = useSelector(state => state.users)

  return users.map((post) => {


      if(post.posts){
        return post.posts.map((postContent) => {
          const likes = postContent.like ? Object.values(postContent.like) : null
          const comments = postContent.comments_text ? Object.values(postContent.comments_text) : null

          const likeNewNotifications = likes ? likes.filter(post => {
            return post.like_notification === false
          } ) : null

          const commentNewNotifications = comments ? comments.filter(post => {
            return post.comment_notification === false
          } ) : null

          const combineLikesCommentsNotifications = likeNewNotifications.concat(commentNewNotifications)
          setNewNotifications(combineLikesCommentsNotifications)
        }
        )
}

    return (
      <div>
          <p>
              {newNotifications}
          </p>
      </div>
    );
 }
)
}

CodePudding user response:

There are multiple errors. But lets face it step by step.

I'll copy and paste your code, but with extra comments, to let you know where I'm referencing:

import React, { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';


export default function Notifications() {
  const [newNotifications, setNewNotifications] = useState('')

  const users = useSelector(state => state.users)

  // Step 0: I guess the error is because this users.map is running everytime (with any update in the component. So, when you set a new state, it'll render again. So, you have to do this, probably 2 times: on mount and after one update. 

  // Step 1: You're using users.map but it returns a new array. My recommendation would be: use users.forEach instead.
  return users.map((post) => {


      if(post.posts){
        return post.posts.map((postContent) => {
          const likes = postContent.like ? Object.values(postContent.like) : null
          const comments = postContent.comments_text ? Object.values(postContent.comments_text) : null

          const likeNewNotifications = likes ? likes.filter(post => {
            return post.like_notification === false
          } ) : null

          const commentNewNotifications = comments ? comments.filter(post => {
            return post.comment_notification === false
          } ) : null

          const combineLikesCommentsNotifications = likeNewNotifications.concat(commentNewNotifications)
          setNewNotifications(combineLikesCommentsNotifications)
        }
        )
}

    return (
      <div>
          <p>
              {newNotifications}
          </p>
      </div>
    );
 }
)
}

(Read Step 0 and Step 1 as comments in the code) Also, about:

But if I move setNewNotifications(combineLikesCommentsNotifications) outside of the loop, it can no longer read likeNewNotifications / commentNewNotifications. What is the best approach to this?

You can do Step 3: To be able to do that, you can use let, set one variable in the parent of the loop and update the value inside the loop (or if you have an array can push even if it's const). it'd be like:

function foo() {
  const users = [{}, {}, {}, {}];
  const usersWithEvenId = [];

  users.forEach(user => {
    if (user.id % 2 === 0) {
      usersWithEvenId.push(user)
    }
  })
}

Taking in consideration these 3 steps the resulted code would be like:

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

export default function Notifications() {
  const [newNotifications, setNewNotifications] = useState('');
  const users = useSelector(state => state.users);

  // Function to get new posts
  const getNewPosts = () => {
    const notifications = [];
    users.forEach((user) => {
      if (user.posts) {
        posts.forEach((post) => {
          // Your logic;
          notifications.push(newNotifications)
        })
      }
    });
    setNewNotifications(notifications);
  };

  // Run to get newPosts on mount (but also in any other moment)
  useEffect(() => {
    getNewPosts();
  }, [])

  return (
    <div>
      <p>
        {newNotifications}
      </p>
    </div>
  );
}


CodePudding user response:

Maybe you can write the code like this:

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

export default function Notifications() {
  const users = useSelector((state) => state.users);

  const combineLikesCommentsNotifications = users.map((post) => {
    if (post.posts) {
      return post.posts.map((postContent) => {
        const likes = postContent.like ? Object.values(postContent.like) : null;
        const comments = postContent.comments_text
          ? Object.values(postContent.comments_text)
          : null;

        const likeNewNotifications = likes
          ? likes.filter((post) => {
              return post.like_notification === false;
            })
          : null;

        const commentNewNotifications = comments
          ? comments.filter((post) => {
              return post.comment_notification === false;
            })
          : null;

        const combineLikesCommentsNotifications = likeNewNotifications.concat(
          commentNewNotifications
        );
        setNewNotifications(combineLikesCommentsNotifications);
      });
    }else{
      return [];
    }
  })

  const [newNotifications, setNewNotifications] = useState(combineLikesCommentsNotifications);


  return (
    <div>
      <p>{newNotifications}</p>
    </div>
  ); ;
}
  • Related