Josu Goñi
Josu Goñi

Reputation: 1274

How do I create a proper text editor in React Native?

I'm creating a note taking app on React Native, and at the moment the text editor is an enhanced TextInput with some extra functionalities like copying, pasting, inserting dates, etc.

The problem is that this is very limited as I can't add line numbers, nor change styles, coloring text, etc. Performance is also a concern for big documents.

I'm experimenting with splitting the text into lines and create one text input per line, but some problems appear: I can't select text across lines, I have to handle individual keystrokes to catch line breaks, the cursor won't move between text inputs, etc.

Another problem is that I can catch soft keyboard events, but no physical keyboard events, as per the onKeyPress documentation.

I wonder whether there is a good solution for this as it seems right now that using TextInputs won't allow me to do what I need.

A good answer would be either a good library, or directions on how to do this by hand, directly using components and catching keyboard events (assuming that this is even possible).

For clarification, I don't want a rich text editor library, I want the tools to build it. I also don't want to use a webview.

Upvotes: 2

Views: 2206

Answers (2)

Josu Goñi
Josu Goñi

Reputation: 1274

This is is the best thing I have found so far. It consists on adding Text components as children to the TextInput. It dates from 2015 and does not fully solve the problem though, but it's a start.

    <TextInput
      ref={this.textInputRef}
      style={styles.input}
      multiline={true}
      scrollEnabled={true}
      onChangeText={this.onChangeText}
    >{
      lines.map((line, index) => {
        return <Text>{line + '\n'}</Text>;
      })
    }</TextInput>

It also confirms that this is not a trivial thing to do in React Native.

Commit in the React Native GitHub repository: Added rich text input support

According to this, images can be added too (but I haven't tested it).

I will edit if I find something else.

Upvotes: 1

PhantomSpooks
PhantomSpooks

Reputation: 3570

I will update later (I have to go to work), so I will post what I have and leave comments on what I was thinking

import React, { 
    useState, useEffect, useRef,
} from 'react';
import { 
    View, StyleSheet, TextInput, Text, useWindowDimensions,
    KeyboardAvoidingView, ScrollView
} from 'react-native';


const TextEditor = ({lineHeight=20}) => {
    const { height, width } = useWindowDimensions()
    // determine max number of TextInputs you can make
    const maxLines = Math.floor(height/lineHeight);
    const [ textLines, setTextLines ] = useState(Array(maxLines).fill(''));
    return ( 
        <KeyboardAvoidingView 
            style={styles.container}
            behavior={"height"}
        >
        <ScrollView style={{height:height,width:'100%'}}>
            {/*Make TextInputs to fill whole screen*/}
            <View style={{justifyContent:'flex-end'}}>
                {textLines.map((text,i)=>{
                    let style = [styles.textInput,{height:lineHeight}]
                    // if first line give it extra space to look like notebook paper
                    if(i ==0)
                        style.push({height:lineHeight*3,paddingTop:lineHeight*2})
                    return (
                    <>
                        <TextInput
                          style={style}
                          onChangeText={newVal=>{
                            textLines[i] = newVal
                            setTextLines(textLines)
                          }}
                          key={"textinput-"+i}
                        />
                    </>
                    )
                })}
            </View>
            </ScrollView>
      </KeyboardAvoidingView>
  )
}

const styles = StyleSheet.create({
    container:{
        flex:1,
        // paddingTop:40
    },
    textInput:{
        padding:0,
        margin:0,
        borderBottomColor:'lightblue',
        borderBottomWidth:1,
        width:'100%',
    }
    
})

export default TextEditor

Here's what I'm thinking:

  • wrap this in a ScrollView and use onEndReached prop to render an additional TextInput if the last TextInput is in focused and has reached maxed character limit
  • store all textinput values in an array

Upvotes: 0

Related Questions