Input resizing with React

An automatically expanding textarea that respects word-breaks

HTML

<body>
  <script src="https://fb.me/react-15.1.0.js"></script>
  <script src="https://fb.me/react-dom-15.1.0.js"></script>

  <img style="width:400px;" src="https://cloud.githubusercontent.com/assets/547148/18024962/171ce86c-6bce-11e6-8216-5ab7f361fdde.jpg" />
  <p>An automatically expanding textarea that respects CSS word-breaks.</p>
  <p>Uses a ghost div to measure height. See CSS comments to hide it, and you're ready to go.</p>
  <p>👻</p>

  <div id="app"></div>
</body>

SCSS

* {
  font-family: monospace;
  font-size: 16px;
}

body {
  padding: 1em 1em 6em 1em;
}

p, label {
  display: block;
  margin: 0.8em 0;;
}

.container {
  position: relative;
}

.textarea {
  width: 360px;
  outline: none;
  min-height: 20px;
  padding: 0;
  box-shadow: none;
  display: block;
  border: 2px solid black;
  overflow: hidden;  // Removes scrollbar
  transition: height 0.2s ease;
}

.textarea--ghost {
  opacity: 0.3;
  display: block;
  white-space: pre-wrap;
  word-wrap: break-word;  
  //  Uncomment below to hide the ghost div... 
  //
  //  visibility: hidden;
  //  position: absolute;
  //  top: 0;
}

Babel + JSX

const DEFAULT_HEIGHT = 20;

class Textarea extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      height: DEFAULT_HEIGHT,
      value: "Don't get lost in the upside down",
    };

    this.setValue = this.setValue.bind(this);
    this.setFilledTextareaHeight = this.setFilledTextareaHeight.bind(this);
  }

  componentDidMount() {
    this.mounted = true;

    this.setFilledTextareaHeight();
  }

  setFilledTextareaHeight() {
    if (this.mounted) {
      const element = this.ghost;

      this.setState({
        height: element.clientHeight,
      });
    }
  }

  setValue(event) {
    const { value }= event.target;

    this.setState({ value });
  }

  getExpandableField() {
    const isOneLine = this.state.height <= DEFAULT_HEIGHT;
    const { height, value } = this.state;

    return (
      <div>
        <label htmlFor="textarea">Add some text...</label>
        <textarea
          className="textarea"
          name="textarea"
          id="textarea"
          autoFocus={true}
          defaultValue={value}
          style={{
            height,
            resize: isOneLine ? "none" : null
          }}
          onChange={this.setValue}
          onKeyUp={this.setFilledTextareaHeight}
        />
      </div>
    );
  }

  getGhostField() {
    return (
      <div
        className="textarea textarea--ghost"
        ref={(c) => this.ghost = c}
        aria-hidden="true"
      >
        {this.state.value}
      </div>
    );
  }

  render() {
    return (
      <div className="container">
        {this.getExpandableField()}
        {this.getGhostField()}
      </div>
    );
  }
}

ReactDOM.render(
  <Textarea />,
  document.getElementById("app")
);