Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Warning: flattenChildren(...): Encountered two children with the same key / Child keys must be unique

Yesterday I added react-router-dom to my project and now when I leave and come back to my Sky element in my nav, it reloads the sky and I get

Warning: flattenChildren(...): Encountered two children with the same key, element-id-50. Child keys must be unique; when two children share a key, only the first child will be used.

(the number 50 used above is just an example, it throws this error ~40 times each time all with different ids)

The problem seems to stem from here in my sky.js file:

componentWillMount() {
    this.props.dispatch(requestSkySetup());
    this.props.dispatch(requestAllElements());

    this.setState({loadedSky: true, loadedElements: true});
}

Since each time I'm going to another screen, this component is unmounting and then re-mounting when I come back.

When receiveSkySetup is finished, the render function in sky.js creates a bunch of divs called Sectors and each Sector creates a few divs called Slots.

Then inside of Slot.render I have:

return connectDropTarget(
            <div className={showOutline ? 'slot showOutline' : 'slot'} style={style} onClick={interactable ? this.handleClick : null}>
                {
                    elements
                        .map(e => (
                            <SkyElement
                                id={e.id}
                                key={`element-id-${e.id}`}
                                title={e.title}
                                size={150}
                                opacity={e.opacity}
                                glow={e.glow}
                                color={e.color}
                                sectorId={e.sectorId}
                                slotId={e.id}
                                dispatch={this.props.dispatch}
                                isDragging={false}
                                transformElement={false} />
                        ))
                }
            </div>
        );

The key element in the SkyElement call above is what's throwing the 40+ errors on each mounting.

Happy to provide more code if needed.

Any help would be hugely helpful. Thanks!

Edit: Console logging elements

Digging in a bit more, the items are doubling in my store.

So, on the 2nd render of the sky tab, the full list of element ids is ["0", "1", "2", "3", "4", "5", "6", "7", "17", "18", "19", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "77", "78", "0", "1", "2", "3", "4", "5", "6", "7", "17", "18", "19", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "77", "78"]

On the 3rd render, elements 0-78 (the ids that apply from the array above) will be added again to the array

In Slot.js

const mapStateToProps = ({elements}, ownProps) => {
    return {
        elements: getElementsBySlotId(elements, ownProps.id),
    };
};

elements here will be n times the number of loads that Sky has done.

In sky.js

const mapStateToProps = ({sky, elements}) => {
    return {
        sectors: getSky(sky).sectors,
        elements: getElementsByKeyName(elements, 'visibleElements'),
        unplacedElements: getElementsByKeyName(elements, 'unplacedElements'),
    };
};

Printing elements.length I see that they double here too. Slot.js is pulling from the same store, so that makes sense

In my elements/reducer.js

case 'receiveAllElements':
        const visibleElements = {};
        const unplacedElements = {};

        const elements = action.elements.reduce((result, index) => {
            result[`${index.id}`] = index;
            return result;
        }, {});

        const keys = Object.keys(elements);
        for (const key of keys) {
            const e = elements[key];

            if (e.sectorId === null) {
                unplacedElements[key] = e;
            } else {
                visibleElements[key] = e;
            }
        }

        const visibleIds = Object.keys(visibleElements);
        const unplacedIds = Object.keys(unplacedElements);
        console.log(visibleIds);
        console.log(unplacedIds); // logging these, the numbers are consistent and don't double, triple etc with each load

        return {
            ...state,
            elementsMap: {
                ...state.elementsMap,
                ...elements,
            },
            visibleElements: [...state.visibleElements, ...visibleIds],
            unplacedElements: [...state.unplacedElements, ...unplacedIds],
        };

Maybe something in there is causing the count to double?

like image 671
Zack Shapiro Avatar asked Jul 25 '17 21:07

Zack Shapiro


People also ask

How do you fix encountered two children with the same key?

The React error "Encountered two children with the same key" occurs when two or more of the elements we return from the map() method have the same key prop. To solve the error, provide a unique value for the key prop on each element or use the index parameter.

What can you do to fix the warning related to react keys?

The easiest fix for this is to create a separate component for the items you're mapping and add the key to that component. Create a new component above your existing component (or link to it your call).

How do you use the key in react?

A “key” is a special string attribute you need to include when creating lists of elements in React. Keys are used to React to identify which items in the list are changed, updated, or deleted. In other words, we can say that keys are used to give an identity to the elements in the lists.


Video Answer


1 Answers

The issue here was

    return {
        ...state,
        elementsMap: {
            ...state.elementsMap,
            ...elements,
        },
        visibleElements: [...state.visibleElements, ...visibleIds],
        unplacedElements: [...state.unplacedElements, ...unplacedIds],
    };

namely, the visibleElements (and unplacedElements values).

[...state.visibleElements, ...visibleIds] will concat 2 arrays so since this code was being hit each time I went back to the Sky tab, it was adding the new ids in ...visibleIds, to the array I already had in ...state.visibleElements and doubling values

like image 140
Zack Shapiro Avatar answered Oct 19 '22 05:10

Zack Shapiro