3

I have the following iteration of option elements for a select list:

{allCustomers.map(customer => {
    console.log(customer.id);
    console.log(typeof(customer.id));
    return (
        <option
            key={customer.id}
        >
            {customer.name}
        </option>
    );
})}

The list of customers I have all have unique ids and the id property is of type number. Here is what I see in the console for the logs statements I have:

1
number
2
number
3
number
4

With the snippet above I kept getting:

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

Then I tried the following and React was happy:

key={'' + customer.id}

Is this the proper way to use numbers as key in React? What is the reason that React is thinking I have duplicate keys in case I leave them as numbers?

Edit This is the allCustomers list I have:

[
  {id: 1, name: "Test Customer - 1", orderSchedules: Array(1)}
  {id: 2, name: "Test Customer - 2", orderSchedules: Array(0)}
  {id: 3, name: "Another Test Customer", orderSchedules: Array(1)}
  {id: 4, name: "Foo Bar Baz", orderSchedules: Array(1)}
]

Edit - Full Code

import React from "react";

export default (props) => {
    const {
        orderSchedule,
        setOrderSchedule,
        allCustomers,
        saveHandler,
    } = props;

    return (
        <>
            <h3>Order Schedule Details</h3>
            <hr/>
            <form onSubmit={saveHandler}>
                <div className="form-group row">
                    <label htmlFor="order-schedule-detail-description-input">Order Schedule Description</label>
                    <input
                        id="order-schedule-detail-description-input"
                        type="text"
                        className="form-control"
                        value={orderSchedule.description}
                        onChange={(event) => {
                            setOrderSchedule({
                                ...orderSchedule,
                                description: event.target.value
                            })
                        }}/>
                    <br/>
                    <br/>
                    <select
                        className="custom-select"
                        onChange={event => {
                            setOrderSchedule({
                                ...orderSchedule,
                                customer: {
                                    id: event.target.value
                                }
                            });
                        }}
                    >
                        {allCustomers && allCustomers.map(customer => {
                            return (
                                <option
                                    value={customer.id}
                                    key={customer.id}
                                >
                                    {customer.name}
                                </option>
                            );
                        })}
                    </select>
                </div>
                <div className="form-group row">
                    <button type="submit"
                            className="btn btn-primary"
                    >
                        Save
                    </button>
                    &nbsp;
                </div>
            </form>
        </>
    );
}

Edit - My Container Class

import React, {useEffect, useState} from "react";
import {useParams} from "react-router-dom";
import * as orderScheduleController from "./orderScheduleController";
import * as customerController from "./../customer/customerController";
import OrderScheduleDetails from "./OrderScheduleDetails";

export default ({history}) => {
    let {id} = useParams();

    const [orderSchedule, setOrderSchedule] = useState({
        description: '',
        customer: {}
    });

    const [allCustomers, setAllCustomers] = useState([{}]);

    useEffect(() => {
        if (id) {
            orderScheduleController.getOrderSchedule(id)
                .then(response => {
                    setOrderSchedule(response.data);
                });
        }
    }, []);

    useEffect(() => {
        customerController.getAllCustomers()
            .then(response => {
                setAllCustomers(response.data);
            });
    }, []);

    function saveHandler(event) {
        event.preventDefault();
        if (!orderSchedule.description) {
            return;
        }
        orderScheduleController
            .createOrderSchedule(orderSchedule)
            .then(() => {
                history.push('/orderSchedules');
            });
    }

    return (
        <OrderScheduleDetails
            orderSchedule={orderSchedule}
            setOrderSchedule={setOrderSchedule}
            allCustomers={allCustomers}
            saveHandler={saveHandler}
        />
    )
}
Koray Tugay
  • 22,894
  • 45
  • 188
  • 319
  • Does this answer your question? [ReactJS : What is the best way to give keys in array element](https://stackoverflow.com/questions/49841086/reactjs-what-is-the-best-way-to-give-keys-in-array-element) [This article](https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318) may also be helpful. – jmargolisvt Feb 29 '20 at 17:55
  • @jmargolisvt No, that does not answer my question. – Koray Tugay Feb 29 '20 at 17:59
  • @jmargolisvt The article you linked is not related to my question either. – Koray Tugay Feb 29 '20 at 18:00
  • I think they answer "what is the proper way to use numbers as key in React?" They also explain why it's complaining. Do you specifically want to know why concatenating the space fools React here? – jmargolisvt Feb 29 '20 at 18:02
  • @jmargolisvt Using the index is bad for a very different reason. I already have unique numbers. I just simplified it to `1 2 3 4` for the sake of this question. I am not concatenating space anywhere btw, merely casting the number to a String. – Koray Tugay Feb 29 '20 at 18:07
  • @KorayTugay I think they meant concating the empty string (i.e. casting to string instead of number). As for *your* question, please post the relevant part of your package.json. Your code should have worked, knowing what versions you're on might help. – Jared Smith Feb 29 '20 at 18:08
  • 1
    @JaredSmith I am using `"react": "^16.12.0",` and `"react-dom": "^16.12.0",` and `"react-router": "^5.1.2",` and `"react-scripts": "^3.3.0",` and `"react-transition-group": "^2.5.1"`. – Koray Tugay Feb 29 '20 at 19:12

1 Answers1

3

Keys have to be unique across their siblings. It's possible that you have other children that are being rendered inside of the parent container that share one or more of the keys you’re specifying here. Your '' + customer.id hack would work because 1 === '1' is false.

coreyward
  • 77,547
  • 20
  • 137
  • 166
  • You are most likely right. When I try `customer.id + 10` everything is also fine again. But I can't find anything that may be clashing.. I posted the full component, could you take a look.. What are _siblings_? Where would I have a clash here, this is a form with one text input and one select list.. – Koray Tugay Feb 29 '20 at 19:10
  • @KorayTugay Just had a look at your code; any chance that `setOrderSchedule` adds a customer to the `allCustomers` array? Your `onChange` callback is setting an `id` property on an object assigned to a `customer` property to `e.target.value`, but your ` – coreyward Feb 29 '20 at 20:25
  • Sorry, it was bad copy/paste, I do have `value`. `setOrderSchedule` does not modify the array. I will paste the container I have too, but nevermind please, this is getting out of control and I do not want to steal your time. However, it is very strange since I do not have any loops or any keys anywhere else.. – Koray Tugay Feb 29 '20 at 20:28
  • @KorayTugay Just had a look; we’re all hung up on the “unique” bit, but the issue is your initial value to `useState` includes an empty object, i.e. one with an `id` of `undefined`, which prompts React to warn about a _missing_ key. – coreyward Feb 29 '20 at 20:32
  • Nah, it is not related.. Even if I start with `-1` there, I still have the same problem. And I said, if I add `customer.id + 10` all seems fine. Thank you for your support, I really do not want to steal your time on this one. I will accept your answer, I am unable to find myself what the problem is.. – Koray Tugay Feb 29 '20 at 20:34
  • I uploaded the project to https://drive.google.com/file/d/1NGbc8AKAUsMiZ3DRJpKBexU-KFi_di8S/view?usp=sharing It is a spring boot / react project in case you want to dig in deeper. If you need help starting the project (I doubt it) you can reach me at koraytugay et icloud dot com. I am not expecting you to look at it, sharing just in case if YOU want to dig in deeper.. Thank you for your time so far already. – Koray Tugay Feb 29 '20 at 20:38
  • You just need mvn clean install and start the `SpringReactApp.java` and `npm start` in `frontend` and you should be good to go from `localhost 3000`. I am sure you know already better, but just in case.. – Koray Tugay Feb 29 '20 at 20:39