import React, { Component } from 'react';

import PropTypes from 'prop-types';
import isString from 'lodash.isstring';
import result from 'lodash.result';

export default class DatString extends Component {
  static propTypes = {
    data: PropTypes.object,
    path: PropTypes.string,
    label: PropTypes.string,
    labelWidth: PropTypes.number,
    liveUpdate: PropTypes.bool,
    onUpdate: PropTypes.func,
    _onUpdateValue: PropTypes.func
  };

  state = {
    value: this.getValue()
  };

  componentWillReceiveProps(nextProps) {
    this.setState({
      value: this.getValue(nextProps)
    });
  }

  getValue(props = this.props) {
    return result(props.data, props.path);
  }

  handleChange = event => {
    const { value } = event.target;

    this.setState({ value }, () => {
      this.props.liveUpdate && this.update();
    });
  };

  handleFocus = () => {
    document.addEventListener('keydown', this.handleKeyDown);
  };

  handleBlur = () => {
    document.removeEventListener('keydown', this.handleKeyDown);
    window.getSelection().removeAllRanges();
    !this.props.liveUpdate && this.update();
  };

  handleKeyDown = event => {
    const key = event.keyCode || event.which;

    if (key === 13) {
      !this.props.liveUpdate && this.update();
    }
  };

  update() {
    const { value } = this.state;

    this.props._onUpdateValue &&
      this.props._onUpdateValue(this.props.path, value);
    this.props.onUpdate && this.props.onUpdate(value);
  }

  render() {
    const { path, label, labelWidth } = this.props;
    const labelText = isString(label) ? label : path;

    return (
      <li className="cr string">
        <label>
          <span className="label-text" style={{ width: `${labelWidth}%` }}>
            {labelText}
          </span>
          <input
            type="text"
            value={this.state.value}
            style={{ width: `${100 - labelWidth}%` }}
            onChange={this.handleChange}
            onFocus={this.handleFocus}
            onBlur={this.handleBlur}
          />
        </label>
      </li>
    );
  }
}

import React, { Component } from 'react';

import PropTypes from 'prop-types';
import isString from 'lodash.isstring';
import result from 'lodash.result';

export default class DatString extends Component {
  static propTypes = {
    data: PropTypes.object,
    path: PropTypes.string,
    label: PropTypes.string,
    labelWidth: PropTypes.number,
    liveUpdate: PropTypes.bool,
    onUpdate: PropTypes.func,
    _onUpdateValue: PropTypes.func
  };

  static defaultProps = {
    data: null,
    path: null,
    label: null,
    labelWidth: 40,
    liveUpdate: true,
    onUpdate: () => null,
    _onUpdateValue: () => null
  };

  constructor() {
    super();
    this.state = {
      value: null
    };
  }

  static getDerivedStateFromProps(nextProps, prevState) {
    const nextValue = result(nextProps.data, nextProps.path);

    if (prevState.value === nextValue) return null;

    return {
      value: nextValue
    };
  }

  handleChange = event => {
    const { value } = event.target;
    const { liveUpdate } = this.props;

    if (liveUpdate) this.update(value);
  };

  handleFocus = () => {
    document.addEventListener('keydown', this.handleKeyDown);
  };

  handleBlur = () => {
    document.removeEventListener('keydown', this.handleKeyDown);
    window.getSelection().removeAllRanges();

    const { liveUpdate } = this.props;
    if (!liveUpdate) this.update();
  };

  handleKeyDown = event => {
    const key = event.keyCode || event.which;
    const { liveUpdate } = this.props;

    if (key === 13 && !liveUpdate) this.update();
  };

  update(value) {
    const { _onUpdateValue, onUpdate, path } = this.props;
    _onUpdateValue(path, value);
    onUpdate(value);
  }

  render() {
    const { path, label, labelWidth } = this.props;
    const labelText = isString(label) ? label : path;

    return (
      <li className="cr string">
        <label>
          <span className="label-text" style={{ width: `${labelWidth}%` }}>
            {labelText}
          </span>
          <input
            type="text"
            value={this.state.value}
            style={{ width: `${100 - labelWidth}%` }}
            onChange={this.handleChange}
            onFocus={this.handleFocus}
            onBlur={this.handleBlur}
          />
        </label>
      </li>
    );
  }
}

fix linting errors and implement getDerivedStateFromProps