React-native-web: TextInput: autoexpandable

Created on 4 Feb 2018  路  5Comments  路  Source: necolas/react-native-web

In react-native TextInput is always autoexpandable. See https://github.com/facebook/react-native/commit/dabb78b1278d922e18b2a84059460689da12578b

I think simplest way to implement this is to use https://github.com/rpearce/react-expanding-textarea
It's just update textarea height based on el.scrollHeight:

  _handleChange(e) {
    const { onChange } = this.props
    if (onChange) onChange(e)
    this._adjustTextarea(e)
  }


  _adjustTextarea({ target = this.el }) {
    target.style.height = 0
    target.style.height = `${target.scrollHeight}px`
  }
}

Most helpful comment

Here is how you do it:

import React, { useState } from 'react';
import { TextInput } from 'react-native';

export default function TextField(props) {
  const [scrollHeight, setScrollHeight] = useState(null);
  return (
    <TextInput
      style={{ height: scrollHeight }}
      onChange={(e) => setScrollHeight(e.target.scrollHeight)}
      {...props}
    />
  );
}

All 5 comments

@gut4 There is a problem with such an implementation. It will break the window scroll occasionally. It's probably much harder to implement with fake height placeholder etc. I had to switch to contentEditable.

Here is how you do it:

import React, { useState } from 'react';
import { TextInput } from 'react-native';

export default function TextField(props) {
  const [scrollHeight, setScrollHeight] = useState(null);
  return (
    <TextInput
      style={{ height: scrollHeight }}
      onChange={(e) => setScrollHeight(e.target.scrollHeight)}
      {...props}
    />
  );
}

@woodpav This with multiline works great to increase the size of the text input, but it unfortunately doesn't shrink it when you remove text.

Using the solution proposed by @gut4 worked in both direction for me.

import * as React from 'react';
import { TextInput } from 'react-native';

export default function App() {

  const _handleChange = (e) => {
    e.target.style.height = 0
    e.target.style.height = `${e.target.scrollHeight}px`
  };

  return (
    <TextInput
      multiline
      onChange={_handleChange}
    />
  );
}


I'm curious if this approach translates across platforms, i.e. can we rely on scrollHeight to be exposed and updated immediately when e.target.style.height is set to zero? My approach is slightly more involved, measuring a hidden Text element to detect shrinking heights, i.e.:

const [height, setHeight] = useState(props.initialHeight);

return(
    <View>
        <TextInput 
            {...props} 
            style={[props.style, {height: height}]}
            onContentSizeChange={e => setHeight(e.nativeEvent.contentSize.height)}
        />
        <Text 
            onLayout={e => e.nativeEvent.layout.height < height && setHeight(e.nativeEvent.layout.height)} 
            style={[props.style, {position: 'absolute', visibility: 'hidden'}]} 
            pointerEvents={'none'}
        >
            {`${props.value || props.defaultValue || props.placeholder ||聽''}\n`}
        </Text>
    </View>
)

@necolas Are there any plans getting this natively supported by react native web at some point?

Could we use https://www.npmjs.com/package/react-textarea-autosize or a similar approach to get closer to the behaviour of the iOS/Android counterparts of TextInput with multiline enabled?

Was this page helpful?
0 / 5 - 0 ratings