React warning: unique "key" prop

Hi,

I keep getting the ‘Each child in a list should have a unique “key” prop’ error in my react component, but I have added it (please see code excerpt below)?

The original data is being loaded from mongodb and the id has the syntax ‘_id’.

//imports
import React, { useEffect, useState } from "react";

//styles
import "./Checklist.css";

//component
const Checklist = () => {
  //constants - allsymptoms is for original data from mongodb
  const [date, setDate] = useState("");
  const [allSymptoms, setAllSymptoms] = useState([""]);
  const [currentSymptoms, setCurrentSymptoms] = useState([allSymptoms]);
  const [cumulativeSymptoms, setCumulativeSymptoms] = useState([""]);

  //get intial sympoms from mongodb
  useEffect(() => {
    const fetchAllSymptoms = async () => {
      const response = await fetch("http://localhost:4000/api/symptoms");
      const json = await response.json();
      if (response.ok) {
        setAllSymptoms(json);
        setCurrentSymptoms(json);
      }
    };
    fetchAllSymptoms();
  }, []);

  //function to handle date selection
  const handleChange = (event) => {
    event.preventDefault();
    setDate(event.target.value);
  };

  //function to handle checkboxes
  const updateCheckStatus = (index) => {
    setCurrentSymptoms(
      currentSymptoms.map((currentSymptom, currentIndex) =>
        currentIndex === index
          ? { ...currentSymptom, checked: !currentSymptom.checked }
          : currentSymptom
      )
    );
  };

  //function to handle submit
  const handleSubmit = (event) => {
    event.preventDefault();
    //adding current day's symptoms to cumulative array
    const currentDaySymptoms = { date, ...currentSymptoms };
    setCumulativeSymptoms(() => [...cumulativeSymptoms, currentDaySymptoms]);
    //reset date to blank on form submit
    setDate("");
    //reset to unchecked on form submit
    const resetSymptoms = currentSymptoms.map((symptom) => {
      const resetSymptom = { ...symptom, checked: false };
      return resetSymptom;
    });
    setCurrentSymptoms(resetSymptoms);
  };

  //debugging only - just to check all symptoms are correct
  useEffect(() => {
    console.log(cumulativeSymptoms);
  }, [cumulativeSymptoms]);

  return (
    <div className="checklist-wrapper">
      <form onSubmit={handleSubmit}>
        <div className="datePicker">
          <label htmlFor="choose date">Choose date:</label>
          <input
            type="date"
            id="choose date"
            name="choose date"
            alt="date picker"
            value={date}
            onChange={handleChange}
          />
          <p>Selected Date: {date}</p>
        </div>

        <div className="checklist">
          {currentSymptoms &&
            currentSymptoms.map((symptom, index) => {
              console.log(symptom._id);
              return (
                <div key={symptom._id}>
                  <label>
                    <input
                      type="checkbox"
                      checked={symptom.checked}
                      onChange={() => updateCheckStatus(index)}
                      value={symptom.name}
                    />
                    {symptom.name}
                  </label>
                </div>
              );
            })}
        </div>
        <button type="submit">submit</button>
      </form>
    </div>
  );
};

export default Checklist;

I have added the ‘console.log(symptom._id)’ and this is displaying each of the id’s for each mapped item in the array as expected, so the syntax must be right, so I don’t understand why this key is not being recognised?

I’m hoping that someone much more experienced than me can point out an obvious error I must have made? I have spent hours on this (the joys of being a developer-in-training!!) and I would really appreciate any help. Apologies for the long code excerpt, this is my first question, so please do tell me if I’ve not asked it in the correct way.

Thank you!

I can’t see any flaws in rendering with map();

But.

currentSymptoms - your initial state for that:

I don’t understand why.
And I aslo think something like this will not be evaluated as falsy value.

Thus,

this && expression may actually lead to rendering before useEffect with fetching will fire. Because useEffect is async.

Try add more logs for currentSymptoms in the component - I would start there.

1 Like

By adding the key prop directly to the div element, the error message should no longer appear.

{currentSymptoms &&
  currentSymptoms.map((symptom, index) => {
    console.log(symptom._id);
    return (
      <div key={symptom._id}>
        <label>
          <input
            type="checkbox"
            checked={symptom.checked}
            onChange={() => updateCheckStatus(index)}
            value={symptom.name}
          />
          {symptom.name}
        </label>
      </div>
    );
  })}

1 Like

Hi Zaklina, thank you for coming back to me. I’m not sure I quite understand what you mean though, as I thought that’s what I’d already done, I’ve added it to the div element and I still get the error message?

Thanks Admit8490, I get that it looks like a duplication to have currentSymptoms & allSymptoms, I did that originally as I had future plans for currentSymtpoms and I anticipate that the data I save to mongodb for that collection will differ to the static allSymtpoms, which won’t change, and will be required for the initial load.

But maybe this is causing some of the issue, and as you say, may be causing rendering before useEffect, so thank you for that, I’ll take a look.

All advice/guidance much appreciated!

1 Like

thi is what i ment to say:
By adding the key prop to the div, React can efficiently update the DOM when the list changes, without having to re-render all the items in the list.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.