Search code examples
reactjstags

React gallery App. I want Add tags to an image individually but the tag is being added to all images. How can I solve this?


**> This is my Gallery Component **

import React, {useState} from 'react';
import useFirestore from '../hooks/useFirestore';
import { motion } from 'framer-motion';


const Gallery = ({ setSelectedImg }) => {
  const { docs } = useFirestore('images');

here im setting the state as a Tags array

  const [tags, setTags] = useState([""]);

  
  const addTag = (e) => {
    if (e.key === "Enter") {
      if (e.target.value.length > 0) {
        setTags([...tags, e.target.value]);
        e.target.value = "";
      }
    }
  };

functions for adding and removing Tags

  const removeTag = (removedTag) => {
    const newTags = tags.filter((tag) => tag !== removedTag);
    setTags(newTags);
  };

  return (
    <>
    
      <div className="img-grid">
      {docs && docs.map(doc => (
      
        < motion.div className="img-wrap" key={doc.id} 
          layout
          whileHover={{ opacity: 1 }}s
          onClick={() => setSelectedImg(doc.url)}
        > 

here Im adding the Tag input to each Image...the problem is that when adding a Tag is added to all the pictures. I want to add the tags for the image that I´m selecting.

        <div className="tag-container">
        {tags.map((tag, ) => {
          return (
            <div key={doc.id} className="tag">
              {tag} <span onClick={() => removeTag(tag)}>x</span>
            </div>
          );
        })}

        <input onKeyDown={addTag} />
      </div>
          <motion.img src={doc.url} alt="uploaded pic"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            transition={{ delay: 1 }}
          >
           
          </motion.img>
       
        </motion.div>
     
      ))}
    </div>  
     
    </>
  )
}

export default Gallery;

Solution

  • The tags array that you are using to store values entered by the user are not unique with respect to each image item. Meaning, every image item in your program is using the same instance of the tags array, what you need to do is

    1. Either create an object that stores an array of tags for each image:
      const [tagsObj, setTagsObj] = {}, then while adding a new tag for say image_1, you can simply do setTagsObj(prevObj => {...prevObj, image_1: [...prevObj?.image_1, newTagValue]},
    2. Or create an Image Component which would then handle tags for a single image:

    Gallery Component:

    {
        imageList.map(imageEl => 
              <ImageItem key={imageEl} image={imageEl} />
            )
    
    }
    

    ImageItem Component:

    import {useState} from 'react';
    
    export default function ImageItem({image}) {
    const [tags, setTags] = useState([]);
    
    const addTag = (e) => {
        if (e.key === "Enter") {
          const newVal = e.target.value;
          if (newVal.length > 0) {
            setTags(prevTags => [...prevTags, newVal]);
            e.target.value = '';
          }
        }
      };
    
    const removeTag = (removedTag) => {
        setTags(prevTags => prevTags.filter((tag) => tag !== removedTag));
      }
    
      return (    
        <div style={{margin: '12px', padding: '12px', width: '100px', height:'100px', display:'flex', flexDirection: 'column', alignItems:'center'}}>
          <span>{image}</span>
          {tags.map((tag, index) => {
              return (
                <div key={tag+index}>
                  {tag} <span onClick={() => removeTag(tag)}>x</span>
                </div>
              );
            })}
            <input onKeyDown={addTag} />
        </div>
      );
    }
    

    Refer this sandbox for ease, if available Gallery unique image tags sandbox

    I suggest using the second method, as it is easy to understand and debug later on.
    I hope this helps, please accept the answer if it does!