React component (react-redux props) keeps rerendering with React.memo()

408 views Asked by At

Im building a chat application with react, react-redux and socket.io. Now, to improve the performance of the app i've add React.memo() to my <Message ... /> component to prevent the rerenderings. However, according to React Profiler all of my Message components are keeping rerendering, as soon as i fetch further messages. My code:

room.jsx (message container)

import { useSelector, useDispatch } from "react-redux";
import {
  fetchMessagesRequest,
  fetchMessagesSuccess,
  fetchMessagesFailure,
  fetchPageMessagesSuccess,
  fetchPageMessagesFailure,
} from "../../redux/actions";

const Room = ({ match, history }) => {

  const dispatch = useDispatch();
  const socket = useSelector((state) => state.socket);
  const room = useSelector((state) => state.room);
  const user = useSelector((state) => state.user);

    <section className='room__content'>
                {room.messages.length ? (
                  <React.Fragment>
                    {room.messages.map((msg, idx) =>
                      idx + 1 === room.messages.length ? (
                        <Message
                          key={msg._id}
                          reference={lastMessageRef}
                          msg={msg}
                          text={msg.message}
                          file={msg.file ? msg.file : ""}
                          date={msg.creationDate}
                          state={msg.state}
                          deleteMessage={() => deleteMessage(msg._id)}
                          likeMessage={() =>
                            broadcastLike(msg._id, user.data.userID)
                          }
                        />
                      ) : (
                        <Message
                          key={msg._id}
                          msg={msg}
                          text={msg.message}
                          file={msg.file ? msg.file : ""}
                          date={msg.creationDate}
                          state={msg.state}
                          deleteMessage={() => deleteMessage(msg._id)}
                          likeMessage={() =>
                            broadcastLike(msg._id, user.data.userID)
                          }
                        />
                      )
                    )}
                    {preload && <Preloader type='inline' />}
                  </React.Fragment>
                ) : (
                  <Placeholder
                    text='No messages'
                    icon='icon icon--bubbles'
                    type='full'
                  />
                )}
              </section>  
    
...

export default withRouter(Room);

message.jsx

import React, { useState, useEffect } from "react";
import "./message.scss";

import { LazyLoadImage } from "react-lazy-load-image-component";

/* REDUX */
import { useSelector, useDispatch } from "react-redux";
import { showGallery, showModal, setMessage } from "../../redux/actions";

const Message = ({
  reference,
  msg,
  text,
  file,
  date,
  state,
  deleteMessage,
  likeMessage,
}) => {
  const [loaded, setLoaded] = useState(false);
  const user = useSelector((state) => state.user);
  const dispatch = useDispatch();

  useEffect(() => {
    let mounted = true;

    axios
      .get(...)
      .then()
      .catch()
      .finally(() => setLoaded(true));

    // CLEANUP
    return () => (mounted = false);
  }, []);

  return (
    <React.Fragment>
      {loaded ? (
        <figure
          ref={reference}
          className={`message${author.tag === user.data.tag ? "--author" : ""}`}
        >
            <div className='message__content'>
              <p className='message__content__text'>{text}</p>
            </div>
        </figure>
      ) : (
        ""
      )}
    </React.Fragment>
  );
};

export default React.memo(Message);

roomReducer.js

...

case "FETCH_PAGE_MESSAGES_SUCCESS":
  const messages = [...action.payload.messages, ...state.messages];

  return {
    ...state,
    messages: messages
      .filter(
        (v, i, a) =>
          a.findIndex((t) => JSON.stringify(t) === JSON.stringify(v)) === i
      )
      .sort((a, b) => new Date(b.creationDate) - new Date(a.creationDate)),
    total: action.payload.total,
    error: [],
  };

...

Profiler

enter image description here

1

There are 1 answers

0
frx On

This is happening because one or more of the dependencies(props) of Message components are getting updated when you are fetching messages. Check if there is any prop depend upon the fetch msg action.

If there are functions which you are passing to Message component then please wrap them in useCallback hook.

And still if the problem exists you can pass a function to check the prevProps and nextProps in React.memo

 const isEqual = (prevProps, nextProps) => {}
 React.memo(Message, isEqual)