7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Reactで行数可変なtextareaを作った話

Last updated at Posted at 2020-01-21

#TL;DR
タイトルなし.gif

中身に応じて高さが変わるtextareaが出来た
コピペ、複数行まとめて削除しても大丈夫

軽く検索しても出てこなかったので
似たようなことをしたくなった人や未来の自分のために書いてみました

#環境
react:15.6.2
redux-form:6.8.0
lodash.isempty:4.4.0

正直reactだけでもOKなはず
廃止されるライフサイクルメソッドなども使っていないので最新版のreactでも動くと思います

他2つは細かいところ書くのが面倒なので使ってます
無くても本質には関係ないので大丈夫です

chromeとIEで動作確認済み(バージョンはわからないですが2020/01付近です)

#参考
textareaの大きさを自動調整

#実装
細かいところはフィーリングで読んでください

index.js
import React from 'react';
import { Field, reduxForm } from 'redux-form';
import TextArea from './textArea';

function index() {
  return (
    <div>
      <Field name="TextArea" component={TextArea} rows={2} />
    </div>
  )
}

export default reduxForm({ form: 'form' })(index);
textArea.js
import React, { Component } from 'react';
import isEmpty from 'lodash.isempty';

class TextArea extends Component {
  constructor(props) {
    super(props);
    this.textArea = null;
  }

  shouldComponentUpdate(nextProps) {
    if (this.props.input.value !== nextProps.input.value) {
      if (isEmpty(nextProps.input.value)) {
        this.textArea.setAttribute('rows', this.props.rows);
      } else {
        this.textArea.setAttribute('rows', this.props.rows);
        while (this.textArea.scrollHeight > this.textArea.offsetHeight) {
          const tempRows = Number(this.textArea.getAttribute('rows'));
          this.textArea.setAttribute('rows', tempRows + 1);
        }
      }
    }
    return true;
  }

  render() {
    return (
      <textarea
        ref={c => {
          this.textArea = c;
        }}
        style={{ resize: 'none' }}
        name={this.props.input.name}
        onChange={e => this.props.input.onChange(e.target.value, this.props.input.name)}
        rows={this.props.rows}
      />
    );
  }
}

#解説
参考にしたコードと似たようなことをしています
以下の部分が全てです

  shouldComponentUpdate(nextProps) {
    // textareaの内容が変更された時のみ操作
    if (this.props.input.value !== nextProps.input.value) {
      if (isEmpty(nextProps.input.value)) {
        // 内容が空なら初期値の行数に戻すだけ
        this.textArea.setAttribute('rows', this.props.rows);
      } else {
        this.textArea.setAttribute('rows', this.props.rows);
        // 内容が有るならスクロールせずに表示できる高さになるまで初期値から1行ずつ増やす
        while (this.textArea.scrollHeight > this.textArea.offsetHeight) {
          const tempRows = Number(this.textArea.getAttribute('rows'));
          this.textArea.setAttribute('rows', tempRows + 1);
        }
      }
    }
    return true;
  }

#注意点
高さにmax-heightなどで上限を定めたい時は
直接textareaにmax-heightを書かないでください

高さの計算とぶつかるのか無限ループになってブラウザが固まります
高さを決めたい場合は適当にラップしてそちらに色々設定してください

※今回で言うと、index.jsのdivに{max-height: 100px, overflow-y: scroll}みたいにすれば
100pxより入力欄が高くなったらスクロールになるよう切り替えられます

#まとめ
shouldComponentUpdateでこういうことして良いのかはわかりませんがとりあえず動いてるのでヨシとします
横方向(cols)にも同じ方法で拡縮できるのではないかと思いますがそこまでは試してないです

7
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?