I made a react component which adds a border when clicked. Now I have 3 similar components on the same page. When I click anyone of them,it adds a border. When I click on any of the other remaining components I want the border to be removed from first and added to the second. Also if I click anywhere in the document other than the components, existing border if any must be removed. I have achieved the same using vanilla Js but unable to implement it using react. Any help is appreciated.
import React from 'react'
function NavItem() {
const menu = {
initial: "noborder",
final: "border",
};
const [itemStyle, setItemStyle] = useState("");
const handleClick=()=>{
setItemStyle(final)
}
return (
<div onClick={handleClick} className={`${itemStyle}`}>NavItem</div>
)
}
export default NavItem
CodePudding user response:
Lift the state up to a parent component. Pass the selected state down to each child component. Use an effect hook to wire up a global event handler to catch clicks elsewhere in the document.
const Parent = () => {
const [selected, setSelected] = React.useState(null);
console.log(selected);
React.useEffect(() => {
const clearSelected = () => setSelected(null);
window.addEventListener("click", clearSelected);
return () => window.removeEventListener("click", clearSelected);
}, []);
const values = ["a", "b", "c"];
return (
<div>
{values.map((value) => (
<Child
key={value}
value={value}
selected={value === selected}
setSelected={setSelected}
/>
))}
</div>
);
};
const Child = ({ value, selected, setSelected }) => {
console.log({ setSelected, selected, value });
return (
<button
type="button"
className={selected ? "selected" : "not-selected"}
onClick={(e) => {
e.stopPropagation();
setSelected(value);
}}
>
{value}
</button>
);
};
ReactDOM.render(<Parent />, document.getElementById("root"));
CodePudding user response:
Your problem is you call setState
separately for each NavItem
, and you don't have the common parent component to share that state, so you should lift your states to the parent component
I assume that you have a parent component called Nav
import React from 'react'
import NavItem from './NavItem'
function Nav() {
const menu = {
initial: "noborder",
final: "border",
};
const [activeNavItem, setActiveNavItem] = useState(1); //active the first `NavItem`
//you can reduce duplication by `map` or depends on your setup
return <>
<NavItem itemStyle={activeNavItem === 1 ? menu.final : menu.initial } handleClick={() => setActiveNavItem(1)}>Item 1</NavItem>
<NavItem itemStyle={activeNavItem === 2 ? menu.final : menu.initial } handleClick={() => setActiveNavItem(2)}>Item 2</NavItem>
<NavItem itemStyle={activeNavItem === 3 ? menu.final : menu.initial } handleClick={() => setActiveNavItem(3)}>Item 3</NavItem>
</>
}
export default Nav
Now you have the same state for all NavItem
import React from 'react'
function NavItem({ itemStyle, handleClick }) {
return (
<div onClick={handleClick} className={`${itemStyle}`}>NavItem</div>
)
}
export default NavItem