React-Materialize折叠面板程序化展开与回调无限循环问题
Hey there! Let's tackle your two questions about React-Materialize's Collapsible component—first fixing that frustrating infinite loop, then covering how to expand items programmatically.
Fixing the "Maximum update depth exceeded" Error
The root cause of your infinite loop is how you're passing the onClick (or onSelect) handler. Right now, you're directly invoking the function instead of passing a callback reference:
// ❌ Bad: This calls changeBoardType immediately on every render <CollapsibleItem onClick={this.changeBoardType("single")} />
Every time your component renders, this.changeBoardType("single") runs, triggers a setState, which forces another render—and the cycle repeats infinitely.
Solutions:
There are three clean ways to fix this:
Wrap the call in an arrow function
This ensures the function only runs when the click/select event fires:<CollapsibleItem onClick={() => this.changeBoardType("single")} header='Single Hand' />Use a data attribute + bound handler
If you prefer to avoid inline arrow functions (for minor performance gains in large lists), bind your handler in the constructor and use a data attribute to pass the value:class YourComponent extends React.Component { constructor(props) { super(props); this.handleCollapseSelect = this.handleCollapseSelect.bind(this); } handleCollapseSelect(event) { const type = event.currentTarget.dataset.type; this.changeBoardType(type); } render() { return ( <Collapsible accordion> <CollapsibleItem onSelect={this.handleCollapseSelect} data-type="single" header='Single Hand' > {/* ... */} </CollapsibleItem> </Collapsible> ); } }Make changeBoardType return a function
Adjust yourchangeBoardTypemethod to return a callback function instead of executing immediately:changeBoardType = (type) => () => { this.setState({ boardType: type }); // Your other logic here } // Then use it like this: <CollapsibleItem onClick={this.changeBoardType("single")} />
All three approaches will stop the immediate function invocation and prevent the infinite loop.
Programmatic Expansion of Collapsible Items
React-Materialize's Collapsible is built on top of Materialize CSS's vanilla JS component, so we can use its API to control expansion programmatically. Here's how:
Step 1: Add a ref to the Collapsible component
First, create a ref to access the underlying DOM element:
class YourComponent extends React.Component { constructor(props) { super(props); this.collapsibleRef = React.createRef(); this.collapsibleInstance = null; } }
Step 2: Initialize the Collapsible instance
In componentDidMount, initialize the Materialize Collapsible instance using the ref:
componentDidMount() { // Initialize the Collapsible with any options you need this.collapsibleInstance = M.Collapsible.init(this.collapsibleRef.current, { accordion: true // Match your existing accordion setting }); }
Step 3: Use the instance methods to expand/collapse
Materialize's Collapsible instance has open() and close() methods that take an item index (starting at 0) as an argument. You can call these methods anywhere in your component:
// Expand the first item (index 0) expandFirstItem = () => { this.collapsibleInstance.open(0); }; // Collapse the second item (index 1) collapseSecondItem = () => { this.collapsibleInstance.close(1); };
Step 4: Attach the ref to your Collapsible
Finally, link the ref to your Collapsible component in the render method:
render() { return ( <> {/* Example button to trigger programmatic expansion */} <button onClick={this.expandFirstItem}>Open Single Hand Panel</button> <Collapsible accordion ref={this.collapsibleRef}> <CollapsibleItem header='Single Hand' icon='filter_1'> {/* ... */} </CollapsibleItem> {/* Other CollapsibleItems */} </Collapsible> </> ); }
For Function Components (React Hooks)
If you're using function components, the logic is similar but uses useRef and useEffect:
import { useRef, useEffect } from 'react'; function YourComponent() { const collapsibleRef = useRef(null); let collapsibleInstance = null; useEffect(() => { if (collapsibleRef.current) { collapsibleInstance = M.Collapsible.init(collapsibleRef.current, { accordion: true }); } // Cleanup on unmount return () => { if (collapsibleInstance) { collapsibleInstance.destroy(); } }; }, []); const expandFirstItem = () => { collapsibleInstance.open(0); }; return ( <> <button onClick={expandFirstItem}>Open Single Hand Panel</button> <Collapsible accordion ref={collapsibleRef}> {/* ... */} </Collapsible> </> ); }
内容的提问来源于stack exchange,提问作者seanEd

