0

I'm building a React component that shows data on an Order Summary Screen to conclude the order process for my App.

I am receiving the message:

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

Here is the complete error for reference:

Check the render method of `SummaryOrder`., , 
    in RCTView (at SummaryOrder.js:24)
    in SummaryOrder (at PreOrderScreen.js:111)
    in ScrollView (at PreOrderScreen.js:105)
    in RCTView (at PreOrderScreen.js:99)

Location on errors are also marked with "=>".

SummaryOrder.js:

import React from "react";
import { View, StyleSheet } from "react-native";
//Number
import NumberFormat from "../../../components/UI/NumberFormat";
//PreOrderItem
import PreOrderItem from "./PreOrderItem";
//Text
import CustomText from "../../../components/UI/CustomText";
import Colors from "../../../utils/Colors";
//PropTypes check
import PropTypes from "prop-types";

export class SummaryOrder extends React.PureComponent {
  render() {
    const { cartItems, total } = this.props;
    return (

    in RCTView (at SummaryOrder.js:24) =>  ***<View style={styles.container}>***


        <CustomText style={{ ...styles.title, marginVertical: 5 }}>
        Order Summary
        </CustomText>
        <View style={{ backgroundColor: "#fff", paddingHorizontal: 10 }}>
          {cartItems.map((item) => {
            return (


     in SummaryOrder (at PreOrderScreen.js:111)  =>  ****<View key={item.item.createdAt}>****


                <PreOrderItem item={item} />
              </View>
            );
          })}
        </View>
        <View style={styles.total}>
          <CustomText
            style={{
              fontSize: 15,
              color: Colors.text,
              fontWeight: "500",
            }}
          >
            Total
          </CustomText>
          <NumberFormat price={total.toString()} />
        </View>
      </View>
    );
  }
}

SummaryOrder.propTypes = {
  cartItems: PropTypes.array.isRequired,
  total: PropTypes.number.isRequired,
};

PreOrderScreen.js:

import React, { useState, useEffect, useRef } from "react";
import { useIsFocused } from "@react-navigation/native";
import { View, StyleSheet, ScrollView } from "react-native";
//Address
import Address from "./components/Address";
//Redux
import { useSelector } from "react-redux";
//Steps
import Colors from "../../utils/Colors";
import { Header, SummaryOrder, TotalButton, UserForm } from "./components";
import Loader from "../../components/Loaders/Loader";

export const PreOrderScreen = (props) => {
  const unmounted = useRef(false);
  const isFocused = useIsFocused();
  const [loading, setLoading] = useState(true);
  const carts = useSelector((state) => state.cart.cartItems);
  const { cartItems, total, cartId } = props.route.params;
  const [error, setError] = useState("");
  const [name, setName] = useState("");
  const [phone, setPhone] = useState("");
  const [address, setAddress] = useState("");
  const [province, setProvince] = useState("");
  const [town, setTown] = useState("");
  useEffect(() => {
    return () => {
      unmounted.current = true;
    };
  }, []);
  useEffect(() => {
    if (isFocused) {
      setLoading(true);
      const interval = setInterval(() => {
        setLoading(false);
      }, 1000);
      return () => clearInterval(interval);
    }
    return;
  }, [isFocused]);
  const getInfo = (province, town) => {
    setProvince(province);
    setTown(town);
  };
  const getReceiver = (name, phone, address) => {
    setName(name);
    setPhone(phone);
    setAddress(address);
  };
  const checkValidation = (error) => {
    setError(error);
  };
  let orderItems = [];
  cartItems.map((item) => {
    orderItems.push({ item: item.item._id, quantity: item.quantity });
  });

  const fullAddress = `${address}, ${town} ,${province}`;
  const toPayment = async () => {
    try {
      if (error == undefined && province.length !== 0 && town.length !== 0) {
        props.navigation.navigate("Payment", {
          screen: "PaymentScreen",
          params: {
            fullAddress,
            orderItems,
            name,
            phone,
            total,
            cartId,
            carts,
          },
        });
      } else {
        alert("Please enter your full information.");
      }
    } catch (err) {
      throw err;
    }
    props.navigation.navigate("Payment", {
      screen: "PaymentScreen",
      params: {
        fullAddress,
        orderItems,
        name,
        phone,
        total,
        cartId,
        carts,
      },
    });
  };
  useEffect(() => {
    if (carts.items.length === 0) {
      props.navigation.goBack();
    }
  }, [carts.items]);
  return (

  in RCTView (at PreOrderScreen.js:99) =>  ***<View style={styles.container}>***

      <Header navigation={props.navigation} />
      {loading ? (
        <Loader />
      ) : (
        <>
          <ScrollView>
            <UserForm
              getReceiver={getReceiver}
              checkValidation={checkValidation}
            />
            <Address getInfo={getInfo} />

      in ScrollView (at PreOrderScreen.js:105) =>
    ***<SummaryOrder cartItems={cartItems} total={total} />***


          </ScrollView>
          <TotalButton toPayment={toPayment} />
        </>
      )}
    </View>
  );
};

Thanks for your help in advance!

  • Can you give us what is in `cartItems`? The `createdAt` values are not unique for them. The quick fix is to use index, but if you plan to do any removing or sorting, you should rather use some sort of unique identifier for the items (email, user ID, etc). – Stardust Sep 16 '21 at 21:37

2 Answers2

0

whenever you use map in react, you should define a unique key for the repeating tag or component, this is for react to keep track of changes in the array you are mapping on, and if any changes occur in array, react will know where to re-render the related tag or component and leave others alone :)

if you don't have a plan to re-order the array, you can simply use index as key but if you want to re-order the array elements, it's better if first you define a unique id for them(if it's not provided by server) and then do the map the array

0

The createdAt values are not unique for them. The quick fix is to use index instead, but if you plan to do any removing or sorting, you should rather use some sort of unique identifier for the items (phone, email, user ID, etc). I only skimmed your code, but you could use phone instead.

The keys are important because it hints to React when it needs to update the list. The docs are helpful https://reactjs.org/docs/lists-and-keys.html#keys for more on this.

Anyway, the quick fix is shown here. Read https://stackoverflow.com/a/43642421 as to why you shouldn't do this. I'll leave a better implementation up to you.

...
{ cartItems.map((item, index) => {
    return (
        <View key={index}>
            <PreOrderItem item={item} />
        </View> );
    }) 
}
...
Stardust
  • 999
  • 3
  • 12
  • 24