Cthulhujr
Cthulhujr

Reputation: 433

React dynamic component list not updating correctly

Codesandbox link https://codesandbox.io/s/mjxrz2npzy

I am creating an app where users can click on an Add Timer button to create a limited number of custom countdown/countup timers, defined in a component CustomTimerRow. Each row has an icon that if they click it, it will remove the timer.

I have the following parent component containing the list of rows:

import React, { Component } from 'react';
import {connect} from 'react-redux';
import CustomTimerRow from './CustomTimerRow';
import AddRow from './AddRow';

const mapStateToProps = state => {
    return {customTimers:state.clock.customTimers}
  }; 

class ConnectedCustomTimers extends Component
{
    constructor(props)
    {
        super(props);
        this.buildCustomTimers = this.buildCustomTimers.bind(this);
    }

    buildCustomTimers()
    {
        let timerList = [];
        let index = 0;
        if(this.props.customTimers)
        {
            for(let t of this.props.customTimers)
            {
                timerList.push(<CustomTimerRow number={index++} timer={t}/>)
            }
        }
        return timerList;
    }

    render()
    {
        const elems = this.buildCustomTimers();
        const addRow = (this.props.customTimers.length < 8 ? <AddRow/>:<span/>);
        return (<div>
                    {elems}
                    {addRow}
                </div>
                    );
    }

}

const CustomTimers = connect(mapStateToProps, null)(ConnectedCustomTimers);
export default CustomTimers

My Redux store has the following actions for the timers:

    case constants.ADD_CUSTOM_TIMER:
        return {...state,customTimers:[...state.customTimers,action.payload]};
    case constants.REMOVE_CUSTOM_TIMER:
        const newTimers = state.customTimers.slice();
        newTimers.splice(action.payload, 1);
        return {...state,customTimers:newTimers};

Everything works perfectly fine when adding rows, however when I remove a row, it always removes the last row from the list, no matter if I select a row in the middle to remove, or if I remove the first row.

When I add logging, all the way through to CustomTimers' render everything looks correct. buildCustomTimers is returning a list of objects that I would expect to see. But once the return happens and React does its thing, I see a different result.

Am I going about adding and removing components wrong here? I'm using both state updates and the containing parent to determine what to display but it seems like re-rendering the updated component list is not working correctly.

UPDATE: I updated the code to have the following:

buildCustomTimers()
{
    let timerList = [];
    let index = 0;
    if(this.props.customTimers)
    {
        for(let t of this.props.customTimers)
        {
            timerList.push(<li key={index++}><CustomTimerRow key={index++} timer={t}/></li>)
        }
    }
    return timerList;
}

render()
{
    const elems = this.buildCustomTimers();
    const addRow = (this.props.customTimers.length < 8 ? <AddRow/>:<span/>);
    return (<div><ul>
                {elems}
                </ul>{addRow}</div>
                );
}

But that did not change the behavior at all so I do not beleive the List and Key answer may be the correct answer (though not discounting if there is another way to do it)

Upvotes: 2

Views: 5436

Answers (2)

Tuomas
Tuomas

Reputation: 169

For me, the solution was that not update tihs.setState(...) with reomved class object, only class parennt.removeChild(...). Otherwise only js object removed and ul/li item remains.

Upvotes: 0

pwor
pwor

Reputation: 91

Maybe the error is in your CustomTimerRow code but when you create a list of components in a loop you must add a "key" props. This key has to be unique.

Lists and Keys

If you try:

<CustomTimerRow number={index++} timer={t} key={t.eventType} />

You'll see that it's working. As t.eventType is not unique, this cannot be your definitive key, but this shows you how keys are important.

Upvotes: 5

Related Questions