简体   繁体   English

React js textarea onchange 不适用于值

[英]React js textarea onchange doesn't work with value

Here is my class component which is a twitter like auto-suggest text area.这是我的类组件,它是一个类似 twitter 的自动建议文本区域。

import React, { Component, createRef } from 'react';
    import { TextArea, List, Triangle } from './Styled';
    import getCaretCoordinates from 'textarea-caret';
    
    class AutoCompleteTextArea extends Component {
        constructor(props) {
            super(props);
            this.state = {
                value: "",
                caret: {
                    top: 0,
                    left: 0,
                },
                isOpen: false,
                matchType: null,
                match: null,
                list: [],
                selectionEnd: null,
            };
            this.users = [
                { name: 'Bob', id: 'bobrm' },
                { name: 'Andrew', id: 'andrew_s' },
                { name: 'Simon', id: 'simon__a' },
            ];
            this.hashtagPattern = new RegExp(`([#])(?:(?!\\1)[^\\s])*$`);
            this.userPattern = new RegExp(`([@])(?:(?!\\1)[^\\s])*$`);
            this.textareaRef = createRef();
        }
    
        componentDidMount() {
            var caret;
            document.querySelector('textarea').addEventListener('input', (e) => {
                caret = getCaretCoordinates(e.target, e.target.selectionEnd);
                this.setState({ caret });
            });
        }
    
        keyDown = (e) => {
            this.autosize();
            const code = e.keyCode || e.which;
            // Down
            //if (code === 40) down()
            // Up
            //if (code === 38) up()
            // Enter
            //if (code === 13) onSelect()
        };
    
        onChange = (e) => {
            const { selectionEnd, value } = e.target;
            console.log(value);
            this.setState({ value });
            const userMatch = this.userPattern.exec(value.slice(0, selectionEnd));
            const hashtagMatch = this.hashtagPattern.exec(
                value.slice(0, selectionEnd)
            );
            if (hashtagMatch && hashtagMatch[0]) {
                this.setState({
                    matchType: hashtagMatch[1],
                    match: hashtagMatch[0],
                    selectionEnd,
                });
                this.suggest(hashtagMatch[0], hashtagMatch[1]);
            } else if (userMatch && userMatch[0]) {
                this.setState({
                    matchType: userMatch[1],
                    match: userMatch[0],
                    selectionEnd,
                });
                this.suggest(userMatch[0], userMatch[1]);
            } else {
                this.setState({
                    match: null,
                    matchType: null,
                    isOpen: false,
                });
            }
        };
    
        suggest = (match, matchType) => {
            if (matchType === '#') {
                someRequest.then((res) => {
                    this.setState({
                        list: res.body,
                        isOpen: res.body.length !== 0,
                    });
                });
            } else if (matchType === '@') {
                this.setState({
                    list: this.users,
                    isOpen: this.users.length !== 0,
                });
            }
        };
    
        autosize = () => {
            var el = document.getElementsByClassName('autoComplete')[0];
            setTimeout(function() {
                el.style.cssText = 'height:auto; padding:0';
                el.style.cssText = 'height:' + el.scrollHeight + 'px';
            }, 0);
        };
    
        hashtagClickHandler = (hashtag) => {
            const { selectionEnd, match, matchType, value } = this.state;
            const select = matchType + hashtag;
    
            // It's replace value text
            const pre = value.substring(0, selectionEnd - match.length) + select;
            const next = value.substring(selectionEnd);
            const newValue = pre + next;
            console.log(newValue);
            this.setState({ isOpen: false, value: newValue });
            this.textareaRef.current.selectionEnd = pre.length;
        };
    
        render() {
            return (
                <>
                    <TextArea
                        id="postText"
                        name="postText"
                        placeholder="What's on your mind ? ..."
                        maaxLength={255}
                        className="autoComplete"
                        onKeyDown={this.keyDown}
                        onChange={this.onChange}
                        value={this.state.value}
                        ref={this.textareaRef}
                    />
                    {this.state.isOpen && (
                        <List top={this.state.caret.top}>
                            <Triangle left={this.state.caret.left} />
                            {this.state.matchType === '#'
                                ? this.state.list.map((hashtag, index) => (
                                      <button
                                          key={'hashtag' + index}
                                          className="listItem"
                                          onClick={() =>
                                              this.hashtagClickHandler(
                                                  hashtag,
                                                  index
                                              )
                                          }
                                      >
                                          <h5>{`#${hashtag}`}</h5>
                                      </button>
                                  ))
                                : this.state.list.map((user, index) => (
                                      <button
                                          key={'user' + index}
                                          className="listItem"
                                      >
                                          <h5>{user.name}</h5>
                                          <p>{user.id}</p>
                                      </button>
                                  ))}
                        </List>
                    )}
                </>
            );
        }
    }
    
    export default AutoCompleteTextArea;

When I have both value and onChange props on my TextArea it doesn't fire the onChange function.当我在 TextArea 上同时拥有 value 和 onChange 道具时,它不会触发 onChange 函数。 But if I remove the value prop it will be fired.但是如果我删除 value 道具,它将被解雇。 Actually I need the onChange because I'm going to change the value when the user clicks on one of the hashtags suggested.实际上我需要 onChange 因为当用户单击建议的主题标签之一时我将更改该值。 My TextArea is just a styled component like this:我的 TextArea 只是一个这样的样式组件:

export const TextArea = styled.textarea`
    float: left;
    width: 450px;
    font-size: 16px;
    font-weight: lighter;
    margin: 22px 0 0 20px;
    border: none;
    &::placeholder {
        color: ${(props) => props.theme.textGrayLight};
    }
    font-family: ${(props) => props.theme.mainFont};
    position: relative;
    resize: none;

    @media ${(props) => props.mediaHD} {
        width: 250px;
    }
`;

The problem is, that the inputs cannot have multiple input event listener.问题是,输入不能有多个input事件侦听器。 Since you already have a input listener(the onChange ), why do you not move the caret setting to this:既然您已经有一个输入侦听器( onChange ),为什么不将插入符号设置移动到此:

onChange = (e) => {
    const { selectionEnd, value } = e.target;        
    const caret = getCaretCoordinates(e.target, selectionEnd);
    this.setState({ caret, value  });

and remove the didMount , which overrides the listener:并删除didMount ,它覆盖了侦听器:

componentDidMount() {
            var caret;
            document.querySelector('textarea').addEventListener('input', (e) => {
                caret = getCaretCoordinates(e.target, e.target.selectionEnd);
                this.setState({ caret });
            });
        }

Here is a sandbox .这是一个沙箱

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM