Search code examples
reactjsreact-hooksreact-functional-component

Render a new component without replacing the old one when state changes in Reactjs


I want to render the Box component when there is data in state variable data. And when the state updates(in the background data is being fetched from the server) I want to render another Box component with the new data and keep adding Box component everytime the state changes.

The problem is everytime the state changes, entire component gets replaced with the new Box component.

This is an overview of what I actually want to do without repeating the code -> just an example.

//box component
function Box(props){
    return(
        <div style={{ "height": props.height, "width": props.width, "backgroundColor": props.color }}></div>
    );
}

function boxMarkup(){

    if(data.length == 0){
        return "";
    }

    if(data.length == 1){
        return(
            <div className="col-1">
                <Box height={data.height + "px"} width={data.width + "px"} color={data.color} />
            </div>
        );
    }

    if(data.length == 2){
        return(
            <div className="col-2">
                {
                    data.map(item => <Box key={item.id} height={item.height + "px"} width={item.width + "px"} color={item.color} />)
                }
            </div>
        );
    }

    if(data.length == 4){
        return(
            <div className="col-4">
                {
                    data.map(item => <Box key={item.id} height={item.height + "px"} width={item.width + "px"} color={item.color} />)
                }
            </div>
        );
    }
}

//return component markup
return(
    <div>
        <EmailSent message={emailSentMessage.message} />
        {boxMarkup()}
    </div>
);

So basically I want to render a new Box component with new data while keeping the old Box components on the screen. Here is my BoxComponent code.


Solution

  • Currently every time the state updates, boxMarkup function gets executed. So every time only one of your if conditions gets executed and only 1 item is returning from the boxMarkup function.

    In order to fix it, you should declare a new state which holds all of your elements and then update it in your boxMarkup function every time your data gets updated:

    const [boxItems, setBoxItems] = useState([]);
    
    useEffect(() => {
      boxMarkup();
    }, [data]);
    
    function boxMarkup(){
      const newBox = data.length == 0 ? "" : data.length === 3 ? null :
        (
          <div className=`col-${data.length}`>
            data.map(item => (
              <Box height={item.height + "px"} width={item.height + "px"} color={item.color} />
            )
          </div>
        );
      setBoxItems([...boxItems, newBox]);
    }
    

    And finally render your UI:

    return(
        <div>
            <EmailSent message={emailSentMessage.message} />
            {boxItems}
        </div>
    );
    

    Update: You faced key error when data.length === 1 because you tried to use data.height, data.width, data.color. Instead you should use data[0].height, data[0].width, data[0].color.