How can I set a button to switch all accordions on and off?
P粉391677921
2023-08-30 16:57:51
<p>I have a component that creates a collapsible accordion using the following code: </p>
<pre class="brush:php;toolbar:false;">import React, {useState} from 'react';
const Collapsible = (props) =>{
const [open, setOpen] = useState(false);
const toggle = () => {
setOpen(!open);
}
return(
<div>
<button className={props.level} onClick={toggle}>{props.label}</button>
{open && (
<div className="toggle">
{props.children}
</div>
)}
</div>
)
}
export default Collapsible;</pre>
<p>I use it in several places in the main application and sometimes in other accordions. In multiple instances, I actually had no idea how many accordions there would be on the page because they were rendered dynamically based on the data. With this in mind, I want to create a button in the main application that turns on (and off) all accordions without having to set a fixed number, and without having to render all accordions in the main application (i.e. some accordions in other component and then imported into the main application). </p>
<p>I tried using ref hooks to achieve this: </p>
<ol>
<li>Add ref in the button of the Collapsible component and access it from the parent component through props: </li>
</ol>
<pre class="brush:php;toolbar:false;"><button className={props.level} onClick={toggle} ref={props.innerRef}>{props.label}</button> ;</pre>
<ol start="2">
<li>Add the following ref in the main application: </li>
</ol>
<pre class="brush:php;toolbar:false;">const childRef = useRef();
const openClick = () => {
childRef.state = true;
}
const closeClick = () => {
childRef.state = false;
}</pre>
<ol start="3">
<li>Use the following buttons in the main application: </li>
</ol>
<pre class="brush:php;toolbar:false;"><button onClick = {openClick}>
Expand All
</button>
<button onClick = {closeClick}>
Collapse all
</button></pre>
<ol start="4">
<li>Add ref to accordion when rendering: </li>
</ol>
<pre class="brush:php;toolbar:false;"><Collapsible label="" level="content" innerRef={childRef}></pre>
<p>This actually does nothing, probably because the way I was trying to access the state in step 2 was wrong, but I figured it was worth a try...</p>
<p>Any ideas on if this is possible? </p>
You can use
Redux
.openAllAccordions
, loop through the IDs, and set the accordions belonging to that ID to open=truecloseAllAccordions
, loop through the IDs, and set the accordions belonging to that ID to open=falseIn a more or less arbitrary collection of component instances, it is common for some coordination to be required. One approach I've used successfully is to create a Context with an associated hook that the component can register with. This hook returns a view of the shared state and a function that modifies that state to suit your needs.
Here you can create a
opener
function that stores each registered component and provide a Context for theopenAll
/closeAll
functions:...There is also a hook called by each child component to register with the context and return the familiar
toggle
/open
value:There is also a separate hook for performing batch operations which is also convenient:
Sandbox
Please note that for simplicity, a separate
opener
(akasetOpen
) function is used here as a unique identifier for each registered component. A flexible alternative is to use other identifiers, so you can open/close arbitrary accordions during navigation etc.