skip to Main Content

I have a parent component containing an array and I am mapping to create multiple cards something like this->

const Users = [
    {
      id: 1,
name: "abc",
age: 12,
    },
    {
      id: 2,
name: "def",
age: 22,
    },
    {
      id: 3,
name: "abf",
age: 32,
    },
  ];

{Users.map((h, index) => {
        return (
          <React.Fragment key={index}>
            <User data={h} />
            <br></br>
          </React.Fragment>
        );
      })}

User.js

import { useState } from "react";
import "./style.css";

function User(user) {


  const [copyTxt, setCopyTxt] = useState("Copy");
  const [copyClass, setCopyClass] = useState("button_copy");

  const handleCopy = () => {
    setCopyTxt("Copied!");
    setCopyClass("button_copied");
  };

  return (
    <div className="user_div">
      
        <div className="p-2 flex-fill button_div">
          <button type="submit" onClick={handleCopy} className={copyClass}>
            {copyTxt}
          </button>
        </div>
      </div>

  );
}

export default User;

current result ->
if all 3 buttons are clicked one by one, for all 3 buttons text changes to Copied!

expected result ->
if all 3 buttons are clicked one by one, the last button clicked should be the only one that shows Copied!, but Other buttons should show Copy.

How do I achieve that?

2

Answers


  1. The solution is life the state up.

    function Parent() {
    const Users = [
        {
        id: 1,
        name: "abc",
        age: 12,
        },
        {
        id: 2,
        name: "def",
        age: 22,
        },
        {
        id: 3,
        name: "abf",
        age: 32,
        },
    ];
    
    const [active,setActive] = useState(null);
    
    return (
        <>
            {Users.map((d)=>(
                <User data={d} active={active} setActive={setActive}/>
            ))}
        </>
    )
    }
    
    function User({data,active,setActive}){
        function handleCopy(){
            setActive(data.id);
        }
    
        return (
            <div>
                <span>{user.name}</span>
                <button onClick={handleCopy}>
                    {active===data.id ? "Copied" : "Copy"}
                </button>
            </div>
        )
    }
    

    Logic is simple store the active id at one place.

    Login or Signup to reply.
  2. You can do this

    First you create a state in the parent component from the array and add the property isCopied to each one and set it default to false:

    const [usersState, setUsersState] = useState(
      Users.map((user) => {
        return {
          ...user,
          isCopied: false,
        };
      })
    )
    

    Also there, you create the function that takes the id of the user from the child component and set its property isCopied to true but false for all others:

    const handleCopy = (id) => {
      setUsersState((prev) =>
        prev.map((h) => {
          if (h.id === id) {
            return { ...h, isCopied: true };
          } else {
            return { ...h, isCopied: false };
          }
        })
      );
    }
    

    Now to each child you pass as props, data ( the user + isCopied property) and also the handleCopy function:

    return (
      <>
        {usersState.map((h) => {
          return (
            <React.Fragment key={h.id}>
              <User data={h} handleCopy={handleCopy} />
              <br></br>
            </React.Fragment>
          );
        })}
      </>
    )
    

    Finally, in the child component, you want to update its state (copyTxt and copyClass) each time property isCopied of the passed user (data) is updated:

    function User({ data, handleCopy }) {
      useLayoutEffect(() => {
        if (data.isCopied == true) {
          setCopyTxt("Copied!");
          setCopyClass("button_copied");
        } else {
          setCopyTxt("Copy!");
          setCopyClass("button_copy");
        }
      }, [data.isCopied]); 
      const [copyTxt, setCopyTxt] = useState();
      const [copyClass, setCopyClass] = useState();
    
      return (
        <div>
          <div>
            <button
              type="submit"
              onClick={() => {
                handleCopy(data.id);
              }}
              className={copyClass}
            >
              {copyTxt}
            </button>
          </div>
        </div>
      );
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search