React-grid-layout: Unable to have grid layout rerendered when switching between tabs.

Created on 16 Jun 2019  路  11Comments  路  Source: STRML/react-grid-layout

I have a page that has a bunch of tabs, where each tab has its own component that contains a react-grid-layout. When I refresh the page, the first tab is selected automatically, and displays fine. But when I click to another tab, the grid layout seems all left justified - i.e. all the components are all to the left (x seems all messed up, but y seems fine in the layouts). Only when I resize the window or zoom in/out via browser, the X gets fixed and layout is ok. But I obviously should not have to play with the browser size to fix this.

Below is what I am doing:

  toggle(tab) {
    if (this.state.activeTab !== tab) {
      this.setState({
        activeTab: tab
      });
      console.log('switch to tab' + tab);
    }
  }

...

        <Nav tabs>
          <NavItem>
            <NavLink
              className={classnames({ active: this.state.activeTab === '1' })}
              onClick={() => { this.toggle('1'); }}
            >
              Tab1
            </NavLink>
          </NavItem>
           <NavItem>
            <NavLink
              className={classnames({ active: this.state.activeTab === '2' })}
              onClick={() => { this.toggle('2'); }}
            >
              Tab2
            </NavLink>
          </NavItem>
        </Nav>
        <TabContent activeTab={this.state.activeTab}>
          <TabPane tabId="1">
            <MyComponentCard1 title='Tab1'/>
          </TabPane>
          <TabPane tabId="2">
            <MyComponentCard2 title='Tab2'/>
          </TabPane>
        </TabContent>

where MyComponentCard1 and MyComponentCard2 have their own ReactGridLayout defined.

Any help would be appreciated. I'm on version 0.16.6.

Most helpful comment

@nitinguptadr-dori , what seems to work better is to keep the setState but change the condition as follow:

if (node instanceof HTMLElement && node.offsetWidth > 0) _this.setState({ width: node.offsetWidth });

All 11 comments

I am having exact same issue and I am using exact same version as @nitinguptadr-dori
I made sure that the ResponsiveGridLayouts have different keys and the children as well.

Below are a list of the other packages I am using:

"dependencies": {
"bootstrap": "^4.3.1",
"chart.js": "^2.7.2",
"classnames": "^2.2.6",
"d3-geo": "^1.10.0",
"faker": "^4.1.0",
"jwt-decode": "^2.2.0",
"node-sass": "^4.11.0",
"prop-types": "^15.6.2",
"react": "^16.4.1",
"react-bootstrap": "^1.0.0-beta.6",
"react-chartjs-2": "^2.7.2",
"react-component-queries": "^2.3.0",
"react-d3-map": "^0.8.3",
"react-d3-speedometer": "^0.4.1",
"react-data-grid": "^6.1.0-0",
"react-dom": "^16.4.1",
"react-ga": "^2.5.3",
"react-grid-layout": "^0.16.6",
"react-icons": "^2.2.7",
"react-infinite-calendar": "^2.3.1",
"react-json-view": "^1.19.1",
"react-multi-bar-slider": "^1.2.1",
"react-notification-system": "^0.2.17",
"react-responsive-carousel": "^3.1.43",
"react-router": "^4.3.1",
"react-router-dom": "^4.3.1",
"react-select": "^3.0.3",
"react-simple-maps": "^0.12.1",
"react-sizeme": "^2.5.2",
"react-transition-group": "^2.3.1",
"reactstrap": "^6.1.0",
"victory": "^0.27.1"
},

@nitinguptadr-dori I had a similar use case a couple years ago (multiple RGL tabs), and I seem to remember having the same issue that you're reporting. I can't promise it will work, but I think I addressed the issue by triggering the resize event programmatically. Something like this:

function fireResized() {
        let evt = document.createEvent("HTMLEvents");
        evt.initEvent('resize', true, false);
        window.dispatchEvent(evt);
    }

I hope this is helpful in some way. I can't exactly remember, but I might have needed to make other adjustments to get it fully working. In any case, I can confirm that your use case is possible without modifications to the core RGL code.

@stephenrs @STRML

Just manually triggering a resize event doesn't seem to help. I am able to see the resize event triggered and can get into the handler, but something still doesn't work.

What I have debugged so far is that there is something weird happening in the translate calculation for a grid item. It's not properly calculating the tx properly for the transform(tx, ty). I can see this when I inspect the style for each placed react-grid-item. This only gets properly set when I do a manual size of the browser window or zoom in/out via browser.

So the issue appears to be circumvented if I manually remove the call to update the width in the following WidthProvider:
Commented:

_this.setState({ width: node.offsetWidth })

in WidthProvider.js

function WidthProvider(ComposedComponent) {
  var _class, _temp2;

  return _temp2 = _class = function (_React$Component) {
    _inherits(WidthProvider, _React$Component);

    function WidthProvider() {
      var _temp, _this, _ret;

      _classCallCheck(this, WidthProvider);

      for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
        args[_key] = arguments[_key];
      }

      return _ret = (_temp = (_this = _possibleConstructorReturn(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this), _this.state = {
        width: 1280
      }, _this.mounted = false, _this.onWindowResize = function () {
        if (!_this.mounted) return;
        // eslint-disable-next-line
        var node = _reactDom2.default.findDOMNode(_this); // Flow casts this to Text | Element
        if (node instanceof HTMLElement) {
//        _this.setState({ width: node.offsetWidth });
        }
      }, _temp), _possibleConstructorReturn(_this, _ret);
    }

But this obviously is not a resolution, I still don't understand why the node.offsetWidth is being set to 0 when I see the failure.

It appears my client component tabs are not setting the container width appropriately. Not sure how to ensure that gets properly set when we have Nav Tabs inside a RGL layout.

I looked at the ReactDOM.findDOMNode(this).getBoundingClientRect().width and saw that for the internal components that are displayed in my tabs, they are set to 0 width until I resize the window manually - then it gets updated.

FYI, switching over to not using the WidthProvider and using RGL directly also circumvents the issue, but then I have a fixed width layout which doesn't look nice when I zoom in/out or have different screen sizes.

@nitinguptadr-dori , commenting out the line in WidthProvider as you mentioned above is causing the grid not be responsive anymore (panels in the grid are not resizing when resizing browser window). It still is responsive for you??

@nitinguptadr-dori , what seems to work better is to keep the setState but change the condition as follow:

if (node instanceof HTMLElement && node.offsetWidth > 0) _this.setState({ width: node.offsetWidth });

Excellent. I believe that change is working for me now as well. I branched out the WidthProvider from the react-grid-layout and supplied my own version.

@nitinguptadr-dori , now the question is: shouldn't this be a permanent fix in react-grid-layout?
I am not sure what the process would be to have this reviewed and incorporated in an official version ...
Can anybody @ react-grid-layout help with this?

@STRML , see the change @nitinguptadr-dori and I had to make for tabs to work. What is the process to have this change reviewed and if found ok, merged into the main repo?
Thank you for your help, highly appreciated.

Was this page helpful?
0 / 5 - 0 ratings