yung peso
yung peso

Reputation: 1796

React-How to solve an issue with list items and keys

I seem to be stuck trying to solve for unique key ID's for my list items. I thought I understood how unique keys work, but clearly I do not.

For my code below, having a key as key={index} is not enough, same goes for key={item}. I keep getting duplicate errors from my console or the fact that my children inside my list do not have key's.

Below is the component that is causing errors. I don't get why Warning: Encountered two children with the same key, is appearing. Wouldn't the children inside a list item (based off the same mapping iteration) want to have the same keys?

For my specific case, I can't use item.id as a key, that is not available for the data I'm using. I did try doing something like item.symbol (Im presenting stock market data) and again this was causing duplicate key errors.

How can I solve this?

export const MarketMovement = (props) =>
{
    const { financialData } = props;



    const priceAvgChangeASC = financialData[2] && sortAll(financialData[2], 'asc', 'price_avg_change')
        .filter((i) => i.price_avg_change < 0)
        .slice(0, 4)
        .map((stock, index) => (
            <Grid>
                <ListItem key={index}>
                    <ListItemAvatar>
                        <Avatar style={{background:GradientRankGreen[index]}}>
                            {index + 1}
                        </Avatar>
                    </ListItemAvatar>
                    <ListItemText
                        primary={stock.symbol}
                        secondary={financialData && financialData[5].filter((i) => i.symbol === stock.symbol).map(
                            filteredSymbol => (
                                <>
                                    {filteredSymbol.company}
                                </>
                            ))}
                    />
                </ListItem>
            </Grid>
        ));
    
    
    const priceAvgChangeDESC = financialData[2] && sortAll(financialData[2], 'desc', 'price_avg_change')
        .filter((i) => i.price_avg_change > 0)
        .slice(0, 4)
        .map((stock, index) => (
            <Grid>
                <ListItem key={index}>
                    <ListItemAvatar>
                        <Avatar style={{background:GradientRankRed[index]}}>
                            {index + 1}
                        </Avatar>
                    </ListItemAvatar>
                    <ListItemText
                        primary={stock.symbol}
                        secondary={financialData && financialData[5].filter((i) => i.symbol === stock.symbol).map(
                            filteredSymbol => (
                                <>
                                    {filteredSymbol.company}
                                </>
                            ))}
                    />
                </ListItem>
            </Grid>
        ));
    
    
    const volumeAvgChangeASC = financialData[2] && sortAll(financialData[2], 'asc', 'volume_avg_change')
        .filter((i) => i.volume_avg_change < -15)
        .slice(0, 4)
        .map((stock, index) => (
            <Grid>
                <ListItem key={index}>
                    <ListItemAvatar>
                        <Avatar style={{background: GradientPurple[index]}}>
                            {index + 1}
                        </Avatar>
                    </ListItemAvatar>
                    <ListItemText
                        primary={stock.symbol}
                        secondary={financialData && financialData[5].filter((i) => i.symbol === stock.symbol).map(
                            filteredSymbol => (
                                <>
                                    {filteredSymbol.company}
                                </>
                            ))} />
                </ListItem>
            </Grid>
        ));
           
        const volumeAvgChangeDESC = financialData[2] && sortAll(financialData[2], 'desc', 'volume_avg_change')
        .filter((i) => i.volume_avg_change > 10)
        .slice(0, 4)
        .map((stock, index) => (
            <Grid>
                <ListItem key={index}>
                    <ListItemAvatar>
                        <Avatar style={{background:GradientOrange[index]}}>
                            {index + 1}
                        </Avatar>
                    </ListItemAvatar>
                    <ListItemText
                        primary={stock.symbol}
                        secondary={financialData && financialData[5].filter((i) => i.symbol === stock.symbol).map(
                            filteredSymbol => (
                                <>
                                    {filteredSymbol.company}
                                </>
                            ))} />
                </ListItem>
            </Grid>
        ));
           
    
    
    return (
        <>
            <Container style={{ padding: "10px",  paddingBottom:"40px" }} maxWidth="md">
            <Typography style={{paddingTop:"50px",  paddingTop:"50px"  }}variant="h2">Market Movers</Typography>
            <Typography variant="overline">Stocks outpacing or declining in historical AVG's for price & volume</Typography>
                <Grid justify="center" direction='row' container spacing={5} alignItems="stretch" >
                <Grid item  xs={12} s={12} md={6} lg={6}>
                    <List dense='true'>
                        <Typography variant="h5">Price {<Chip label="Bullish" size="small"/>}</Typography>
                        <Typography variant="caption">Average Closing Price</Typography>
                        <Divider component="li" variant="middle" light="true"/>
                        {priceAvgChangeASC && priceAvgChangeASC.length ? priceAvgChangeASC :  <Typography variant="caption">No stock passes parameter</Typography>}
                        </List>
                    </Grid>
                    <Grid item xs={12} s={12} md={6} lg={6}>
                            <List dense='true'>
                            <Typography variant="h5">Price {<Chip label="Bearish" size="small"/>}</Typography>
                            <Typography variant="caption">Average Closing Price</Typography>
                                <Divider component="li" variant="middle" light="true"/>
                                {priceAvgChangeDESC && priceAvgChangeDESC.length ? priceAvgChangeDESC :  <Typography variant="caption">No stock passes parameter</Typography>}

                            </List>
                    </Grid>
                    <Grid item xs={12} s={12} md={6} lg={6}>
                            <List dense='true'>
                            <Typography variant="h5">Volume {<Chip label="Gaining" size="small"/>}</Typography>
                            <Typography variant="caption">Average Daily Volume</Typography>
                                <Divider component="li" variant="middle" light="true"/>
                                {volumeAvgChangeASC && volumeAvgChangeASC.length ? volumeAvgChangeASC :  <Typography variant="caption">No stock passes parameter</Typography>}
                            </List>
                    </Grid>
                    <Grid item xs={12} s={12} md={6} lg={6}>
                            <List dense='true'>
                            <Typography variant="h5">Volume {<Chip label="Losing" size="small"/>}</Typography>
                            <Typography variant="caption">Average Daily Volume</Typography>
                                <Divider component="li" variant="middle" light="true"/>
                                {volumeAvgChangeDESC && volumeAvgChangeDESC.length ? volumeAvgChangeDESC :  <Typography variant="caption">No stock passes parameter</Typography>}
                            </List>
                    </Grid>
            </Grid>  
            </Container>
        </>
    )
}

EDIT: The answered solution seems to solve the problem expect for one case in my code: As you can see, I have added the key inside the grid component as thats the first child. However Im still getting an error.

export const MarketSentiment = (props) =>
{
    const { financialData } = props;

    const ratingScaleASC = financialData && financialData[4] && sortAll(financialData[4], 'asc', 'rating_scale')
    .filter((i) => i.rating_scale < 2.5 )
    .slice(0, 4)
    .map((stock, index) => (
        <Grid key={index}>
        <ListItem>
        <ListItemAvatar>
        <Avatar style={{ backgroundColor: GreenRank[index]}}>
            {index + 1}
        </Avatar>
        </ListItemAvatar>
        <ListItemText
        primary={stock.symbol}
        secondary={financialData && financialData[5].filter((i) => i.symbol === stock.symbol).map(
            filteredSymbol => (
                <>
                    {filteredSymbol.company}
                </>
            ))}/>
        </ListItem>
        </Grid>
    ));

    const ratingScaleDESC = financialData.length && financialData[4] && sortAll(financialData[4], 'desc', 'rating_scale')
    .filter((i) => i.rating_scale > 3.5)
    .slice(0, 4)
    .map((stock, index) => (
        <Grid key={index}>
        <ListItem>
        <ListItemAvatar>
        <Avatar style={{ backgroundColor: RedRank[index]}}>
            {index + 1}
        </Avatar>
        </ListItemAvatar>
        <ListItemText
        primary={stock.symbol}
        secondary={financialData && financialData[5].filter((i) => i.symbol === stock.symbol).map(
            filteredSymbol => (
                <>
                    {filteredSymbol.company}
                </>
            ))}/>
        </ListItem>
        </Grid>
    ));

    const putCallDESC = financialData.length && financialData[3] && sortAll(financialData[3], 'desc', 'put_call_ratio')
    .filter((i) => i.put_call_ratio > 1.5)
    .slice(0, 4)
    .map((stock, index) => (
        <Grid key={index}>
        <ListItem>
        <ListItemAvatar>
        <Avatar style={{ backgroundColor: RedRank[index]}}>
            {index + 1}
        </Avatar>
        </ListItemAvatar>
        <ListItemText
        primary={stock.symbol}
        secondary={financialData && financialData[5].filter((i) => i.symbol === stock.symbol).map(
            filteredSymbol => (
                <>
                    {filteredSymbol.company}
                </>
            ))}/>
        </ListItem>
        </Grid>
    ));

    const putCallASC = financialData.length && financialData[3] && sortAll(financialData[3], 'asc', 'put_call_ratio')
    .filter((i) => i.put_call_ratio < .5)
    .slice(0, 4)
    .map((stock, index) => (
        <Grid key={index}>
        <ListItem>
        <ListItemAvatar>
        <Avatar style={{ backgroundColor: GreenRank[index]}}>
            {index + 1}
        </Avatar>
        </ListItemAvatar>
        <ListItemText
        primary={stock.symbol}
        secondary={financialData && financialData[5].filter((i) => i.symbol === stock.symbol).map(
            filteredSymbol => (
                <>
                    {filteredSymbol.company}
                </>
            ))}/>
        </ListItem>
        </Grid>
    ));

    
    return (
        <>
            <Container style={{ padding: "10px",  paddingBottom:"40px" }} maxWidth="md">
            <Typography variant="h2">Market Sentiment</Typography>
            <Typography variant="overline">an overview of sentiments based off stocks in your bucket. Investor sentiment are based off the number of put to calls. </Typography>
                <Grid justify="center" direction='row'  container spacing={5} alignItems="stretch">
                    <Grid item xs={12} s={12} md={6} lg={6}>
                    <List dense='true'>
                        <Typography variant="h5">Rating {<Chip label="Bullish" size="small"/>}</Typography>
                        <Typography variant="caption">Investor Sentiment</Typography>
                        <Divider component="li" variant="middle" light="true"/>
                        {putCallASC && putCallASC.length ? putCallASC :  <Typography variant="caption">No stock passes parameter</Typography>}
                        </List>
                    </Grid>
                    <Grid item xs={12} s={12} md={6} lg={6}>
                        <List dense='true'>
                        <Typography variant="h5">Rating {<Chip label="Bearish" size="small"/>}</Typography>
                        <Typography variant="caption">Investor Sentiment</Typography>
                        <Divider component="li" variant="middle" light="true"/>
                        {putCallDESC && putCallDESC.length ? putCallDESC :  <Typography variant="caption">No stock passes parameter</Typography>}
                        </List>
                    </Grid>
                    <Grid item xs={12} s={12} md={6} lg={6}>
                        <List dense='true'>
                        <Typography variant="h5">Rating {<Chip label="Buy" size="small"/>}</Typography>
                        <Typography variant="caption">Analyst Sentiment</Typography>
                        <Divider component="li" variant="middle" light="true"/>
                        {ratingScaleASC && ratingScaleASC.length ? ratingScaleASC :  <Typography variant="caption">No stock passes parameter</Typography>}
                        </List>
                    </Grid>
                    <Grid item xs={12} s={12} md={6} lg={6}>
                        <List dense='true'>
                        <Typography variant="h5">Rating {<Chip label="Sell" size="small"/>}</Typography>
                        <Typography variant="caption">Analyst Sentiment</Typography>
                        <Divider component="li" variant="middle" light="true"/>
                        {ratingScaleDESC && ratingScaleDESC.length ? ratingScaleDESC :  <Typography variant="caption">No stock passes parameter</Typography>}
                        </List>
                    </Grid>
                </Grid>  
            </Container>
        </>
    )
}

Error:

Each child in a list should have a unique "key" prop.

Check the render method of `ForwardRef(ListItemText)`. It was passed a child from MarketSentiment.

Upvotes: 0

Views: 990

Answers (1)

Raafat dev
Raafat dev

Reputation: 333

the direct child after the map() should have the unique key.

in your code:

     .... 
     ... 
     .map((stock, index) => (
         <Grid>
            <ListItem key={index}> // not the direct child 

the direct child is the Grid component

how it should be:

    .... 
    ... 
    .map((stock, index) => (
            <Grid key={index} > // put the unique key here 
                <ListItem >


EDIT:

you should implement the same fix to the other direct children every time you map over an array and return children .

If the direct child is React Fragmants, don't forget to change it from the Shorthand syntax to the normal syntax.

That means: to change: <> </> to: <React.Fragment></React.Fragment>, to be able to add the key property to it, since you can't add the property to the Shorthand version

in your code, please don't forget to fix the following ...

in your code:

    .... 
    ... 
    .map(
       filteredSymbol => (
         <>  // the direct child is React Fragmants, change it from the Shorthand to the normal React.Fragment. to be able to add the key property to it, since you can't add the property to the Shorthand version 
           {filteredSymbol.company}
         </>
     ))}/>

how it should be:

    .... 
    ... 
    .map((filteredSymbol, index)=> (
         <React.Fragment key={index}> // put the unique key here 
           {filteredSymbol.company}
         </React.Fragment>
     ))}/>

Upvotes: 4

Related Questions