2
votes

I'm trying to put the div scroll with ref tag inside Dialog Material-UI Design I'm getting an error says Cannot read property 'scrollHeight' of undefined

If I use my code out of Dialog it works fine here's the example

Working Example

All what I'm trying to do is to make the scroll div to start from the end

I did an example on Not working with Dialog

import React from 'react';
import { render } from 'react-dom';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import RaisedButton from 'material-ui/RaisedButton';

class App extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      open: false,
    }
  }

  handleOpen = () => {
    this.setState({ open: true });
  };
  handleClose = () => {
    this.setState({ open: false });
  };
  componentDidMount() {
    this.shoppingListContainer.scrollTop = this.shoppingListContainer.scrollHeight;
  }

  render() {
    return (
      <MuiThemeProvider>
      <div id="scrolldiv">
          <RaisedButton label="Alert" onClick={this.handleOpen} />
        <Dialog
          title="Dialog With Actions"
          modal={false}
          open={this.state.open}
          onRequestClose={this.handleClose}
        >
        <div>
        <ul style={{
          width: 115,
          height: 100,
          overflow: 'auto',
          border: '2px black solid'
        }}
          ref={(shoppingListContainer) => { this.shoppingListContainer = shoppingListContainer; }}
        >
          <li>Instagram</li>
          <li>WhatsApp</li>
          <li>Oculus</li>
          <li>Instagram</li>
          <li>WhatsApp</li>
          <li>Oculus</li>
          <li>Instagram</li>
          <li>WhatsApp</li>
          <li>Oculus</li>
          <li>Instagram</li>
          <li>WhatsApp</li>
          <li>Oculus</li>

        </ul>
        </div>
          </Dialog>
      </div>
      </MuiThemeProvider>
    );
  }
}

render(<App />, document.getElementById('root'));
2
You can find the answer below, with the running code.Haider Ali Anjum

2 Answers

3
votes

Here is a possible solution.

The refs actually work in a pattern, when you define a ref it is assigned to the Global Execution Context this. So that, you can use them as this.YourRefName. But these refs a re not assigned to this, until your component is rendered/mounted completely.

In your case, you are using a ref of the <ul> component as named shoppingListContainer. in the method componentDidMount() method of the root component of your project, After rendeing the intial view of your project, you can see the <ul> component is not rendered yet, That's why the ref you provided is showing undefined.

In such cases you do apply module approach to your component hierarchy. Following is the running code as per your implementation:

import React from 'react';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
import Dialog from 'material-ui/Dialog';
import RaisedButton from 'material-ui/RaisedButton';

class ListContainer extends React.Component {
  scrollToBottom = () => {
    this.listContainer.scrollTop = this.listContainer.scrollHeight;
  }

  componentDidMount() {
    this.scrollToBottom();
  }

  componentDidUpdate() {
    this.scrollToBottom();
  }

  render() {
    return (
      <ul style={{
        width: 115,
        height: 100,
        overflow: 'auto',
        border: '2px black solid'
      }}
        ref={(element) => { this.listContainer = element; }}
      >
        <li>Instagram</li>
        <li>WhatsApp</li>
        <li>Oculus</li>
        <li>Instagram</li>
        <li>WhatsApp</li>
        <li>Oculus</li>
        <li>Instagram</li>
        <li>WhatsApp</li>
        <li>Oculus</li>
        <li>Instagram</li>
        <li>WhatsApp</li>
        <li>Oculus</li>

      </ul>
    )
  }

}

class App extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      open: false,
    }
  }

  handleOpen = () => {
    this.setState({ open: true });
  };
  handleClose = () => {
    this.setState({ open: false });
  };

  render() {
    return (
      <MuiThemeProvider>
        <div id="scrolldiv" ref='listC' >
          <RaisedButton label="Alert" onClick={this.handleOpen} />
          <Dialog
            title="Dialog With Actions"
            modal={false}
            open={this.state.open}
            onRequestClose={this.handleClose}
          >
            <div>
              <ListContainer />
            </div>
          </Dialog>
        </div>
      </MuiThemeProvider>
    );
  }
}

export default App;

In the code above, I broke the component structure, to make sure reactjs will access the ref, exactly when the component with ref definition is rendered.

It will actually make your approach easy to understand and maintain.

1
votes

This is happening because react is not able to find shoppingListContainer You can try this code, it worked for me.

import React from 'react';
import { render } from 'react-dom';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import RaisedButton from 'material-ui/RaisedButton';

class App extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      open: false,
    }
  }

  handleOpen = () => {
    this.setState({ open: true });
  };
  handleClose = () => {
    this.setState({ open: false });
  };
  componentDidMount() {
    let shoppingListContainer = this.refs.scrolldiv;
    if (shoppingListContainer) {
      shoppingListContainer.style.scrollWidth =
        shoppingListContainer.style.scrollHeight;
    }
  }

  render() {
    return (
      <MuiThemeProvider>
      <div>
          <RaisedButton label="Alert" onClick={this.handleOpen} />
        <Dialog
          title="Dialog With Actions"
          modal={false}
          open={this.state.open}
          onRequestClose={this.handleClose}
        >

            <div className="shopping-list" refs="scrolldiv" 
            style={{
              width: 125,
              height: 100,
              overflow: 'auto',
              border: '2px black solid'
            }}>
              <ul>
                <li>Instagram</li>
                <li>WhatsApp</li>
                <li>Oculus</li>
                <li>Instagram</li>
                <li>WhatsApp</li>
                <li>Oculus</li>
              </ul>
            </div>
          </Dialog>

      </div>
      </MuiThemeProvider>
    );
  }
}

render(<App />, document.getElementById('root'));