【问题标题】:How to prepending before icon for text area for React component如何在 React 组件的文本区域图标之前添加
【发布时间】:2021-07-08 18:43:41
【问题描述】:

我正在尝试实现TextArea 的 React 版本,每次按 Return/Enter 时都会附加“$”。

每次有人按 Enter 键时,我都很难预先添加符号 # 或 % 或美元。我该怎么办?

这是我的基本尝试,但我有点迷茫:

const MyComponent = () => {
  const [name, setName] = React.useState('');

  return (
    <div>
      <textarea value={name} onChange={(e) => { setName(e.target.value) }} />
    </div>
  );
}

ReactDOM.render(<MyComponent />, document.getElementById('root'));

【问题讨论】:

    标签: reactjs textarea


    【解决方案1】:

    好的,所以我有一点时间,并认为这可能是一次很好的学习经历。所以我给你介绍:MoneyInputList

    import './css/MoneyInputList.css'
    
    import { useState, useEffect } from 'react';
    
    let latestAdded = 'moneyInputList-input-0';
    let lastSize = 0;
    
    const MoneyInputList = () => {
        const [recordList, setRecordList] = useState({data: ['']});
    
    
        const handleFormSubmit = (e) => {
            e.preventDefault();
            if(recordList.data[recordList.data.length-1] !== ''){
                setRecordList({data: [...recordList.data, '']})
            }
        };
    
        useEffect(() => {
            if(lastSize !== recordList.data.length)
                document.getElementById(latestAdded).focus();
    
            lastSize =  recordList.data.length;
        }, [recordList]);
    
        
        return (
            <form autoComplete='off' onSubmit={handleFormSubmit}>
                <div className="main-container">
                    {recordList.data.length > 0 &&
                        recordList.data.map((record, iter) => {
                            latestAdded = "moneyInputList-input-"+iter;
                            return (
                            <div key={"moneyInputList-field-"+iter} className="record-field">
                                <div className="record-sign">$</div>
                                <input className="record-input" id={"moneyInputList-input-"+iter} value={recordList.data[iter]} onChange={(e) => {
    
                                    if(e.target.value === '' && iter !== recordList.data.length-1){
                                        let modifiedData = [];
                                        recordList.data.forEach((e,i) => {
                                            if(i !== iter)
                                                modifiedData.push(e);
                                        });
                                        setRecordList({data: modifiedData});
                                        return;
                                    }
    
                                    const filteredValue = e.target.value.split('').filter(e=>(e.charCodeAt() >= '0'.charCodeAt() && e.charCodeAt() <= '9'.charCodeAt()));
                                    
    
                                    let formattedValue = [];
                                    filteredValue.forEach((elem, i) => {
                                        if((filteredValue.length - i) % 3 === 0 && i !== 0)
                                            formattedValue.push(',');
                                        formattedValue.push(elem);
                                    });
                                    formattedValue = formattedValue.join('');
                                    e.target.value = formattedValue;
    
                                    let myData = recordList.data;
                                    myData[iter] = e.target.value;
                                    setRecordList({data: myData});
                                }} type="text"/>
                            </div>
                        )})
                    }
                </div>
                <input style={{flex: 0, visibility: 'collapse', height: 0, width: 0, padding: 0, margin: 0}} type="submit"/>
            </form>
        )
    }
    
    export default MoneyInputList;
    
    
    

    这个组件应该做你需要它做的事情。它不是最好的代码,但它可以工作。你可以看到它在工作here。当然,您可能仍需要更改一些内容以使其适合您的代码库并可能实现 redux,但总体思路就在这里。您可以通过键入任何数字来使用它,按 Enter 将创建一个新行,删除一行的内容将删除它。

    【讨论】:

    • 这太棒了!!哇!!我实际看到的只有一件事是,每当我们输入某些内容时,请返回上一行,对其进行编辑(工作正常)。但是,要回到最后一行,我不能使用我的键盘。我将不得不手动单击最后一行(这有点烦人)。非常感谢您的帮助@Marko Borković 这就是我喜欢 SO 的原因。
    • @TechnoCorner 为了使用向上/向下键实现换行,您可以设置一个键盘事件,然后根据键简单地关注上方或下方的一个。也不要忘记将线程标记为已回答:)
    • 肯定会的!你真棒!!最后一个问题。由于您使用Input 来跟踪每一行,所以它更容易,但是如果我想复制粘贴一些东西(比如美元的逗号分隔字符串),我该怎么做? TextArea 更容易,但因为input 是不可能的。请帮助@Marko Borković
    • @TechnoCorner 我很确定 react 有一个 onPaste 方法,你可以处理粘贴的文本并相应地格式化。之后,您可以从当前输入中删除粘贴的文本并将格式化的值添加到 recordList.data
    • 如果您可以在代码上编写一些 cmets,我将不胜感激,因为这是非常必要的,而且我很难理解其中的一些逻辑部分。 @Marko Borković(尤其是filteredValue
    【解决方案2】:

    我希望我能正确理解你想要做什么,这是一个超级磨损的版本。您可能需要稍微更改代码以适应您的用例。

    import { useState, useEffect } from "react";
    
    export default function App() {
      const [textValue, setTextValue] = useState("");
      const [displayedTextValue, setDisplayedTextValue] = useState("");
    
      useEffect(() => {
        let splitTextValue = textValue.split("\n");
        splitTextValue = splitTextValue.map((line, iter) => {
          if (iter === splitTextValue.length - 1) return line;
          if (line[0] !== "$") return "$ " + line;
          return line;
        });
        setDisplayedTextValue(splitTextValue.join("\n"));
      }, [textValue]);
    
      return (
        <div>
          <textarea
            value={displayedTextValue}
            onChange={(e) => {
              setTextValue(e.target.value);
            }}
          />
        </div>
      );
    }
    

    【讨论】:

    • 谢谢。现在只有一个问题,当我们按下回车键时,我们正在添加$。是否可以在我们按 Enter 之前添加$?其次,如果我输入数字,是否可以将commas 添加到数字中? (如 30,000)等
    • 是的,这是可能的,您可以通过删除以下行来做到这一点:if (iter === splitTextValue.length - 1) return line; 并编辑您需要检查该行是否为有效数字的数字,然后每 3 个位置添加逗号不包括最后一个位置的字符串。
    • 太棒了。最后一件事,当我输入一些东西时,按回车/回车,回去编辑它,它似乎不喜欢它。我们怎样才能解决这个问题?这太棒了@Marko Borković
    • 我认为 useEffect 每次有变化时都会运行,假设我们编辑了一些不是最后一行的东西,光标一直跳到最后一行:( .. 创作体验很棒,但编辑体验不是。我们怎样才能解决这个问题?
    • 嗯,这是使用 textArea 并仅修改其内容的问题之一,我建议创建一个自定义组件,该组件基本上将$ 作为单独的div 添加到每一行输入旁边。我还建议将每一行分开,以使数据修改更容易。在这一点上,修复所有这些小错误变得非常复杂,只需使用更好的组件“设计”即可轻松解决。
    【解决方案3】:

    这是一个使用键事件的版本,我认为在根据键处理事物时更简洁。

    这是the repro on Stackblitz,这是代码:

    import React from 'react';
    import { render } from 'react-dom';
    
    const App = () => {
      const enterKeyCode = 'Enter';
      const backspaceKeyCode = 'Backspace';
    
      const [val, setVal] = React.useState('$ ');
      const [keyCodeEvent, setKeyCodeEvent] = React.useState();
    
      React.useEffect(() => {
        if (keyCodeEvent) {
          // Handle numpad 'enter' key
          if (keyCodeEvent[0].includes(enterKeyCode)) {
            setVal(`${val}\n$ `);
          } else if (keyCodeEvent[0] === backspaceKeyCode) {
            setVal(`${val.slice(0, -1)}`);
          } else {
            setVal(`${val}${keyCodeEvent[1]}`);
          }
        }
      }, [keyCodeEvent]);
    
      return (
        <div>
          {/* Empty onChange to prevent warning in console */}
          <textarea onKeyDown={e => setKeyCodeEvent([e.code, e.key])} value={val} onChange={() => {}} />
        </div>
      );
    };
    
    render(<App />, document.getElementById('root'));
    

    我在 Marko Borković 的回答中阅读了您的 cmets,因此我处理了退格,但 他是完全正确的,当您说您应该为此构建一个特殊组件时。这将更容易改进和清洁。如果您想向您的组件添加功能,您将无法避免其他一些错误。

    【讨论】:

    • 非常感谢您的回复。我确实尝试过你的 stackblitz,但看起来退格编辑问题仍然存在,这使得它对我来说不是很有用:(感谢你的回复。
    • @TechnoCorner 你是什么意思?它对我来说似乎工作正常:我按退格键(删除一个字符),效果很好。您能否详细说明发生了什么,以便我可以尝试修复它?
    • 感谢您的回复。如果输入 100,请按 Enter 并输入 200 并尝试返回并编辑 100。光标跳回最后一行,我什至无法编辑 100。感谢您的帮助@Quentin Grisel
    • @TechnoCorner 好吧,退格键没问题,我想你说的是arrowLeft...实际上处理起来很烦人,因为在修复了这些箭头键的捕获之后,你需要处理由于组件的刷新,退格键(擦除键)将光标设置为输入的最后一个字符。这正是您希望通过创建自己的组件来避免的那种问题(正如您已经在 Marko 的第二个答案中看到的那样)。即使它可能不是最干净的,但它似乎运作良好;)
    猜你喜欢
    • 1970-01-01
    • 2016-04-02
    • 1970-01-01
    • 2021-05-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-23
    • 1970-01-01
    • 2011-12-07
    相关资源
    最近更新 更多