You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native富文本TextInput换行及光标异常问题求助

解决React Native富文本Textarea的光标跳转与内容消失问题

问题根源分析

  1. 光标跳转问题:原代码在TextInput内部手动为每行添加额外的\n,导致渲染内容的长度比实际value多了N个换行符(N为行数),TextInput的光标位置计算基于实际value长度,而渲染内容更长,因此出现光标跳两个字符、行末输入跳行的问题。
  2. 内容消失问题:第二个版本未给TextInput绑定value={value},导致受控状态失效,组件内部状态与useState的value脱节;同时useEffect中强制修改选区的逻辑错误,进一步破坏了光标与内容的同步。

修正后的完整代码

import React, { useState } from 'react';
import { TextInput, TextStyle, StyleProp, NativeSyntheticEvent, TextInputChangeEventData, Selection, Text } from 'react-native';

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

type OnChangeFun = NativeSyntheticEvent<TextInputChangeEventData>;
type OnSelectionFun = NativeSyntheticEvent<{ selection: Selection }>;

const Textarea = ({ initValue, style, onChange = () => {} }: TextareaProps) => {
  const [value, setValue] = useState<string>(initValue);
  const [selection, setSelection] = useState<Selection>({ start: 0, end: 0 });

  const changeHandler = ({ nativeEvent: { text } }: OnChangeFun) => {
    setValue(text);
    onChange(text);
  };

  const onSelectionChange = ({ nativeEvent: { selection } }: OnSelectionFun) => {
    setSelection(selection);
  };

  // 自定义渲染函数,处理Markdown标题样式
  const renderText = (text: string) => {
    return text.split('\n').map((line, index, array) => {
      // 匹配标准Markdown标题格式(# 标题、## 标题等)
      const isHeader = line.match(/^#{1,6}\s/);
      const lineStyle = isHeader ? styles.header : undefined;
      
      // 仅非最后一行添加换行符,保证渲染内容与实际value长度一致
      return (
        <React.Fragment key={`line-${index}`}>
          <Text style={lineStyle}>{line}</Text>
          {index !== array.length - 1 && '\n'}
        </React.Fragment>
      );
    });
  };

  return (
    <TextInput
      value={value} // 核心:绑定value确保受控状态
      selection={selection}
      style={[styles.textarea, style]}
      multiline
      onChange={changeHandler}
      onSelectionChange={onSelectionChange}
      renderText={renderText} // 使用官方API自定义文本渲染
    />
  );
};

// 示例样式
const styles = {
  textarea: {
    height: 200,
    borderWidth: 1,
    borderColor: '#ccc',
    padding: 8,
  },
  header: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#2c3e50',
  },
};

export default Textarea;

关键修复点

  • 绑定受控value:value={value}确保TextInput的内容与状态完全同步,彻底解决内容消失问题。
  • 使用renderText属性:React Native官方提供的自定义文本渲染API,避免直接在TextInput内部嵌套Text组件导致的光标计算错误,同时支持行内样式定制。
  • 精准控制换行符:仅对非最后一行添加换行符,保证渲染内容的长度与实际value完全一致,光标位置计算恢复正常。
  • 移除错误选区逻辑:删除useEffect中强制修改选区的代码,仅通过onSelectionChange记录用户的光标操作,保持光标行为自然。

Snack中[object Object]问题解决

原代码直接在TextInput内部渲染Fragment数组,部分环境会将其转为字符串[object Object],使用renderText属性后,TextInput会正确处理返回的React元素,解决该显示问题。

内容的提问来源于stack exchange,提问作者jcubic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 16:00:42