Miscellaneous

Fixing React’s ‘Each child in a list should have a unique “key” prop’ Warning

August 12, 2022

When developing in React there’s one error I commonly see in the console while debugging and it’s always this one:

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

<insert meme about harry, hermoine and ron>

Thankfully this is a very simple problem to fix, as the warning implies you just need to make sure that each iteration of the <li> tag has it’s own unique key assigned to it. Take, for example, this code:

import * as React from "react";

interface IProps {
   Status: string;
}

interface IError {
   Status: "400" | "401" | "402" | "403" | "404" | "500";
   Description: string;
   Color: string;
}
const ReactExample: React.FC<IProps> = React.memo(( props ) => {
   const [errors, setErrors] = React.useState<IError[]>([]);

   React.useEffect(() => {
    if (props.Status == "400") {
	 newErrors.push({
		Status: "400",
		Description: "Bad request",
		Color: "yellow"
	 });
    }
    if (props.Status == "401") {
	 newErrors.push({
		Status: "401",
		Description: "Unauthorized",
		Color: "yellow"
	 });
    }
	if (props.Status == "402") {
	 newErrors.push({
		Status: "402",
		Description: "Payment Required",
		Color: "yellow"
	 });
    }
	if (props.Status == "403") {
	 newErrors.push({
		Status: "403",
		Description: "Forbidden",
		Color: "red"
	 });
    }
	if (props.Status == "404") {
	 newErrors.push({
		Status: "404",
		Description: "Not found",
		Color: "yellow"
	 });
	}
	if (props.Status == "500") {
	 newErrors.push({
		Status: "500",
		Description: "Internal server error",
		Color: "red"
	  });
    } 
    setErrors(newErrors);
   }, []);

   return (
      <div className="http-status-errors">
         {errors.length > 0 ? (
            <ul>
               {errors.map((error: IError) => (
                  <li className={"color-" + error.Color}>
                     <span>{error.Description}</span>
                  </li>
               ))}
            </ul>
         ) : null}
      </div>
   );
});

export default ReactExample;

To fix the error for this example you will want to not only add a key to the <li> tag but you will also need something you can pull a unique value from. Your absolute best bet will always be to use a GUID, and if working with an API you will ideally have one returned with the dataset. However in this example you’ll notice IError does not contain a GUID that we can use, however the Status property will be unique, so one solution might look like:

   return (
      <div className="http-status-errors">
         {errors.length > 0 ? (
            <ul>
               {errors.map((error: IError) => (
                  <li key={error.Status} className={"color-" + error.Color}>
                     <span>{error.Description}</span>
                  </li>
               ))}
            </ul>
         ) : null}
      </div>
   )

Unfortunately you aren’t always so lucky as to get a unique identity back with your data, so what do you do in that case? Well, as a last resort we can simply add an iterator to our map call and use that:

import * as React from "react";

interface IProps {
   Status: string;
}

interface IError {
   Status: "400" | "401" | "402" | "403" | "404" | "500";
   Description: string;
   Color: string;
}
const ReactExample: React.FC<IProps> = React.memo(( props ) => {
   const [errors, setErrors] = React.useState<IError[]>([]);

   React.useEffect(() => {
    if (props.Status == "400") {
	 newErrors.push({
		Status: "400",
		Description: "Bad request",
		Color: "yellow"
	 });
    }
    if (props.Status == "401") {
	 newErrors.push({
		Status: "401",
		Description: "Unauthorized",
		Color: "yellow"
	 });
    }
	if (props.Status == "402") {
	 newErrors.push({
		Status: "402",
		Description: "Payment Required",
		Color: "yellow"
	 });
    }
	if (props.Status == "403") {
	 newErrors.push({
		Status: "403",
		Description: "Forbidden",
		Color: "red"
	 });
    }
	if (props.Status == "404") {
	 newErrors.push({
		Status: "404",
		Description: "Not found",
		Color: "yellow"
	 });
	}
	if (props.Status == "500") {
	 newErrors.push({
		Status: "500",
		Description: "Internal server error",
		Color: "red"
	  });
    } 
    setErrors(newErrors);
   }, []);

   return (
      <div className="http-status-errors">
         {errors.length > 0 ? (
            <ul>
               {errors.map((error: IError, i) => (
                  <li key={i} className={"color-" + error.Color}>
                     <span>{error.Description}</span>
                  </li>
               ))}
            </ul>
         ) : null}
      </div>
   );
});

export default ReactExample;

Save and reload your app and you’ll find that your warning is gone.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.