I have 2 details
tag, each has a control to toggle it on/off. Code snippet here . Clicking Control A should toggle on/off page A, clicking Control B should toggle on/off page B.
I did it with an if else if
plus 2 useState
, this would not be feasible when there are multiple details
. How can I refactor the code such that maybe the if else if
can be avoided and it detects which Control I click in a cleverer way?
Page.js
const Page = ({ name, isOpen, setIsOpen }) => {
return (
<>
<details
open={isOpen}
onToggle={(e) => {
setIsOpen(e.target.open);
}}
>
<summary>Page {name} title</summary>
<div>Page {name} contents</div>
</details>
</>
);
};
export default Page;
Control.js
const Control = ({ toggle }) => {
return (
<>
<a onClick={() => toggle("A")} href="#/">
Control A
</a>
<br />
<a onClick={() => toggle("B")} href="#/">
Control B
</a>
</>
);
};
App.js
export default function App() {
const [isOpenA, setIsOpenA] = useState(false);
const [isOpenB, setIsOpenB] = useState(false);
const toggle = (name) => {
if (name === "A") {
setIsOpenA((prevState) => !prevState);
} else if (name === "B") {
setIsOpenB((prevState) => !prevState);
}
};
return (
<div className="App">
<Control toggle={toggle} />
<Page name={"A"} isOpen={isOpenA} setIsOpen={setIsOpenA} />
<Page name={"B"} isOpen={isOpenB} setIsOpen={setIsOpenB} />
</div>
);
}
You can use an array to represent open ones
const [openPages, setOpenPages] = useState([])
And to toggle filter the array
const toggle = (name) => {
if(openPages.includes(name)){
setOpenPages(openPages.filter(o=>o!=name))
}else{
setOpenPages(pages=>{ return [...pages,name]}
}
}
I would personally use an object as a map for your toggles as in something like:
const [isOpen, _setIsOpen] = useState({});
const setIsOpen = (pageName,value) => _setIsOpen({
...isOpen,
[pageName]: value
});
const toggle = (name) => setIsOpen(name, !isOpen[name]);
and then in the template part:
<Page name={"A"} isOpen={isOpen["A"]} setIsOpen={toggle("A")} />
In this way you can have as many toggles you want and use them in any way you want
I think this would be quite cleaner, also you should put the various page names in an array and iterate over them as in
const pageNames = ["A","B"];
{
pageNames.map( name =>
<Page name={name} isOpen={isOpen[name]} setIsOpen={toggle(name)} />)
}
At least that's how I would go about it
Adithya's answer worked for me.
For future reference, I put the full working code here. The onToggle
attribute in Page.js
is not needed. All required is passing correct true/false
to open={isOpen}
in Page.js
.
App.js:
export default function App() {
const [openPages, setOpenPages] = useState([]);
const toggle = (name) => {
if (openPages.includes(name)) {
setOpenPages(openPages.filter((o) => o !== name));
} else {
setOpenPages((pages) => {
return [...pages, name];
});
}
};
return (
<div className="App">
<Control toggle={toggle} />
<Page name={"A"} isOpen={openPages.includes("A")} />
<Page name={"B"} isOpen={openPages.includes("B")} />
<Page name={"C"} isOpen={openPages.includes("C")} />
</div>
);
}
Page.js
const Page = ({ name, isOpen }) => {
return (
<>
<details open={isOpen}>
<summary>Page {name} title</summary>
<div>Page {name} contents</div>
</details>
</>
);
};
Control.js remains the same.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.