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

React中textarea换行提交后数组未识别换行问题求助

问题描述

我有一个<textarea/>字段,按下Enter键会提交并在数组中创建新条目,按下Shift + Enter则在<textarea/>输入框内换行。但实际按下Shift+Enter换行后提交,数组并未识别到换行,渲染时换行消失。

输入时的截图:
输入时的截图

条目添加后的截图:
条目添加后的截图

可以看到,数组似乎无法识别输入中的换行。

Todobox.jsx

import React, { createContext } from 'react';
import Item from './Item';
import { useState, useContext } from 'react';
import '../App.css';
import trash from '../trash_can.png'
import { ElementContext } from '../ElementContext';

export const ItemContext = createContext();

export const ItemContextProvider = ({ children }) => {
    const [items, setItems] = useState([]);
    const [itemId, setItemId] = useState(1);
    const [itemData, setItemData] = useState(); 
    const [refDict, setRefDict] = useState({});

    const newItemId = (items) =>{
        setItemId(itemId + 1);
        console.log(itemId)
    }

    const newItem = (itemChange, boxid) => {
        newItemId();
        if (!refDict[itemId]) {
          setItems(prev => [...prev, { itemboxid: boxid, itemdata: itemChange, itemid: itemId }]);
          setRefDict((prev) => ({...prev, [itemId]: true}));
        }
        console.log(items);
    };

    const value = {
        items,
        setItems,
        newItem,
        itemId
    };

    return(
        <ItemContext.Provider value={value}>
            {children}
        </ItemContext.Provider>
    )
};

export default function Todobox({ boxtitle, boxid }){
    const { elements, setElements, newTitle } = useContext(ElementContext);
    const { items, setItems, newItem } = useContext(ItemContext);
    const [boxheader, setBoxHeader] = useState('');
    const [itemChange, setItemChange] = useState('');

    const handleChange = (e) => {
        setBoxHeader(e.target.value);
    }

    const handleKeydown = (e) => {
        if(e.keyCode == 13 && e.shiftKey == false){
            setElements(elements.map(element => {
                if (element.boxid === boxid) {
                  setBoxHeader(e.target.value)
                  return { ...element, boxtitle: boxheader };
                } else {
                  return element;
                }
              }))
            e.preventDefault();
            alert('Title has been set to: ' + boxheader);
        }
    }

    const handleDelete = (e) => {
        setElements(elements.filter(element => element.boxid !== boxid))
    }

    const handleItemChange = (e) =>{
        setItemChange(e.target.value);
    }

    const handleNewItem = (e) =>{
        if(e.keyCode == 13 && e.shiftKey == false){
            newItem(itemChange, boxid)
            e.preventDefault();
            e.target.value = '';
        }
    }
    
    return(
        <>
        <div className='element-box'>
            <img src={trash} className='element-box-trash' onClick={handleDelete}></img>
            <textarea className='element-title-input' placeholder='Add title...' onChange={handleChange} onKeyDown={handleKeydown} value={boxheader}></textarea>
            {items.map(item => {
                if(item.itemboxid === boxid){
                    return <Item key={item.itemid} itemid={item.itemid} itemdata={item.itemdata}/>;
                } else if(item.itemboxid !== boxid){
                    return null;
                }
            })}
            <textarea 
            className='element-input' 
            type='text' 
            placeholder={`Add item... ${boxid}`}
            onChange={handleItemChange}
            onKeyDown={handleNewItem}
            onClick={() => {console.log(boxid)}}
            />
        </div>
        </>
    )
}

Item.jsx

import React, { useContext } from 'react';
import '../App.css';
import { ElementContext } from '../ElementContext';
import { ItemContext } from './Todobox';

export default function Item({ itemid, itemdata }){
    const { setHideModal, modals, setModals } = useContext(ElementContext);

    const handleNewModal = () => {
        setHideModal(false)
        setModals(prev => [...prev, { modalItemId: itemid, modalId: '1', modalData: itemdata }]);
        console.log(modals);
    };

    return(
        <div className='item-container' onClick={handleNewModal}>
            <a className='item-text'>{itemdata}</a>
        </div>
    )
}

相关CSS

.item-text {
  padding: 2px;
  opacity: 1;
  word-wrap: break-word;
}
解决方法

问题出在渲染阶段:textarea中的换行符是\n,但HTML默认不会将\n解析为换行,只会当作空格处理。有两种修复方式:

方式一:CSS保留换行

修改.item-text的CSS,添加white-space: pre-wrap;,让浏览器保留换行符并自动换行:

.item-text {
  padding: 2px;
  opacity: 1;
  word-wrap: break-word;
  white-space: pre-wrap; /* 新增该行 */
}

方式二:将换行符转为<br/>标签

在Item组件中,把itemdata里的\n替换成<br/>标签:

export default function Item({ itemid, itemdata }){
    const { setHideModal, modals, setModals } = useContext(ElementContext);

    const handleNewModal = () => {
        setHideModal(false)
        setModals(prev => [...prev, { modalItemId: itemid, modalId: '1', modalData: itemdata }]);
        console.log(modals);
    };

    // 转换换行符为br标签
    const formattedText = itemdata.split('\n').map((line, index) => (
        <React.Fragment key={index}>
            {line}
            {index !== itemdata.split('\n').length - 1 && <br />}
        </React.Fragment>
    ));

    return(
        <div className='item-container' onClick={handleNewModal}>
            <a className='item-text'>{formattedText}</a>
        </div>
    )
}

两种方式都能解决换行消失的问题,推荐用CSS方案,更简洁且符合样式与逻辑分离的原则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 08:55:31