Dynamically Open and Close antd / react Collapse Component

10,553

There is nothing bad to do it with state variable. Once state or props change, it will render the entire components and also the nexted ones.

But if there is any performance issue due to re-rendering, probably you should look into pure components and react lifecycle methods to improve the performance (or avoid re-render).

In addition, you can also use special props of Antd's Collapse destroyInactivePanel, it will destroy (unmount) inactive panels.

Code for your reference (https://codesandbox.io/s/y30z35p1vv?fontsize=14)

import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Collapse, Button, Select } from "antd";

const Panel = Collapse.Panel;
const text = `
  A dog is a type of domesticated animal.
  Known for its loyalty and faithfulness,
  it can be found as a welcome guest in many households across the world.
`;

class AvoidRenders extends React.Component {
  state = {
    openPanel: "1"
  };

  onChange = key => {
    this.setState({
      openPanel: key
    });
  };

  render = () => {
    return (
      <div>
        <Select
          dropdownMatchSelectWidth={false}
          defaultValue="1"
          onChange={this.onChange}
        >
          <Select.Option key="1" value="1">
            Panel 1
          </Select.Option>
          <Select.Option key="2" value="2">
            Panel 2
          </Select.Option>
          <Select.Option key="3" value="3">
            Panel 3
          </Select.Option>
        </Select>
        <Collapse activeKey={this.state.openPanel} destroyInactivePanel={true}>
          <Panel header="This is panel header 1" key="1">
            <Text1 />
          </Panel>
          <Panel header="This is panel header 2" key="2">
            <Text2 />
          </Panel>
          <Panel header="This is panel header 3" key="3">
            <Text3 />
          </Panel>
        </Collapse>
      </div>
    );
  };
}

class Text1 extends React.PureComponent {
  componentWillUnmount = () => {
    console.log("Destroyed 1");
  };
  componentWillUpdate = () => {
    console.log("Updated 1");
  };
  render = () => (
    <p>
      {console.log("Rendered 1")}
      {text}
    </p>
  );
}

class Text2 extends React.PureComponent {
  componentWillUnmount = () => {
    console.log("Destroyed 2");
  };
  componentWillUpdate = () => {
    console.log("Updated 2");
  };
  render = () => (
    <p>
      {console.log("Rendered 2")}
      {text}
    </p>
  );
}

class Text3 extends React.PureComponent {
  componentWillUnmount = () => {
    console.log("Destroyed 3");
  };
  componentWillUpdate = () => {
    console.log("Updated 3");
  };
  render = () => (
    <p>
      {console.log("Rendered 3")}
      {text}
    </p>
  );
}

ReactDOM.render(<AvoidRenders />, document.getElementById("container"));

I hope, this would help.

Share:
10,553
gotjosh
Author by

gotjosh

Updated on June 25, 2022

Comments

  • gotjosh
    gotjosh almost 2 years

    I want to dynamically open/close Collapse Elements independent of user interaction (to reveal a specific panel via a search result).

    The Collapse react component has setActiveKey (and uses it on user click) but it is not exposed to the Collapse Node reference.

    Is there anyway to open or close in a way that will not trigger a re-render?

    I tried using state and props, but this always re-renders the full tree of nested Collapse components, which in my case takes over 3 seconds.

    Posting the full code would be excessive here as there are many interdependent components nested. However the basic structure is visible here: https://codesandbox.io/s/nk64q4xy8p

    I want to open a specific panel via a different user interaction. In the codepen that would be onChange of the select or clicking the button.

  • gotjosh
    gotjosh about 5 years
    Thanks! I adapted your sandbox: codesandbox.io/embed/n3zl5yyz7m so that: 1) i can both click to expand/collapse the accordion and control from the select 2) i can control the destroy prop 3) all three panels are forceRendered on the first render... Now, I don't seem to get a destroy and render (even if the switch is on) - but i get a re-render of the whole component
  • Shreyans Shrivastav
    Shreyans Shrivastav about 5 years
    It's not destroying because you are using "forceRender". Anyhow, updating activeKey will always re-render the whole component. The best that can be done is to avoid re-rendering of child components (using pure components). If there is any heavy logic in render that's taking more than 3 second,move it to somewhere else. If you really want a way around. Use pure css accordion codepen.io/raubaca/pen/PZzpVe , I can help more, if you can share your code.
  • gotjosh
    gotjosh about 5 years
    Thanks so much for your help and offer! I found a solution using shouldComponentUpdate() and a one time forceRender to ensure that the children components are available at startup, but not rendered on every collapse/expand...
  • gotjosh
    gotjosh about 5 years
    Actually the first line of this answer is a big part of my problem: "it will render the entire component"... I am able to force render once and avoid rerendering of the whole tree here: codesandbox.io/embed/n3zl5yyz7m but i am still not satisfied that the whole component needs to render when i want to change the state of an inner component...