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

Sending a Thunk to the store using Typescript

Within my primary store.ts file, the following code is present: const store = createStore( rootReducer, composeWithDevTools(applyMiddleware(thunk)) ); store.dispatch(fetchUser()); Upon initial rendering, an action is dispatched to fetchUser in ord ...

Step-by-step guide for activating a text box when a check box is marked

I'm looking to activate and deactivate two text boxes when a check box is selected. Currently, only one text box is enabled when the check box is checked. How can I modify my code to enable and disable two text boxes based on the check box status? Her ...

When the JSON object is transferred to the node server, it undergoes modifications

With the following code snippet, I have managed to develop a function that generates JSON data and transmits it to server.js (my node server). function deleteEmail(i) { emailObj.splice(i, 1); var general = {}; var table = [] general.table ...

Switch out the name of multiple elements with mootools

Is there a Moo tool that can replace multiple element IDs? I currently have the following code: $$('myelement').each(function(el){ var get_all_labels = el.getElements('label'); var get_label_id = get_all_l ...

The scroll function triggers even upon the initial loading of the page

Trying to solve the challenge of creating a fullscreen slider similar to the one described in this question, I created a jsfiddle (currently on hold) Despite knowing that scrolling too fast causes bugs and that scrolling both ways has the same effect, m ...

Unable to receive any response with AJAX requests

Welcome to my HTML page <html> <head> <title>Using AJAX</title> </head> <script type="text/javascript" src="ajax.js"></script> <body> <form action="searchItems.php" name="searchItem" method="p ...

Executing a POST Request using a Custom Node Module in Express

I'm in the process of developing a web application where the user inputs their username, submits it, and then the application processes the input through a POST request to another server. Based on the response from the external server, the user is red ...

utilize the useRef hook to display the total number of characters within a text area

Introducing the following component: import React from 'react'; export interface TexareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> { maxLength?: number; id: string; } export const Textarea = React.forwardRef( ( ...

Personalize a tactile message such as View additional details

I'm currently developing a React Native app and I want to implement a "See more" button for additional information. Can someone help me figure out how to achieve this? Similar to this example ...

Ways to retrieve text from a pdf document using JavaScript?

Is there a way to extract text from a PDF using JavaScript? I've looked into npm modules like PDF-TO-TEXT, but they require a file path name as input. I'm currently using the react-drop-to-upload module to allow users to drop PDF files onto a rea ...

sending a selection of JSON string values to a jQuery function

I have a JSON string that contains different items, but I am only interested in the 'locked' array and would like to pass it to a jQuery function. The JSON string was generated using json_encode from PHP. {"ignored":[],"status":{"message":"succe ...

Trouble with CSS transitions not functioning while altering React state

Each time a user clicks on an accordion, the content should smoothly show or hide with a transition effect. However, the transition only seems to work when opening a closed accordion, not when closing an already open one! To get a clearer idea of this iss ...

Converting a Perl hash into a JavaScript hash: A comprehensive guide

I am currently using the template toolkit framework and working with a Perl hash data type in my tt file. My goal is to convert this Perl hash data type into a JavaScript hash data type. Code: template: [% PERL %] use JSON qw(encode_json) ...

Implement a loader in AngularJS to display when transitioning between pages

Is there a way to implement a loader that appears when the page starts changing and only disappears once the entire page is fully rendered to prevent clipping bugs? I have already set up the loader as shown below: $scope.$on('$routeChangeStart' ...

Create a Referral Program page within a swapping interface similar to the functionalities found in platforms like PancakeSwap, PantherSwap, and

Currently, my goal is to create a referral program page similar to the one found at . I have explored the source code on GitHub for the PantherSwap interface, but unfortunately, I did not come across any references to that specific section. Would you be ...

While Advanced REST Client successfully retrieves an infinite JSON file from the AngularJS REST service, Postman encounters an error with the code ERR_INCOMPLETE_CHUNKED_ENCODING

I've encountered a peculiar issue. When making a REST call in Angular (using an app built with Ionic v1) to a Java endpoint, something goes awry and Chrome throws the following error: https://i.sstatic.net/1sxp7.png The code of interest is this Angul ...

The value specified as type '{ value: BigNumber; }' cannot be assigned to the parameter type 'Overrides & { from?: string | Promise<string> | undefined; }'

I am currently working on a smart contract using Solidity (version 0.8.0) at my buildspace project. Below is a snippet of my code written in TypeScript (4.5.x)/JavaScript and Node.js 16.13.x: ... const waveContractFactory = await hre.ethers.getContractFact ...

What is the best way to verify an array of objects within an asynchronous function?

I am struggling with validating an array of objects being sent to the server. It seems that my current code is not working properly when the array is empty or if the objects within it are invalid. I'm confused about why it's not functioning as ex ...

The code is running just fine when tested locally, but it seems to encounter an issue when accessed remotely, yielding

Currently, I am in the process of developing a dual twin setup using a Raspberry Pi. The goal is to simulate a continuous transmission of body temperature data, which is then sent to a server that stores the information in a MongoDB database. Everything fu ...

There seems to be an issue with React-hook-form and material-ui not retaining value changes in the onBlur() method

Stepping into the realm of react-hook-form is a new experience for me. I'm putting in effort to grasp everything, but there are still some pieces missing from the puzzle. Seeking assistance akin to Obiwan Kenobi! The Dilemma at Hand: An <Textfiel ...