Issue
I have a 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 problem is that when I enter a character the cursor jumps two characters. If it's the last character in the line it jumps to the next line.
I've tried to add 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>
);
};
But this makes the whole content disappear when I type something or click inside the input.
Is there a way to add a new line after each line in the Rich Text editor and have the cursor in the right position?
Unfortunately, I can't create Snack with the code because this is totally broken in Snack, the output of the Textarea is [object Object]
.
Solution
The problem in your original code is caused by adding extra \n
after the last line. Think about it: say you have value
with 3 lines. When you render it inside TextInput
, your next value will have 4 lines:
line1\n
line2\n
line3\n
(empty line 4)
If you replace {"\n"}
with conditional render, the problem is solved:
const lines = value.split('\n');
// ...
return (
<Fragment key={`${index}-${line}`}>
<Text style={textStyle}>{line}</Text>
{lines.length === index + 1 ? null : '\n'}
</Fragment>
);
P. S. The cursor is moving by 1 extra character because apparently React Native
tracks cursor position relative to the end of the input value; since you added 1 extra character to the end of the value, cursor remained fixed to N
characters from the end, which caused it to visually jump by 1 extra character to the right with every update.
Answered By - Maksym Shcherban
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.