How to make children clicked when clicking parent - reactjs

How can I make children clicked when clicking parent?
My component is like this
const AllForms = ({ data }) => {
const divclick = ()=>{
// click all the buttons inside the div
}
const populateFormidButton = () => {
return data.subs.map((i, index) => {
return (
<button key={index} type="submit" form={i.formId}></button>
)
})
}
return (
<div onClick={divclick}>
{populateFormidButton()}
</div>
)
}
export default AllForms
I want when I click the div all the buttons inside that div will be clicked too. How to do that?

I hope I understood the task correctly.
In this example I used useRef and querySelectorAll("button") and click()
import { useRef } from "react";
const Form = ({ data }) => {
const divReference = useRef(null);
const divclick = () => {
const buttonList = [...divReference.current.querySelectorAll("button")];
buttonList.forEach(el=>el.click());
};
const populateFormidButton = () => {
return data.subs.map((i, index) => {
return (
<button key={index} type="submit" form={i.formId}>
{i}
</button>
);
});
};
return (
<div
ref={divReference}
onClick={divclick}
>
{populateFormidButton()}
</div>
);
};
export default Form;

Related

React 18: Button click in map function to reflect information only related to one item and not all items

I am new to React and using React 18 in this app. My problem is that if I click one button inside a map function, it reflects information about all the items. I want only that item information to show for which I clicked the button. The isShown === true part in the CountryInfo.js file is what should reflect only one item; currently clicking the show button shows all item information on the UI (I don't want this to happen). How do I do this?
Visually, this is my UI,
If you see the image above, clicking any show button returns all countries information, which should not happen.
Below is my code:
App.js
import { useState, useEffect } from 'react';
import axios from "axios";
import CountryInfo from './components/CountryInfo';
const App = () => {
const [countries, setCountries] = useState([]);
const [searchCountry, setSearchCountry] = useState("");
const handleCountryChange = event => {
setSearchCountry(event.target.value);
}
const getAllCountriesData = () => {
axios.get("https://restcountries.com/v3.1/all")
.then(response => {
setCountries(response.data);
})
}
useEffect(() => {
getAllCountriesData();
}, []);
return (
<>
<h2>Data for countries</h2>
find countries:
<input value={searchCountry} onChange={handleCountryChange} />
{searchCountry.length > 0 && <CountryInfo countries={countries} searchCountry={searchCountry} />}
</>
)
}
export default App;
CountryInfo.js
import React from "react";
import { useState } from "react";
const CountryInfo = ({ countries, searchCountry }) => {
const [isShown, setIsShown] = useState(false);
let filteredList = countries.filter(country =>
country.name.common.toLowerCase().includes(searchCountry.toLowerCase()));
const handleClick = () => {
setIsShown(true);
}
if (filteredList.length > 10) {
return <div>Too many matches, specify another filter</div>
}
else {
return filteredList.map(country => {
return (
<>
<div key={country.name.common}>
{!isShown &&
<div>
{country.name.common}
<button type="submit" onClick={handleClick}>show</button>
</div>
}
{isShown &&
<div key={country.name.common}>
<h2>{country.name.common}</h2>
<p>
Capital: {country.capital}
{'\n'}
Area: {country.area}
</p>
Languages:
<ul>
{
Object.values(country.languages)
.map((language, index) => <li key={index}>{language}</li>)
}
</ul>
<img src={country.flags.png} alt={`${country.name.common} flag`} height={150} />
</div>
}
</div>
</>
)
})
}
}
export default CountryInfo;

React - How to prevent parent re-render on prop change

I am making a calculator using react.
Every time I press a number button, the whole application re-renders, instead of the <Display />.
To prevent it, I tried 2 different approaches for App, But neither of them worked.
Here is the sandbox link.
Any help would be appreciated.
Put clickHandler inside of useCallback()
const App = () => {
const [screen, setScreen] = useState("0");
console.log("render");
const clickHandler = useCallback(
(val) => {
if (val === "AC") {
setScreen("");
return;
}
screen === "0" ? setScreen(val) : setScreen(screen + val);
},
[screen]
);
return (
<div className="App">
<div className="display">{screen}</div>
<ButtonList clickHandler={clickHandler} />
</div>
);
};
Put Display component inside of React.memo
const App = () => {
const [screen, setScreen] = useState("0");
console.log("render");
const clickHandler = (val) => {
if (val === "AC") {
setScreen("");
return;
}
screen === "0" ? setScreen(val) : setScreen(screen + val);
};
const displayComponent = () => {
return (
<>
<div className="display">{screen}</div>
<ButtonList clickHandler={clickHandler} />
</>
);
};
const MemoizedComponent = React.memo(displayComponent);
return (
<div className="App">
<MemoizedComponent />
</div>
);
};
And here's the ButtonList & Button component.
export const ButtonList = ({ clickHandler }) => {
const arr = [...Array.from(Array(10).keys()).reverse(), "AC"];
return (
<div className="buttons">
<div className="numbersWrapper">
{arr.map((item) => (
<Button
key={item}
clickHandler={clickHandler}
value={item.toString()}
/>
))}
</div>
</div>
);
};
export const Button = ({ value, clickHandler }) => {
return (
<button
name={value}
onClick={() => {
clickHandler(value); //where the clickEvent happens
}}
>
{value}
</button>
);
};
If you don't want a component re-render,You would have to define the click handler in another component that you would like to re-render.
So do it like this:
const App = () => {
console.log("render");
return (
<div className="App">
<childComponent />
</div>
);
};
export const childComponent = () => {
const [screen, setScreen] = useState("0");
const clickHandler = (val) => {
if (val === "AC") {
setScreen("");
return;
}
screen === "0" ? setScreen(val) : setScreen(screen + val);
};
return (
<>
<div className="display">{screen}</div>
<ButtonList clickHandler={clickHandler} />
</>
);
}
This way you prevent a particular component from re-rendering. But note that if you update a state or do anything from which causes re-renders from the parent component, It would equally re-render the child component.

Dynamic react toggle button for multiple elements

I need to toggle multiple blocks with true/false in react state, but true and false works for all blocks at the same time.
import { useState } from "react";
...
const [toggleThisElement, setToggleThisElement] = useState(false);
...
{
data.map((d, id) => {
return (
<div className="single-history" key={id}>
<div className="h-head">
click this div for toggle h-info block
</div>
<div className="h-info">toggling block</div>
</div>
)
})
}
Currently, all your toggle items share the same state. To avoid that create a separate component for toggling logic called ToggleItem as below.
import { useState } from "react";
const ToggleItem = ({ discription, id }) => {
const [toggleThisElement, setToggleThisElement] = useState(false);
return (
<div className="single-history" key={id}>
<button
className="h-head"
onClick={() => setToggleThisElement((prev) => !prev)}
>
click this btn for toggle h-info block {id}
</button>
{toggleThisElement && <div className="h-info">{discription}</div>}
</div>
);
};
export default function App() {
const data = ["first", "second", "third"];
return (
<>
{data.map((d, id) => {
return <ToggleItem id={id} discription={d} />;
})}
</>
);
}

how to handle onClick event with multiple component with the same class name in React?

I'm new to react.js and I want to apply the toggle feature at 'place-box' by using 'isOpen' state and my intention is it only works when I click single place-box div so I added onClick event at 'place-box' div. but all of the elements are toggled at the same time.
I guess it's because they all have the same class name.
how can I fix this?
import React, { useState, useEffect } from "react";
import { useQuery } from "#apollo/client";
import { FETCH_CITIES_QUERY } from "../../server/Data/RentQueries";
import PlaceResult from "../Rent/PlaceResult";
const CityResult = (props) => {
const [placeId, setPlaceId] = useState();
const [isOpen, setIsOpen] = useState(false);
const { loading, error, data } = useQuery(FETCH_CITIES_QUERY, {
variables: { cityName: cityName },
});
const showPlaceInfo = (placeId, e) => {
e.preventDefault();
setPlaceId(placeId);
setIsOpen((isOpen) => !isOpen);
};
return (
<div>
{data &&
data.cities.map((city) => {
return (
<div className="city-box">
{city.places.map((place) => {
return (
// this is place-box div and I added onClick event here
<div
className="place-box"
key={place.id}
onClick={(e) => {
e.stopPropagation();
showPlaceInfo(place.id, e);
}}
>
<li className="place-name">{place.name}</li>
{isOpen && (
<PlaceResult className="place-indiv" placeId={placeId} />
)}
{!isOpen && (
<div className="place-info-box">
<li>{place.address}</li>
{conditionCheck(city.condition)}
<li>{place.phone}</li>
</div>
)}
</div>
);
})}
</div>
);
})}
</div>
);
};
export default CityResult;
Your variable isOpen is used for all cities. If you change isOpen to true all place-boxes are opened. You should store the id of the currently opened city inside a variable and compare against it to check if the current city in the for loop should be opened.
import React, { useState, useEffect } from "react";
import { useQuery } from "#apollo/client";
import { FETCH_CITIES_QUERY } from "../../server/Data/RentQueries";
import PlaceResult from "../Rent/PlaceResult";
const CityResult = (props) => {
const [placeId, setPlaceId] = useState();
const [openedPlaceId, setOpenedPlaceId] = useState(undefined);
const { loading, error, data } = useQuery(FETCH_CITIES_QUERY, {
variables: { cityName: cityName },
});
const showPlaceInfo = (placeId, e) => {
e.preventDefault();
setPlaceId(placeId);
setOpenedPlaceId(placeId);
};
return (
<div>
{data &&
data.cities.map((city) => {
return (
<div className="city-box">
{city.places.map((place) => {
return (
// this is place-box div and I added onClick event here
<div
className="place-box"
key={place.id}
onClick={(e) => {
e.stopPropagation();
showPlaceInfo(place.id, e);
}}
>
<li className="place-name">{place.name}</li>
{openedPlaceId === place.id && (
<PlaceResult className="place-indiv" placeId={placeId} />
)}
{!(openedPlaceId === place.id) && (
<div className="place-info-box">
<li>{place.address}</li>
{conditionCheck(city.condition)}
<li>{place.phone}</li>
</div>
)}
</div>
);
})}
</div>
);
})}
</div>
);
};
export default CityResult;
This way only the clicked place will be opened.

How can I change the background color of elements when they're clicked?

I have a list of div elements in a ReactJS projects. I want to just get an indication when someone clicks change the background color.
the following is the basic code.
function changetoselected(event){
// now change backgroundColor of
// event.currentTarget to white
}
<div>
bigarrayofsize100plus.map((item,index) =>{
return(
<div
className="p-2"
onClick={(e) => changetoselected(e)}
style={{backgroundColor:"green"}}
>
.....
</div>
)
}
</div>
I dont want to store in the state all the elemets uncessarily. I dont have to trace clicked items here.
If once clicks i want to just change color. How can i do it
Use the style property to set a backgroundColor like this.
function changetoSelected(event){
event.target.style.backgroundColor = '#fff'
}
You can also use Refs in React like this
For a Function Component do this
`
import { useRef } from 'react';
function MyComponent() {
const divEl = useRef(null);
const changeToSelected = () => {
divEl.current.style.backgroundColor = '#fff';
};
return (
<div ref={divEl} onClick={changeToSelected}>
...
</div>
);
}
For a Class Component do this
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.divElement = React.createRef();
}
changetoselected = () => {
this.divElement.current.style.backgroundColor = '#fff';
}
render() {
return <div ref={this.divElement} onClick={this.changetoselected}>
...
</div>;
}
}
After all, working with pure dom (by ref or event) may not be what you are searching for, you can consider using react state and apply className or style to your dom elements
import { useState } from 'react';
const MyComponent = () => {
const [backgroundColor, setBackgroundColor] = useState('green');
return (
<div
onClick={() => setBackgroundColor('white')}
style={{ backgroundColor }}
>
...
</div>
);
}
EDIT
function MyComponent() {
const divEl = useRef(null);
const changeToSelected = () => {
divEl.current.style.backgroundColor = '#fff';
};
return (
<div>
{bigarrayofsize100plus.map((item,index) =>
<ChildComp
key={index}
item={item}
>
.....
</div>
)}
</div>
);
}
function ChildComp({ item }) {
const divEl = useRef(null);
const changeToSelected = () => {
divEl.current.style.backgroundColor = '#fff';
};
return (
<div
ref={divEl}
onClick={changeToSelected}
className="p-2"
style={{backgroundColor:"green"}}
>
// do stuff with item heere
</div>
);
}

Resources