'Unable to get Plotly.js graph to resize within react-grid-layout

I have been working on a react-grid-layout to display and move graphs around on screen. Currently I am able to add a plotly.js graph to a container. It is moveable but does not resize with the container. I am wondering if an async function is required to allow the plot to re-render when the container box is resized. Below is the code for the grid layout, and for the histogram as well.

const ReactGridLayout = WidthProvider(Responsive);

const Dash = (props) => {
  const { value, addItem } = props
  const ref = useRef()

  return (
    <div>
      <button onClick={addItem}>Add Item</button>
        <ReactGridLayout
          className="layout"
          onLayoutChange={(e) => console.log(props.layout)}
          breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
          cols={{ lg: 12, md: 12, sm: 6, xs: 4, xxs: 2 }}
          resizeHandles={['s', 'w', 'e', 'n', 'sw', 'nw','se', 'ne']}
          verticalCompact={false}
          // onDragStart={'.dragbackground'}
          // isDraggable={false}
          draggableHandle=".dragHandle"
               
      >        
        {_.map(value, (item, i) => (
          <div id = 'gridID' ref={ref} key={i} data-grid={props.layout[i]}  onClick={() => props.updateLayout(props.layout[i])}>
            <span className='dragHandle'>Drag From Here</span>
            <br/>
            <DashItem  key={i} >
              {item}
            </DashItem>
            <span className='dragHandle'>Drag From Here</span>
          </div>
        ))}
        </ReactGridLayout>
    </div>
  );
}


Dash.propTypes = {
  value: PropTypes.array,
  onIncreaseClick: PropTypes.func.isRequired
}


const mapStateToProps = (state) => {
  return {
    value: state.count,
    layout: state.layout,
    onLayoutChange: state.onLayoutChange,
  };
};

const mapDispatchToProps = dispatch => { 
  return {
    addItem: () => dispatch({type: actionTypes.ADD_GRID_ITEM}),
    updateLayout: (i) => dispatch({type: actionTypes.UPDATE_LAYOUT, layoutId: i}),
    removeItem: (i) => dispatch({type: actionTypes.REMOVE_ITEM, layoutElId: i})
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(Dash);

Here is the code for the histogram using plotly.js:

export default function Histogram(props) {

  const { width, height, ref } = useResizeDetector({
    //
  })

  const layout = props.layout

  return(   
    <div style={{height: '100%', width: '100%'}}> 
      <Plot
      useResizeHandler = {true}
      ChartComponent = {Histogram}
      callback={(chart) => this.setChart(chart)}
      style={{width: "100%", height: "100%"}}
      config={{responsive: true}}
      data={[
        {
          x: d1,
          type: 'histogram',
          marker: {
            colour: 'red',
            opacity: 0.5
          },
        },
        {
          x: d2,
          type: 'histogram',
          marker: {
            colour: 'blue',
            opacity: 0.5
          }
        }
      ]}
      layout={{   
        ...layout,
        height: height,
        width: width,
        autosize:true,
        margin: {
          l: 50,
          r: 50,
          b: 100,
          t: 100,
          pad: 4
        },
        title: 'Histogram Title',
          barmode: 'stack',
          bargap: 0.05,
          bargroup: 2,
          xaxis: {
            title: 'X Axis Title'
          },
          yaxis: {
            title: 'Frequency',
            automargin:true
          }}}
      style= {{
        display: 'flex',
        alignItems: 'center',
      }}
      config= {{
      responsive: true 
      }}  
      />
    </div>   
  )
};

The Redux reducer I am using to input graph elements into my grid:

const reducer = (state=initialState, action) => {
    switch (action.type) {
        case actionTypes.ADD_GRID_ITEM:
            const id = uuid()
            console.log("adding: " + id)
            return{
                ...state,
                count: state.count.concat(<Histogram/>),
                layout: state.layout.concat({
                    i: `${id}`,
                    x: 2,
                    y: 0,
                    w: 4,
                    h: 5
                }),
            }


Solution 1:[1]

I have just built something similar and what works for me is to wrap the plot in a div with a ref using react-resize-detector so that it always fills its parent that way it responds to window resize and dragging resize but remains decoupled from the react layout grid. Here is the plot component I used:

import { useResizeDetector } from 'react-resize-detector'
import Plot from 'react-plotly.js'
export default function ResponsivePlot(props) {
  const { width, height, ref } = useResizeDetector({ 
    // refreshMode: 'debounce', 
    // refreshRate: 1000
  })
  const { layout, layers } = props
  return (
    <div ref={ref} style={{ display: 'flex', height: '100%' }}>
      <Plot
        data={layers}
        layout={{
          ...layout, 
          ...{
            width: width, 
            height: height
          }
        }}
      />
    </div>
  )
};

Usage:

import ResponsivePlot from './ResponsivePlot'

//...other stuff 

<ResponsivePlot layers={layers} layout={layout} />

Solution 2:[2]

Why dont you set an ID of the chart container (grid layout child component) and when its resizes simply get the new height and width of the container aka grid layout child component / grid item and pass it on to the chart. I am doing the same and it works flawlessly on all breakpoints.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Graham Hesketh
Solution 2 Abhay Phougat