Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

With React, how to bind to the user's clipboard paste event and parse data?

I need to build a email verification for which requires a code sent to the user's email address. Very similar to the way Slack handle's email verification in their signup flow:

Slack

When you paste, the pasted text is inputed in one input after the other. With react, what is the right way to go about implementing a feature like this?

After componentDidMount, should I be binding and capturing the paste keyPress? Is that the right approach?

like image 320
AnApprentice Avatar asked Dec 11 '17 22:12

AnApprentice


People also ask

How to copy and paste text from React component to clipboard?

The first thing we do is create a React component called YourComponent and set it up to be default export for the file. Then, we create a function name copyCodeToClipboard that will handle our Copy to Clipboard functionality. Inside that function, we hold the <textarea> element in an el variable and select its value with the el.select () method.

How to copy text from one page to another in react?

There is a textbox and button on a page and button click copy the text to the clipboard user enters the text into the textbox, event handler onChange fires, and stores this into react state.

How do I copy text from one page to the clipboard?

Now when we click on the button ‘Copy to Clipboard’, the function copyToClipboard gets triggered through onClick event which copies the state value to the clipboard with copy () function. Now we can copy our text anywhere by just clicking Ctrl+V key.

How to bind handlers in react render ()?

Consider the fact that React uses the Virtual DOM. When render occurs, it will compare the updated Virtual DOM with the previous Virtual DOM, and then only update the changed elements to the actual DOM tree. In our case, when render () is called, this.handleClick.bind (this) will be called as well to bind the handler.


1 Answers

Super simple example to get you started in the right direction. This would need some work before being used. Here's what it does:

  • Allows the user to paste in a code and fills each input with the value
  • Allows the user to type in a code
  • Allows the user to edit a code
  • When the user enters a value, it shifts the focus to the next input
  • Only allows numeric inputs
  • Only allows a single number in each input

There's nothing particularly tricky here. We're using local state for the sake of the example, but this could be moved to another state management implementation like Redux.

The demo uses two components:

  1. <Input /> - to render a controlled input
  2. <App /> to render a container for the <Input />

The <App /> component handles the onPaste event, and passes the appropriate value from the pasted data to each <Input /> component

Each <Input /> component contains a controlled <input/> element, which consists of only a value.

// A functional component to keep it simple
class Input extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: ''
    };
  }
  
  handleKeyDown = (event) => {
    // Handle the delete/backspace key
    if (event.keyCode === 8 || event.keyCode === 46) {
      this.setState({
        value: ''
      });
      
      return;
    }
    
    // Handle the tab key
    if (event.keyCode === 9) {
      return;
    }
    
    // Handle numbers and characters
    const key = String.fromCharCode(event.which);
    if (Number.isInteger(Number(key))) {
      this.setState({
        value: key
      }, () => {
        // Move focus to next input
        this.refs[(this.props.index + 1) % 6].focus()
      });
    }
  }
  
  componentWillReceiveProps = (nextProps) => {
    if (nextProps.value !== this.state.value) {
      this.setState({
        value: nextProps.value
      })
    }
  }

  render() {
    return (
      <div className="inputContainer">
        <input 
          className="input"
          value={this.state.value} 
          onKeyDown={this.handleKeyDown}
          ref={(ref) => this.refs[this.props.index] = ref}
          maxLength="1"
        />
      </div>
    )
  }
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      code: null
    }
  }

  handlePaste = (event) => {
    event.clipboardData.items[0].getAsString(text => {
      const code = text.split("").map((char, index) => {
        if (Number.isInteger(Number(char))) {
          return Number(char);
        }
        
        return "";
      });
      
      this.setState({
        code
      });
    })
  }
  
  render() {
    const code = this.state.code;
  
    return (
      <div className="container" onPaste={this.handlePaste}>
        <Input value={code && code[0]} index={0} />
        <Input value={code && code[1]} index={1} />
        <Input value={code && code[2]} index={2} />
        <div className="spacer">-</div>
        <Input value={code && code[3]} index={3} />
        <Input value={code && code[4]} index={4} />
        <Input value={code && code[5]} index={5} />
      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById("app"));
.container {
  display: flex;
}

.inputContainer {
  flex: 1;
  border: 1px solid #cccccc;
}

.inputContainer:last-child {
  border-top-right-radius: 5px;
  border-bottom-right-radius: 5px;
}

.inputContainer:first-child {
  border-top-left-radius: 5px;
  border-bottom-left-radius: 5px;
}

.spacer {
  flex: 0.3 0;
  text-align: center;
  height: 40px;
  line-height: 40px;
  font-size: 24px;
}

.input {
  width: 100%;
  height: 40px;
  line-height: 40px;
  font-size: 24px;
  text-align: center;
  border: none;
  outline: none;
  border-radius: 5px;
  box-sizing: border-box;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
Test Code: 135791

<div id="app"></div>
like image 193
Brett DeWoody Avatar answered Oct 11 '22 03:10

Brett DeWoody