Home > Net >  REACT Duplicated Key Warning
REACT Duplicated Key Warning

Time:06-28

I am currently get this duplicated key warning "Warning: Encountered two children with the same key". However, I am unsure where this duplication of key comes from. I am using the fileData id as my key which should be unique as it is firebase generated id. Therefore, I am not so sure what is happening behind here.

Here are my codes below and the warning I get.

MultimediaDetails.js

import React, { useEffect, useState } from "react";
import * as AiIcons from "react-icons/ai";
import * as FaIcons from "react-icons/fa";
import { database } from "../../../firebase";
import ViewImageFileModal from "../../modals/multimediaModals/view/ViewImageFileModal";

/**
 * It's a component that displays audio, video, and image files
 * @param props - The props object that is passed to the component.
 * @returns The MultimediaDetails component is being returned.
 */
const MultimediaDetails = (props) => {
    /* Destructuring the props object. */
    const { pId } = props;

    /* Setting the state of the component. */
    const [imageData, setImageData] = useState([]);
    const [imageMessage, setImageMessage] = useState(true);
    const userType = JSON.parse(localStorage.getItem("admin") ?? false);

    // Modal Variables
    const [showViewImageModal, setShowViewImageModal] = useState(false);
    const [fileData, setFileData] = useState(Object);

    /**
     * When the user clicks on the audio, video, or image file, the file data is set and the modal is
     * toggled.
     * @param obj
     */

    const viewImageFile = (obj) => {
        setFileData(obj);
        toggleViewImageModal();
    };

    /* The function to toggle modal states */
    const toggleAddImageModal = () => setShowAddImageModal((p) => !p);
    const toggleViewImageModal = () => setShowViewImageModal((p) => !p);

    useEffect(() => {
        /* Query data from database and listening for changes. */
        const imageQuery = database.portfolioRef.doc(pId).collection("images");


        const unsubscribeImage = imageQuery.onSnapshot((snapshot) => {
            if (snapshot.docs.length !== 0) {
                setImageMessage(false);
                setImageData(
                    snapshot.docs.map((doc) => ({ ...doc.data(), id: doc.id }))
                );
            } else {
                setImageMessage(true);
            }
        });

        return () => {
            unsubscribeImage();
        };
    }, [pId]);

    return (
        <div className="multimedia-section">
            <div id="image-section">
                <div id="image-header">
                    <h6>
                        <u>Images</u>
                    </h6>
                    {userType ? (
                        <button className="addbtn" onClick={() => toggleAddImageModal()}>
                            <AiIcons.AiOutlinePlus /> Add Image File
                        </button>
                    ) : (
                        <></>
                    )}
                </div>
                <div id="image-content" className="multimedia-flex">
                    {imageMessage ? (
                        <p>There is not existing images for this portfolio.</p>
                    ) : (
                        <div>
                            {imageData.map((doc) => (
                                <button
                                    key={doc.id}
                                    className="fileBtn"
                                    onClick={() => viewImageFile(doc)}
                                >
                                    <FaIcons.FaImage /> {doc.imageName}
                                </button>
                            ))}
                        </div>
                    )}
                </div>
            </div>
            <ViewImageFileModal
                show={showViewImageModal}
                toggleModal={toggleViewImageModal}
                pId={pId}
                data={fileData}
                key={fileData.id}
            />
        </div>
    );
};

export default MultimediaDetails;

The initialised values for the Modal.

    /* Setting the initial state of the component. */
    const valueState = {
        name: '',
        description: ''
    }
    const { currentUser } = useAuth();
    const [formStateDisabled, setFormStateDisabled] = useState(true);
    const [deleteState, setDeleteState] = useState(false);
    const [message, setMessage] = useState('');
    const [imageUrl, setImageUrl] = useState("");
    const [loadForm, setLoadForm] = useState(false)
    const [view, setView] = useState(false);

    /* Destructuring the props object. */
    const { show, toggleModal } = props;
    const { handleChange, handleSubmit, values, errors, loading } =
        useForm(validateUpdate, valueState, handleUpdate);

    useEffect(() => {
        if (Object.keys(props.data).length !== 0) {
            values.name = props.data.imageName;
            values.description = props.data.imageDesc;
            setLoadForm(true);
        }
    }, [])

The warning I get (Shown Below), each time I click on the modal button to open the button, I noticed the warning actually repeats twice, and when I close it, it repeats another 2 times making it 4. I am not sure what is the cause of this, please help! Thank you!

warning

CodePudding user response:

Recommended solution

The problem is that your doc.id is repeating.

You are setting the imageData at the imageQuery.onSnapshot callback function, when you run the following code:

setImageData(snapshot.docs.map((doc) => ({ ...doc.data(), id: doc.id })));

What you need to make sure is that doc.id is unique in this context (because you're using this value at the key attribute in your buttons).

That's the correct way to fix it.

Alternative solution

Another way to handle it (as a last resort), is using the following code, where you use the index position of the element at the key attribute:

{imageData.map((doc, index) => (
    <button
        key={index}
        className="fileBtn"
        onClick={() => viewImageFile(doc)}
    >
        <FaIcons.FaImage /> {doc.imageName}
    </button>
))}

But this is not recommended according to the React documentation:

We don’t recommend using indexes for keys if the order of items may change. This can negatively impact performance and may cause issues with component state. Check out Robin Pokorny’s article for an in-depth explanation on the negative impacts of using an index as a key. If you choose not to assign an explicit key to list items then React will default to using indexes as keys.

Here is an in-depth explanation about why keys are necessary if you’re interested in learning more.

CodePudding user response:

Instead of doc.id use map item index like bellow. See if it works.

{imageData.map((index, doc) => (
   <button
      key={index}
          className="fileBtn"
            onClick={() => viewImageFile(doc)}>
                <FaIcons.FaImage /> {doc.imageName}
   </button>
))}

CodePudding user response:

This is the culprit:

{imageData.map((doc) => (
    <button
        key={doc.id}
        className="fileBtn"
        onClick={() => viewImageFile(doc)}
    >
        <FaIcons.FaImage /> {doc.imageName}
    </button>
))}

The main issue here is that doc.id is duplicate, probably you have duplicate data in your imageData or ou have a faulty data in your database or something that generate a non-unique id.

To easily fix the issue, what you can do is use index of map.

{imageData.map((doc, index) => (
    <button
        key={index}
        className="fileBtn"
        onClick={() => viewImageFile(doc)}
    >
        <FaIcons.FaImage /> {doc.imageName}
    </button>
))}

index are always unique. but I suggest you should fix and see why you have duplicate data instead of just bypassing it with an index.

  • Related