first time asking questions haha, I'm trying to remove a specific JSX element from my array of elements but I am not sure how to identify said element is the one being clicked to get removed so I wanted to know if anyone encountered this issue before and how you would go about solving it.
This is the code for the input Container
const InputComponent = (props) => {
return (
<div className="input-container" onClick={e =>console.log(e)}>
<input className="input-name" type="text" />
<input className="input-value" type="text" />
<button className="remove-button" onClick={props.remove}><img src={minusIcon} alt="remove-icon" /></button>
</div>
);
}
export default InputComponent;
and this is the code for the parent component to manage the removal of said element
const Main = () => {
const [newInput, setInput] = useState([]);
const [currentInput, setCurrentInput] = useState([<InputComponent key={0}/>]);
const [currentIndex, setIndex] = useState(0)
const [currentPlanName, setCurrentPlanName] = useState('Current-Plan');
const [newPlanName, setNewPlanName] = useState('New-Plan');
// const [currentInputValue, setCurrentValue] = useState('')
// const [newInputValue, setNewValue] = useState('')
// Sets Keys for each New Element in Array
const [newKey, setNewKey] = useState(0);
const [currentKey, setCurrentKey] = useState(0)
// Handle Removal of specific array by using key
const handleCurrentRemoval = () => {
let newArray = currentInput.filter(element => element.key !== )
console.log(newArray)
setCurrentInput(newArray)
}
// Initialize Keys for each Array
const currentArrayElement = {
element: <InputComponent key={currentKey} remove={handleCurrentRemoval} />,
index: currentKey
};
const newArrayElement = <InputComponent key={newKey+1}/>
// Adds new Element to array
const handleCurrentClick = () => {
setCurrentInput(prevValues => [...prevValues, currentArrayElement.element])
setCurrentKey(currentKey+1);
console.log(currentArrayElement)
console.log(currentInput)
};
const handleNewClick = () => {
setInput(prevValues => [...prevValues, newArrayElement])
};
// const handleRemoveClick = (value) => {
// currentInput.filter(current => value !=)
// }
return (
<div className="main-container">
<div className="quote-container">
<div className="current-plan">
<h2>{currentPlanName}</h2>
{
currentInput.map((inputs) => {
return inputs
})
}
<div className="button-container">
<button className="add-input" onClick={handleCurrentClick}><img src={addIcon} alt="add"/></button>
</div>
</div>
<div className="new-plan">
<h2>{newPlanName}</h2>
{
newInput.map((inputs) => {
return inputs
})
}
<div className="button-container">
<button className="add-input" onClick={handleNewClick}><img src={addIcon} alt="add"/></button>
</div>
</div>
</div>
</div>
);
}
export default Main;
I do apologize in advance if I posted this incorrectly.
Thank you for your assistance
I think you've made this entirely more complex than it needs to be with all the index value storage in state. It is also anti-pattern in React to store JSX in state, you should store data in state and render the data to JSX.
I suggest storing generated input ids in the arrays instead and mapping these to JSX.
Example:
// id generator
let id = 0;
const getId = () => id++;
export default function App() {
const [newInput, setInput] = useState([]);
const [currentInput, setCurrentInput] = useState([]);
const [currentPlanName, setCurrentPlanName] = useState("Current-Plan");
const [newPlanName, setNewPlanName] = useState("New-Plan");
// Handle Removal of specific element by using id
const handleCurrentRemoval = (removeId) => {
setCurrentInput((ids) => ids.filter((id) => id !== removeId));
};
// Adds new id to array
const handleCurrentClick = () => {
setCurrentInput((ids) => ids.concat(getId()));
};
const handleNewClick = () => {
setInput((ids) => ids.concat(getId()));
};
const handleRemoveClick = (removeId) => {
setInput((ids) => ids.filter((id) => id !== removeId));
};
return (
<div className="main-container">
<div className="quote-container">
<div className="current-plan">
<h2>{currentPlanName}</h2>
{currentInput.map((id) => {
return (
<InputComponent
key={id}
remove={() => handleCurrentRemoval(id)}
/>
);
})}
<div className="button-container">
<button className="add-input" onClick={handleCurrentClick}>
<img src={addIcon} alt="add" />
</button>
</div>
</div>
<div className="new-plan">
<h2>{newPlanName}</h2>
{newInput.map((id) => {
return (
<InputComponent key={id} remove={() => handleRemoveClick(id)} />
);
})}
<div className="button-container">
<button className="add-input" onClick={handleNewClick}>
<img src={addIcon} alt="add" />
</button>
</div>
</div>
</div>
</div>
);
}
Related
I am new to react. I'm trying to update the parent state from the child but i have an error on another component at the the same level of the child one.
that's my code.
RedirectPage.js (parent)
const RedirectPage = (props) => {
const [status, setStatus] = useState("Loading");
const [weather, setWeather] = useState(null);
const [location, setLocation] = useState(null)
const [showLoader, setShowLoader] = useState(true)
const [userId, setUserId] = useState(false)
const [isPlaylistCreated, setIsPlaylistCreated] = useState(false)
const headers = getParamValues(props.location.hash)
const getWeather = () =>{
//fetch data..
//...
//...
.then(response => {
var res = response.json();
return res;
})
.then(result => {
setWeather(result)
setShowLoader(false)
setStatus(null)
setLocation(result.name)
});
})
}
const changeStateFromChild = (value) => {
setIsPlaylistCreated(value)
}
useEffect(() => {
getWeather()
},[]);
return (
<div className="containerRedirectPage">
{showLoader ? (
<div className="wrapperLogo">
<img src={loader}className="" alt="logo" />
</div>)
: (
<div className="wrapperColonne">
<div className="firstRow">
<WeatherCard weatherConditions={weather}/>
</div>
{isPlaylistCreated ? (
<div className="secondRow">
<PlaylistCard />
</div>
) : (
<PlaylistButton userId={userId} headers={headers} weatherInfo={weather} playlistCreated={changeStateFromChild} />
)}
</div>
)}
</div>
)
};
export default RedirectPage;
PlaylistButton.js:
export default function PlaylistButton({userId, headers, weatherInfo, playlistCreated}) {
const buttonClicked = async () => {
// ...some code...
playlistCreated(true)
}
return (
<div className="button-container-1">
<span className="mas">CREA PLAYLIST</span>
<button onClick={buttonClicked} id='work' type="button" name="Hover">CREA PLAYLIST</button>
</div>
)
}
and that's the other component i'm getting the error when i click on button.
WeatherCard.js:
const WeatherCard = ({weatherConditions}) => {
const [weather, setWeather] = useState(null);
const [icon, setIcon] = useState(null);
const getTheIcon = () => {
// code to get the right icon
}
setIcon(x)
}
useEffect(() => {
getTheIcon()
},[]);
return (
<div className="weatherCard">
<div className="headerCard">
<h2>{weatherConditions.name}</h2>
<h3>{Math.floor(weatherConditions.main.temp)}°C</h3>
</div>
<div className="bodyCard">
<h5>{weatherConditions.weather[0].description}</h5>
<img className="weatherIcon" src={icon} alt="aa" />
</div>
</div>
)
};
export default WeatherCard;
the first time i load the redirect page WeatherCard component is right. When i click the button i get this error:
error
Can someone explain me why ?
What is the effect of the setting playlistCreated(true) ?
Does it affects the weatherCondition object ?
If weatherCondition could be undefined at some point you need to check it before using its properties (name, main.temp, and weather)
Update:
The error clearly state that it cannot read name from weather because it's undefined. You have to check it before using the weather object properties.
if (!weatherConditions) {
return <div>Loading...</div> // or something appropriate.
}
return (
<div className="weatherCard">
<div className="headerCard">
<h2>{weatherConditions.name}</h2>
{weatherConditions.main && <h3>{Math.floor(weatherConditions.main.temp)}°C</h3>}
</div>
<div className="bodyCard">
{weatherConditions.weather &&
{weatherConditions.weather.length > 0 &&
<h5>{weatherConditions.weather[0].description}</h5>}
....
)
I am creating a page to update product details on an e-commerce site I am building using NextJS, and I have the image upload section nested inside an accordion on the individual item page. Once images have been uploaded, I would like to clear the upload form and close the accordion. It is closing the accordion I am having trouble with.
ImageUploadAccordion.js:
import React, {useRef} from 'react';
import {Accordion} from 'react-bootstrap'
import ImageUpload from './ImageUpload'
export default function ImageUploadAccordion({ item }) {
const accordionRef = useRef(null);
const toggleAccordion = () => {
accordionRef.current.click();
}
return (
<Accordion ref={accordionRef} defaultActiveKey="0">
<Accordion.Item eventKey="1">
<Accordion.Header>
<span className="btn btn-outline-success">Upload Images</span>
</Accordion.Header>
<Accordion.Body>
<ImageUpload
toggle={toggleAccordion}
item={item}
/>
</Accordion.Body>
</Accordion.Item>
</Accordion>
)
}
ImageUpload.js:
import React, {useState} from 'react';
import { useRouter } from 'next/router'
export default function ImageUpload({ item, toggle }) {
const router = useRouter()
const [images, setImages] = useState([])
const [imageURLS, setImageURLS] = useState([])
const [tags, setTags] = useState([])
const [theInputKey, setTheInputKey] = useState('')
const uploadImageToClient = (event) => {
if (event.target.files && event.target.files[0]) {
setImages((imageList) => [...imageList, {"index": images.length, "data": event.target.files[0]}]);
setImageURLS((urlList) => [
...urlList,
URL.createObjectURL(event.target.files[0])
]);
}
let randomString = Math.random().toString(36);
setTheInputKey(randomString)
};
const uploadTagToClient = (e) => {
if (event.target.value) {
const name = e.target.getAttribute("name")
// const i = event.target.value;
// document.getElementById("image-upload")
setTags((tagList) => [...tagList, {"name": name, "tag": e.target.value}]);
}
};
const removeImage = (name) => {
// debug
alert(`Trying to remove image index ${name}`)
let newImages = []
let newTags = []
setImages(images.filter(image => image.data.name !== name));
setTags(tags.filter(tag => tag.name !== name));
}
const uploadToServer = async (e) => {
const body = new FormData()
images.map((file, index) => {
body.append(`file${index}`, file.data);
});
// Use the filenames as keys then we can retrieve server side once we have the images
tags.map((tag, index) => {
body.append(tag.name, tag.tag)
})
const response = await fetch("/api/file", {
method: "POST",
"Content-Type": "multipart/form-data",
body
})
var message = await response.json();
alert(message['message'])
setImages([])
setTags([])
toggle()
};
const openImageUploadDialogue = () =>{
document.getElementById("image-upload").click()
}
return (
<div className="container">
<input style={{display:'none'}} accept="image/*" id="image-upload" type="file" key={theInputKey || '' } className="btn btn-outline-success-inverse" onChange={uploadImageToClient} />
<button className="btn btn-outline-success-inverse" onClick={openImageUploadDialogue} >
Add Image
</button>
<hr className = "text-pink"/>
<div className="row">
<div className="col d-flex flex-wrap">
{images.map((file, index) => {
return (
<div className="div p-1" key={file.data.name}>
<p className="text-pink">{file.data.name}</p>
<p>Tag</p>
<input type="text" name={file.data.name} id={`${file.data.name}`} onChange={uploadTagToClient} />
<img src={imageURLS[index]} height="200" width="150" />
<div className="btn btn-outline-success-inverse" onClick={ () =>removeImage(file.data.name)}>Remove Image</div>
</div>
);
})}
</div>
<button
className="btn btn-outline-success-inverse"
type="submit"
onClick={uploadToServer}
>
Upload Images
</button>
</div>
</div>
);
}
I tried by creating a reference to the accordion using useRef, and a function which uses this reference to activate the click event, which I passed to the ImageUpload component, according to another answer to a similar question, but it doesn't seem to work and I'm unsure as to why.
Any help always appreciated :-)
I believe you have the wrong target as the ref, update it to target the button that is automatically generated to wrap the header content.
<h2 class="accordion-header"><button type="button" aria-expanded="true" class="accordion-button"><span class="btn btn-outline-success">Upload Images</span></button></h2>
Rudimentary example:
export default function ImageUploadAccordion({ item }) {
const accordionRef = useRef(null);
const toggleAccordion = () => {
accordionRef.current.querySelector('button').click();
}
return (
<Accordion defaultActiveKey="0">
<Accordion.Item eventKey="1">
<Accordion.Header ref={accordionRef}>
<span className="btn btn-outline-success">Upload Images</span>
</Accordion.Header>
<Accordion.Body>
<ImageUpload
toggle={toggleAccordion}
item={item}
/>
</Accordion.Body>
</Accordion.Item>
</Accordion>
)
}
I have this component that loads some data with buttons. I have a function that gets button via ID and updates state/variable to that buttons value, then loads data based on that. However since all the button IDs are the same, it only works for the first button...
I'm not sure how to set unique IDs for each button since they're generated through a map() function, and even if i did know, i'm not sure how my function would then target each button without writing a function for each one...
Edit: I've set unique IDs for buttons now, still need to solve the other problem.
return member.map((player, index) => (
<>
{" "}
<React.Fragment key={index}>
<br />{" "}
<div>
<br />
{player.map((stats) => (
<React.Fragment key={stats.player}>
<div class="flex-child">
<button id={'btn' + index} value={stats.player}>
{stats.player}
</button>
<p>{stats.role}</p>
<p>{stats.win_rate}</p>
<p>{stats.matches}</p>
<p>{stats.total_battles} Total Battles</p>
<p>{stats.score} / Points Earned</p>
</div>
</React.Fragment>
))}
</div>
<br />
</React.Fragment>
</>
));
};
export default SquadMembers;
here is index.js
import Head from "next/head";
import React, { useState } from "react";
import Teams from "../components/Teams";
import styles from "../../styles/Home.module.css";
import SquadMembers from "../components/SquadMembers";
import SquadData from "../components/SquadData";
export default function Home() {
const [teams, setTeams] = useState([]);
const [squads, setSquads] = useState([]);
const [player, setPlayer] = useState("Player Name");
const [squad, setSquad] = useState("default");
const [loading, setLoading] = useState(false);
function clicky() {
if (!document.getElementById("btn")) {
} else {
setPlayer(document.getElementById("btn").value);
loadPeople();
}
}
if (typeof window === "object") {
if (!document.getElementById("btn")) {
} else {
document.getElementById("btn").onclick = function () {
clicky();
};
}
}
function handleChange(e) {
setSquad(e.target.value);
}
const loadPeople = async () => {
setLoading(true);
const req = await fetch(`/api/player/${player}`);
const json = await req.json();
setTeams(json);
setLoading(false);
};
const loadSquad = async () => {
setLoading(true);
const req = await fetch(`/api/squad/${squad}`);
const json = await req.json();
setSquads(json);
setLoading(false);
setTeams([]);
};
return (
<div className={styles.main}>
<main className={styles.main}>
<h1>Silph Team Finder</h1>
<br />
<div>
<select value={squad} onChange={handleChange}>
<option value="default" selected disabled hidden>
Choose here
</option>
<option value="a342">Lots of options, cut them to save space</option>
</select>
<button onClick={() => loadSquad()}>Load</button>
<input
value={player}
id="player"
onChange={(e) => setPlayer(e.target.value)}
/>
<button onClick={() => loadPeople()} id="pbtn">
Load
</button>
{loading && <div className={styles.load}>LOADING</div>}
</div>
<div className={styles.teams}>
<SquadData squadz={squads} />
<Teams teams={teams} />
<div class="player-container">
<SquadMembers squadz={squads} />
</div>
</div>
</main>
</div>
);
}
Would be much easier to have something like:
<button value={stats.player} onClick={() => handleClick(stats.player)}>
{stats.player}
</button>
...
const handleClick = (player) => {
setPlayer(player);
loadPeople();
}
In this way you don't need id for button. Not only but you will avoid warning on same id for multiple elements.
Then I would like to talk about loadPeople(). If I understood correctly in this function you are using player that would be setted by setPlayer.
This is not a good approach. setPlayer is async and you could take an old value of player. Much better pass the last player value directly to loadPeople function. Something like:
const handleClick = (player) => {
setPlayer(player);
loadPeople(player);
}
const loadPeople = async (newPlayer) => {
setLoading(true);
const req = await fetch(`/api/player/${newPlayer}`);
const json = await req.json();
setTeams(json);
setLoading(false);
};
I am trying to rewrite a small app from vanilla js to react, and in one element I encountered a problem with passing on values in the inputs. What this element does, is after selecting a number it generates that many inputs to fill, and after filling send its id and value further (value can also be empty)
In Vanilla Js I did it with id and querySelector, but in React I have a trouble to change it correct
React code:
import React, { useState, useEffect } from "react";
import "./style.css";
import Values from "./Values";
export default function App() {
const [numberValue, setNumberValue] = useState("");
const [inputValues, setInputValues] = useState([]);
const [sendValues, setSendValues] = useState(false);
const [inputs, setInputs] = useState([]);
let numbers = [4, 6, 8];
//reset teamsName on change teamsValue
useEffect(() => {
for (let i = 1; i <= numberValue; i++) {
setInputValues(prev => [
...prev,
{
id: i,
value: ""
}
]);
}
}, [numberValue]);
const showButtons = numbers.map((number, i) => (
<button
className={`${numberValue === number ? "button active" : "button"}`}
onClick={() => {
setNumberValue(number);
setInputValues([]);
setInputs([]);
showInputs();
}}
>
{number}
</button>
));
//let inputs = [];
const showInputs = () => {
for (let i = 1; i <= numberValue; i++) {
setInputs(prev => [
...prev,
<input
type="text"
className="input"
placeholder={`Input ${i}`}
//value={inputValues.find(input => input.id === i && input.value)}
onChange={e =>
inputValues.filter(
input =>
input.id === i &&
setInputValues([
...inputValues,
{ id: i, value: e.target.value }
])
)
}
/>
]);
}
};
return (
<>
<div className="button-group">{showButtons}</div>
{numberValue && (
<>
<h3 className="title">Your inputs</h3>
<div className="input-group">{inputs}</div>
</>
)}
<button onClick={() => setSendValues(true)}>SEND</button>
{sendValues && <Values inputValues={inputValues} />}
</>
);
}
JS:
const buttonGroup = document.querySelector(".button-group");
const inputGroup = document.querySelector(".input-group");
const inputValues = document.querySelector(".input-values");
let n;
const showInputs = number => {
n = number;
inputGroup.innerHTML = ''
for (let i = 1; i <= number; i++) {
inputGroup.innerHTML += `
<input type="text" name="name" id="input-${i}" class="input" placeholder="team name"> <br>
`;
}
};
let values = []
const showValues = () => {
//clear
inputValues.innerHTML = '';
values = [];
//show new
for (let i = 1; i <= n; i++) {
const input_val = document.querySelector(`#input-${i}`).value;
values.push({
id: i,
value: input_val
});
}
for(let i = 0; i<=n; i++){
inputValues.innerHTML += `
<p>id: ${values[i].id} value:${values[i].value}
</p>
`
}
};
Links to code:
React -> https://stackblitz.com/edit/react-uw9dzc?file=src/App.js
JS -> https://codepen.io/Arex/pen/qBqLVBq?editors=1111
I took the liberty to simplify your code a bit. Basically I assigned value as it's own variable const value = e.target.value; as it is a synthetic event and tends to get lost if you pass it further down, so this preserves the value. Also, I changed inputValues to an object to make it easier to update:
// App.js
export default function App() {
const [numberValue, setNumberValue] = useState("");
const [inputValues, setInputValues] = useState({});
const [sendValues, setSendValues] = useState(false);
let numbers = [4, 6, 8];
const showButtons = numbers.map((number, i) => (
<button
className={`${numberValue === number ? "button active" : "button"}`}
onClick={async () => {
await setNumberValue(number);
await setInputValues({});
}}
>
{number}
</button>
));
return (
<>
<div className="button-group">{showButtons}</div>
{numberValue && (
<>
<h3 className="title">Your inputs</h3>
<div className="input-group">
{[...new Array(numberValue)].map((_value, id) => (
<input
type="text"
className="input"
placeholder={`Input ${id}`}
onChange={e => {
const value = e.target.value;
setInputValues(prev => {
prev[id] = value;
return prev;
});
}}
/>
))}
</div>
</>
)}
<button onClick={() => setSendValues(true)}>SEND</button>
{sendValues && <Values inputValues={inputValues} />}
</>
);
}
// Values.js
const Values = ({ inputValues }) => {
const showValues = Object.keys(inputValues).map(input => (
<div>
{input} : {inputValues[input]}
</div>
));
return <div>{showValues}</div>;
};
export default Values;
There are multiple issues in the shared code as follows:
It is not recommended to store components in state, in the shared code you are storing <input/> component in state. For more details check this
Unnecessary states are being used, always try to keep a minimum number of states as more number of states as more states are needed to be managed, making things unnecesarily complicated.
using previous state syntax to generate new state where it is not needed.
I am adding a working code with minimum changes for you reference.
App.js
import React, { useState, useEffect } from "react";
import "./style.css";
import Values from "./Values";
export default function App() {
const [numberValue, setNumberValue] = useState('');
const [inputValues, setInputValues] = useState([]);
const [sendValues, setSendValues] = useState(false);
let numbers = [4, 6, 8];
//reset teamsName on change teamsValue
useEffect(() => {
setInputValues(
Array(numberValue).fill("")
);
setSendValues(false)
}, [numberValue]);
const showButtons = numbers.map((number, i) => (
<button
className={`${numberValue === number ? "button active" : "button"}`}
onClick={() => {
setNumberValue(number);
}}
>
{number}
</button>
));
return (
<>
<div className="button-group">{showButtons}</div>
{numberValue && (
<>
<h3 className="title">Your inputs</h3>
<div className="input-group">
{inputValues.map((val, i) => (
<input
key={`input${i}`}
type="text"
className="input"
placeholder={`Input ${i+1}`}
value={val}
onChange={(e) => {let newValues = inputValues.slice(); newValues[i]=e.target.value; setInputValues(newValues)}
}
/>
))}
</div>
</>
)}
<button onClick={() => setSendValues(true)}>SEND</button>
{sendValues && <Values inputValues={inputValues} />}
</>
);
}
Values.js
import React from "react";
const Values = ({ inputValues }) => {
const showValues = inputValues.map((input, i) => (
<div key={'output'+i}>
{i+1} : {input}
</div>
));
return <div>{showValues}</div>;
};
export default Values;
I am also sharing a updated stackblitz code reference you shared for better understanding Updated snippet with fixes for reference.
I have two React components: ul list and each li item, which is a flash-card. In each item there are two strings: searching word and its translation (api.source, api.target) and also checkbox. After clicking on the checkbox and later on the button (CardList.js) I would like to get my data and send it to my backend. Can anybody please tell me how to get these two strings and pass them into addFlashCards function? Right now I have hardcoded it as dataSet
const CardsList = ({ fetchedApi }) => {
const addFlashCards = () => {
const dataSet = [
{
apiSource: "sampleDataCheckbox",
apiTarget: "sampleDataCheckbox2",
},
{
apiSource: "sampleDataCheckbox",
apiTarget: "sampleDataCheckbox",
},
];
const url = "/api/saveToDB";
axios
.post(url, { data: dataSet, user: "SOME USER" })
.then((res) => {
const fetchedData = res.data;
console.log(fetchedData);
})
.catch((err) => console.error(err));
};
return (
<div className="cards-list">
<button onClick={addFlashCards}>Add</button>
<ul className="cards-list__list">
{fetchedApi.map((api) => {
return <CardItem api={api} />;
})}
</ul>
</div>
);
};
export default CardsList;
const CardItem = ({ api }) => {
const [isChosenCard, setIsChosenCard] = useState(false);
const clickedCheckbox = () => {
setIsChosenCard(!isChosenCard);
};
return (
<>
<li className="card-item">
<input
className="fiszkiBox"
type="checkbox"
onClick={clickedCheckbox}
/>
<div className="flip-card">
<div className="flip-card-inner">
<div className="flip-card-front">
<p id="api-source">{api.source}</p>
</div>
<div className="flip-card-back">
<p className="api-target">{api.target}</p>
</div>
</div>
</div>
</li>
</>
);
};
export default CardItem;
Many thanks!
So as summary you have a list which you render and you want to select items which would be sent to backend. In this case you can add a empty array to your CardList by using useState hook which will contain the selected items.
const CardsList = ({ fetchedApi }) => {
const [selectedItems, setSelectedItems] = useState([]);
const addFlashCards = () => {
// Here you can do some kind of check with mapping the selectedItems Array, as you need
const url = "/api/saveToDB";
axios
.post(url, { data: selectedItems, user: "SOME USER" })
.then((res) => {
const fetchedData = res.data;
console.log(fetchedData);
})
.catch((err) => console.error(err));
};
function selectItem(item){
// So below, we do it to make component render by creating new array instead of mutating the current array. Actually try to play with code, you can even remove these and mutate current and set.
let selectedItemsArray = [...selectedItems];
// Here below, we check if selectedItem already exists in selectedItemsArray, if it exists we would like to remove it right? if it does not exist, we just add it to array by pushing it.
let itemCheck = selectedItemsArray.find((arrayItem) => arrayItem.source === item.source);
if (itemCheck) {
// Here if selected item was in array, we just remove it by filtering the array
selectedItemsArray = selectedItemsArray.filter((arrayItem) => arrayItem.source !== item.source)
}
else{
// Here selected item was not in our array so we just push it
selectedItemsArray.push(item)
}
setSelectedItems(selectedItemsArray);
}
return (
<div className="cards-list">
<button onClick={addFlashCards}>Add</button>
<ul className="cards-list__list">
{fetchedApi.map((api, index) => {
return <CardItem api={api} onItemSelected={item=> selectItem(item)} />;
})}
</ul>
</div>
);
};
const CardItem = ({ api, onItemSelected }) => {
return (
<>
<li className="card-item">
<input
className="fiszkiBox"
type="checkbox"
onClick={() => onItemSelected({
source: api.source,
target: api.target
})}
/>
<div className="flip-card">
<div className="flip-card-inner">
<div className="flip-card-front">
<p id="api-source">{api.source}</p>
</div>
<div className="flip-card-back">
<p className="api-target">{api.target}</p>
</div>
</div>
</div>
</li>
</>
);
};
These codes should work, hope you got the logic also. This approach would be logical in your case.