The react component doesn't re-render after state change

Viewed 35

I have a React project where in the useEffect hook there is an eventListener with which the data of the event will be displayed. At the first attempt it works well but when another event is coming and I changed the state of the array which contains the elements which need to be displayed the component doesn't rerender.

App.js

import React, { useEffect, useReducer, useState } from "react";

function App(){
    const [dataTableElements, setDataTableElements] = useState([]);

    const sendMessage = (message) => {
        window.opener.postMessage(message, '*');
    }

    const receiveMessage = (e) => {
        if (e.data.source === 'source') {
            if (e.data.type === 'SEND_DATA') {
                const message = e.data.message;
                createElements(message);
            }
        } else {
            return;
        };
    };

    const createElements = (data) => {
        var temp = Object.assign([], dataTableElements);
        temp = [];
        {Object.keys(data).forEach((value, index) => {
            temp.push(<MyComponent key={value + " " + index} data={data[value]} />)
        })}
        
        setDataTableElements(temp);
    }

    useEffect(() => {
        sendMessage(message_READY);
        window.addEventListener("message", receiveMessage, false);

        return () => window.removeEventListener("message", receiveMessage);

    }, []);

    return (
        <Layout className="mainLayout">
            <Header>
                <AppHeader />
            </Header>
            <Content>
                <div className="tablesContainer">
                    {dataTableElements}
                </div>
            </Content>
        </Layout>
    );
    
}

export default PopupWindow;

1 Answers

I think the source of your problem is your useEffect function runs only when your component renders for the first time so it assign a function to your eventListener but this function is always the same and with same parameters, so when ever the event is dispatched it will run the same function with same parameter and then set you state to the same value that was before so it considers it as the state hasn't changed, You have to watch for your function on your useEffect and put your function on useCallback hook to prevent infinite loop

import React, { useEffect, useState } from "react";
import { useCallback } from "react";

function App(){
    const [dataTableElements, setDataTableElements] = useState([]);

    const sendMessage = (message) => {
        window.opener.postMessage(message, '*');
    }

    const receiveMessage = useCallback((e) => {
        if (e.data.source === 'source') {
            if (e.data.type === 'SEND_DATA') {
                const message = e.data.message;
                createElements(message);
            }
        } else {
            return;
        };
    },[]);

    const createElements = (data) => {
        var temp = Object.assign([], dataTableElements);
        temp = [];
        {Object.keys(data).forEach((value, index) => {
            temp.push(<MyComponent key={value + " " + index} data={data[value]} />)
        })}
        
        setDataTableElements(temp);
    }

    useEffect(() => {
        sendMessage(message_READY);
        window.removeEventListener("message", receiveMessage); //here to prevent creating mutilple event listeners for same event , I am remove any old event and creating a new one
        window.addEventListener("message", receiveMessage, false);

        return () => window.removeEventListener("message", receiveMessage);

    }, [receiveMessage]);

    return (
        <Layout className="mainLayout">
            <Header>
                <AppHeader />
            </Header>
            <Content>
                <div className="tablesContainer">
                    {dataTableElements}
                </div>
            </Content>
        </Layout>
    );
    
}

export default PopupWindow;

Related