Enhance your text in TextInput by incorporating newline characters with advanced editing features

I'm encountering an issue with my Textarea component that handles Markdown headers:

type TextareaProps = {
    initValue: string;
    style?: StyleProp<TextStyle>;
    onChange?: (value: string) => void;
};

type OnChangeFun = NativeSyntheticEvent<TextInputChangeEventData>;

const Textarea = ({initValue, style, onChange = () => {}}: TextareaProps) => {
  const [value, setValue] = useState<string>(initValue);

  const changeHandler = ({nativeEvent: {text}}: OnChangeFun) => {
    setValue(text);
    onChange(text);
  };
  return (
    <TextInput
      style={[styles.textarea, style]}
      multiline
      onChange={changeHandler}>
      <Text>
        {value.split('\n').map((line, index) => {
          const style = line.match(/^#/) && styles.header;
          return (
              <Fragment key={`${index}-${line}`}>
                <Text style={style} >{ line }</Text>
                {"\n"}
              </Fragment>
          );
        })}
      </Text>
    </TextInput>
  );
};

The challenge is that whenever I type a character, the cursor moves two characters. And if it's the last character in a line, it jumps to the next line.

In an attempt to address this, I've implemented controlled selection:

const Textarea = ({initValue, style, onChange = () => {}}: TextareaProps) => {
  const [value, setValue] = useState<string>(initValue);
  const [selection, setSelection] = useState<Selection>({
    start: 0,
    end: 0
  });
  useEffect(() => {
    setSelection(({start, end}) => {
      if (start === end) {
        start += 1;
        end = start;
      }
      return { start, end };
    });
  }, [value]);
  const changeHandler = ({nativeEvent: {text}}: OnChangeFun) => {
    setValue(text);
    onChange(text);
  };
  const onSelection = ({ nativeEvent: { selection }}: OnSelectionFun) => {
    setSelection(selection);
  };
  return (
    <TextInput
      selection={selection}
      style={[styles.textarea, style]}
      multiline
      onSelectionChange={onSelection}
      onChange={changeHandler}>
      <Text>
        {value.split('\n').map((line, index) => {
          const style = line.match(/^#/) && styles.header;
          return (
            <Fragment key={`${index}-${line}`}>
              <Text style={style} >{ line }</Text>
              <Text>{"\n"}</Text>
            </Fragment>
          );
        })}
      </Text>
    </TextInput>
  );
};

However, this results in the entire content disappearing when typing or clicking inside the input field.

Is there a way to insert a new line after each line in the Rich Text editor and maintain the correct cursor position?

Unfortunately, I am unable to create a Snack with the code as it breaks completely in Snack, displaying [object Object] as the output of the Textarea.

Answer №1

The issue in your original script arises from the inclusion of an additional \n following the final line. Imagine this: assume you have a variable named value containing 3 lines. Once it is displayed within a TextInput, the succeeding value will now span across 4 lines:

line1\n
line2\n
line3\n
(empty line 4)

To resolve this matter, simply substitute {"\n"} with conditional rendering:

const lines = value.split('\n');
// ...
return (
  <Fragment key={`${index}-${line}`}>
    <Text style={textStyle}>{line}</Text>
    {lines.length === index + 1 ? null : '\n'}
  </Fragment>
);

Furthermore, please take note that the cursor moves by an extra character due to React Native monitoring the cursor's position relative to the input value's end. Since an extra character was introduced at the value's conclusion, the cursor appeared to be anchored N characters away from the end, consequently causing it to shift one character further right upon every update.

Answer №2

After exploring various queries on: How can I insert a line break into a component in React Native? and stumbling upon an insightful response by Charlie Morton, I was inspired to craft the following code:

const Textarea = ({ initValue, style, onChange = () => {} }: TextareaProps) => {
  const [value, setValue] = useState<string>(initValue);
  const changeHandler = ({ nativeEvent: { text } }: OnChangeFun) => {
    setValue(text);
    onChange(text);
  };
  return (
    <TextInput
      selection={selection}
      style={[styles.textarea, style]}
      multiline
      onSelectionChange={onSelection}
      onChange={changeHandler}>
      <Text>
        {value.split(/(\n)/).map((line, index) => {
          const key = `${index}-${line}`;
          if (line === '\n') {
            return <Text key={key}>{ line }</Text>;
          }
          const style = line.match(/^#/) && styles.header;
          return <Text key={key style={style}>{ line }</Text>;
        })}
      </Text>
    </TextInput>
  );
};

Observing that a newline as a separate item in the array yields favorable outcomes for the text input field.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Guide on transferring object between two $states using ui-router

Visit this link for more information Expected Behavior Upon logging in, selecting a Ticker button is expected to trigger the display of matching Tags for that specific Ticker. Actual Results However, upon clicking a Ticker button after logging in, the ...

Utilizing pixel values for Material-UI breakpoints rather than using the default sm, md, lg, xl options

Below is the code snippet that I am using: [theme.breakpoints.only('lg')]: {} The above code works perfectly and shifts at the desired breakpoint. However, when I implement the following: [theme.breakpoints.between('1200', '1021&a ...

Issues with functionality of React/NextJS audio player buttons arise following implementation of a state

I am currently customizing an Audio Player component in a NextJs application using the ReactAudioPlayer package. However, the standard Import Next/Audio and using just <Audio> without props did not yield the expected results. The player functions as ...

Vue.js methods bound as properties on a parent object

There are times when I come across scenarios where it would be convenient to bind methods as an object property rather than a direct Vue method. For instance, instead of: <MyInput :formatter="currencyFormat" :parser="currencyParser& ...

While using axios to make a GET request, I encountered errors when checking for .isSuccess in react

const searchInvoiceList = async ( plantLocation: string, invoiceType: string ) => { let dataList: InvoiceData[] = []; await axios .get(`${linkURL}inv/getControlList/${plantLocation}/${invoiceType}`) .then((response) => { dataLis ...

The data type 'unknown' cannot be assigned to the type 'any[]', 'Iterable<any>', or (Iterable<any> & any[])

I have been working on creating a custom search filter in my Angular project, and it was functioning properly. However, I encountered an error in my Visual Studio Code. In my previous project, everything was working fine until I updated my CLI, which resul ...

Discover the method to retrieve HTML content by sending an HTTP POST request in AngularJS, similar to loading content

My attempt to retrieve HTML content using Angular Js Post HTTP method was successful, but only for text contents like P tags. I am now trying to fetch HTML contents that include input types such as text boxes using Angular JS. Interestingly, when I utilize ...

Using AJAX/JQuery along with PHP to instantly send notifications without refreshing the page for a contact form

Website URL: This website was created by following two separate tutorials, one for PHP and the other for AJAX. The main objective was to develop a contact form that validates input fields for errors. If no errors are found, a success message is displayed ...

PHP Header Redirect Not Redirecting Correctly

As a newcomer to PHP, I conducted some research and attempted to implement a solution found on Stack Overflow, but unfortunately, it did not work for me. My goal is to redirect users to another page after a specific code has been executed. Despite removing ...

Using JavaScript's Regex to match sentences, while ensuring any full stops within quotes are ignored

Here is the regular expression: (?:(?:Jr|Master|Mr|Ms|Mrs|Dr|Capt|Col|Sgt|Sr|Prof|Rep|Mt|Mount|St|Etc|Eg)\.\s+|["'“(\[]?)(?:\b(?:(?!(?:\S{1,})[.?!]+["']?\s+["']?[A-Z]).)*)(?:(?:(?:Jr|Master|Mr|M ...

What is the best way to integrate a Ruby object into JavaScript?

I'm attempting to integrate Ruby into the JS.erb file, where I need access to the @user object and its relationships. Instead of converting to a JSON object, I prefer the ERB to precompile on the server-side. My goal is to iterate in the JS file rat ...

What is the best way to retrieve information from an Array within a JSON Object?

I currently have a Json object called FriendJson, which contains an array field named friends. This is the Json Object: [ { "id": 4, "updated": "2023-01-07T22:06:23.929206Z", "created": "2023-01-0 ...

Changing a single variable into an array that holds the variable in JavaScript

Is there a way to change 5 into [5] using JavaScript? I need this functionality for a method that utilizes jQuery's $.inArray. It should be able to handle both scalar variables and arrays, converting scalars into arrays with a single element. ...

Having difficulty interpreting the json data retrieved from the specified url

<html> <body> <script src='http://code.jquery.com/jquery-1.10.2.min.js'></script> <script> $(document).ready(function () { $.ajax({ type: 'GET& ...

The AngularJS ngModel directive encounters issues when used within a ui-bootstrap tabset

Check out the code snippet below to see the issue at hand: <!DOCTYPE html> <html ng-app="plunker"> <head> <title>AngularJS Plunker</title> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/cs ...

WebStorm 6 does not recognize the post method in Node.js Express

I recently started learning about node.js and decided to experiment with the express module in my application. Everything was going well until I attempted to use the app.post method. I am developing my app on WebStorm 6.0.2 and it doesn't seem to reco ...

In Javascript, merge two arrays together in a specific format

Can we transform two arrays into a specific format so I can create my D3 graph? Here are the two arrays I have: date = ["sept,09 2015","sept, 10 2015","sept, 11 2015"] likes = [2,4,5] I need to convert them to this format: [{ date: '...', lik ...

Unable to display image in jqGrid binary format

In our system, we use a standard function to retrieve images stored as binaries in the database, and this function works seamlessly throughout the entire system. However, when implementing jqGrid, I encountered difficulties using the existing structure as ...

Adding a loader to the specific button that has been clicked can be achieved by following these steps:

I am currently in the process of building an e-commerce platform website and I'm looking to implement a feature where users can add products to their cart with just a click of a button. However, before the product is added to the cart, I want to disp ...

Sharing information between pages in React through Router

I am struggling to transfer a single string from one page to another using react router and only functional components. I have created a button that links my pages, but I can't seem to pass the string successfully. Here is an example of the code on th ...