Home > Enterprise >  How can I change the state of individual elements in a map function?
How can I change the state of individual elements in a map function?

Time:09-01

I want the content to display when the tab is clicked. The issue that I'm having is that once the tab is clicked, all the tabs open... and likewise close when clicked again. I've been trying for hours to figure out how to fix this. I thought I had an answer by having a state that I could set the index to and then write a condition for the tab to open when the index of the state is the same but I noticed that after clicking on another tab, the other one closes. I would appreciate it so much if someone could help me open an individual tab when it's clicked and always stay open until clicked again, meaning, I could have multiple tabs open at once.

Here's a demo: https://codesandbox.io/s/orrigenda-react-question-5oxg47

import React, { useEffect, useState } from 'react'
import axios from 'axios';
import LeaguesStyle from '../components/styles/LeaguesStyle.css';

const Leagues = () => {
  const [teamz, setTeams] = useState([]);
  const [loading, setLoading] = useState(false)
  const [isOpen, setOpen] = useState(false);

  const getTeams = async () => {
    try {
      const res = await axios.get('https://api-football-standings.azharimm.site/leagues');
      setTeams(res.data.data)
      setLoading(true);
      console.log(res.data)
    } catch (err) {
      alert(err.message)
    }
  }
  useEffect(() => {
    getTeams();
  }, []);

  return (
    <div className="leagues">
      {loading &&
        teamz.map(item => (
          <div className='teamCard' key={item.id}>
            <div onClick={() => setOpen(!isOpen)} className="teamDiv">
              <img src={item.logos.dark} className='teamLogo' />
              <h1>{item.name}</h1>
            </div>
            {isOpen && <div className='card-content-active'>{item.abbr}</div>}
          </div>
        ))}
    </div>
  );
}

CodePudding user response:

You need to track the individual truthy values per item.id. This can be easily done by using an object to keep track of all the previous states via the spread operator. Once an initial state is set per tab, then it's just a matter of toggling that individual state between true and false. You delineate between tabs by dynamically assigning the id to the truthy value ([id]: !isOpen[id]). Here is the code in totality:

import React, { useEffect, useState } from "react";
import axios from "axios";
import LeaguesStyle from "./LeaguesStyle.css";

const Leagues = () => {
  const [teamz, setTeams] = useState([]);
  const [loading, setLoading] = useState(false);
  const [isOpen, setOpen] = useState({});

  const getTeams = async () => {
    try {
      const res = await axios.get(
        "https://api-football-standings.azharimm.site/leagues"
      );
      setTeams(res.data.data);
      setLoading(true);
      console.log(res.data);
    } catch (err) {
      alert(err.message);
    }
  };
  useEffect(() => {
    getTeams();
  }, []);

  const handleOpen = (id) => {
    setOpen((prevTruthys) => ({ ...prevTruthys, [id]: !isOpen[id] }));
  };

  console.log(isOpen);

  return (
    <div className="leagues">
      {loading &&
        teamz.map((item) => (
          <div className="teamCard" key={item.id}>
            <div onClick={() => handleOpen(item.id)} className="teamDiv">
              <img src={item.logos.dark} className="teamLogo" alt="logo" />
              <h1>{item.name}</h1>
            </div>
            {isOpen[item.id] === true && (
              <div className="card-content-active">{item.abbr}</div>
            )}
          </div>
        ))}
    </div>
  );
};

export default Leagues;

Here is the code sandbox: https://codesandbox.io/s/orrigenda-react-question-forked-42lbfo?file=/src/App.js

CodePudding user response:

The solution is to store all clicked tabs in a list using the item ID, when the tab is open and you clicked again the ID is removed from the list

here is the code with the solution: I created a function to update the state. setOpenById(tabId) and a function for checking if the tab is open isTabOpen(tabId) the onClick now uses that function onClick={() => setOpenById(item.id)}

import React, { useEffect, useState } from "react";
import axios from "axios";
import LeaguesStyle from "./LeaguesStyle.css";

const Leagues = () => {
  const [teamz, setTeams] = useState([]);
  const [loading, setLoading] = useState(false);
  const [openTab, setOpenTab] = useState([])

  const getTeams = async () => {
    try {
      const res = await axios.get(
        "https://api-football-standings.azharimm.site/leagues"
      );
      setTeams(res.data.data);
      setLoading(true);
      //console.log(res.data);
    } catch (err) {
      alert(err.message);
    }
  };
  useEffect(() => {
    getTeams();
  }, []);

  const setOpenById = (tabId) => {
    if(!isTabOpen(tabId)){
      setOpenTab([...openTab, tabId])
    } else{
      var array = [...openTab] // make a separate copy of the array
      var index = array.indexOf(tabId)
      if (index !== -1) {
        array.splice(index, 1)
        setOpenTab(array)
      }
    }
  } 

  const isTabOpen = (tabId) => {
    return openTab.indexOf(tabId) !== -1
  }

  return (
    <div className="leagues">
      {loading &&
        teamz.map((item) => (
          <div className="teamCard" key={item.id}>
            <div onClick={() => setOpenById(item.id)} className="teamDiv">
              <img src={item.logos.dark} className="teamLogo" alt="logo" />
              <h1>{item.name}</h1>
            </div>
            {isTabOpen(item.id) && <div className="card-content-active">{item.abbr}</div>}
          </div>
        ))}
    </div>
  );
};

export default Leagues;


  • Related