В реакцииJS, как скопировать текст в буфер обмена?

Я использую ReactJS, и когда пользователь щелкает ссылку, я хочу скопировать некоторый текст в буфер обмена.

Я использую Chrome 52, и мне не нужно поддерживать какие-либо другие браузеры.

Я не понимаю, почему этот код не приводит к копированию данных в буфер обмена. (происхождение фрагмента кода происходит из сообщения Reddit).

Я делаю это неправильно? Может ли кто-нибудь предложить, есть ли "правильный" способ реализовать копию в буфер обмена с помощью реакции?

copyToClipboard = (text) => {
  console.log('text', text)
  var textField = document.createElement('textarea')
  textField.innerText = text
  document.body.appendChild(textField)
  textField.select()
  document.execCommand('copy')
  textField.remove()
}

Ответы

Ответ 1

Лично я не вижу необходимости в библиотеке для этого. Глядя на http://caniuse.com/#feat=clipboard, он довольно широко поддерживается в настоящее время, однако вы все равно можете делать такие вещи, как проверка, есть ли функциональность в текущем клиенте, и просто скрыть кнопку копирования, если это не так.

import React from 'react';

class CopyExample extends React.Component {

  constructor(props) {
    super(props);

    this.state = { copySuccess: '' }
  }

  copyToClipboard = (e) => {
    this.textArea.select();
    document.execCommand('copy');
    // This is just personal preference.
    // I prefer to not show the the whole text area selected.
    e.target.focus();
    this.setState({ copySuccess: 'Copied!' });
  };

  render() {
    return (
      <div>
        {
         /* Logical shortcut for only displaying the 
            button if the copy command exists */
         document.queryCommandSupported('copy') &&
          <div>
            <button onClick={this.copyToClipboard}>Copy</button> 
            {this.state.copySuccess}
          </div>
        }
        <form>
          <textarea
            ref={(textarea) => this.textArea = textarea}
            value='Some text to copy'
          />
        </form>
      </div>
    );
  }

}

export default CopyExample;

Обновление: переписано с использованием React Hooks в React 16.7.0-alpha.0

import React, { useRef, useState } from 'react';

export default function CopyExample() {

  const [copySuccess, setCopySuccess] = useState('');
  const textAreaRef = useRef(null);

  function copyToClipboard(e) {
    textAreaRef.current.select();
    document.execCommand('copy');
    // This is just personal preference.
    // I prefer to not show the the whole text area selected.
    e.target.focus();
    setCopySuccess('Copied!');
  };

  return (
    <div>
      {
       /* Logical shortcut for only displaying the 
          button if the copy command exists */
       document.queryCommandSupported('copy') &&
        <div>
          <button onClick={copyToClipboard}>Copy</button> 
          {copySuccess}
        </div>
      }
      <form>
        <textarea
          ref={textAreaRef}
          value='Some text to copy'
        />
      </form>
    </div>
  );
}

Ответ 2

Вы должны обязательно рассмотреть возможность использования пакета, такого как @Shubham выше, советует, но я создал рабочий код на основе того, что вы описали: http://codepen.io/dtschust/pen/WGwdVN?editors=1111. Он работает в моем браузере в хроме, возможно, вы можете увидеть, есть ли там что-то, что я там пропустил, или если в вашем приложении есть какая-то сложная сложность, которая мешает этому работать.

// html
<html>
  <body>
    <div id="container">

    </div>
  </body>
</html>


// js
const Hello = React.createClass({
  copyToClipboard: () => {
    var textField = document.createElement('textarea')
    textField.innerText = 'foo bar baz'
    document.body.appendChild(textField)
    textField.select()
    document.execCommand('copy')
    textField.remove()
  },
  render: function () {
    return (
      <h1 onClick={this.copyToClipboard}>Click to copy some text</h1>
    )
  }
})

ReactDOM.render(
<Hello/>,
  document.getElementById('container'))

Ответ 3

Самый простой способ - использовать пакет react-copy-to-clipboard npm.

Вы можете установить его с помощью следующей команды

npm install --save react react-copy-to-clipboard

Используйте его следующим образом.

const App = React.createClass({
  getInitialState() {
    return {value: '', copied: false};
  },


  onChange({target: {value}}) {
    this.setState({value, copied: false});
  },


  onCopy() {
    this.setState({copied: true});
  },


  render() {
    return (
      <div>

          <input value={this.state.value} size={10} onChange={this.onChange} />

        <CopyToClipboard text={this.state.value} onCopy={this.onCopy}>
          <button>Copy</button>
        </CopyToClipboard>

                <div>
        {this.state.copied ? <span >Copied.</span> : null}
                </div>
        <br />

        <input type="text" />

      </div>
    );
  }
});

ReactDOM.render(<App />, document.getElementById('container'));

Подробное объяснение предоставляется по следующей ссылке

https://www.npmjs.com/package/react-copy-to-clipboard

Вот бег fiddle.

Ответ 4

Используйте эту простую встроенную функцию onClick для кнопки, если вы хотите программно записывать данные в буфер обмена.

onClick={() => {navigator.clipboard.writeText(this.state.textToCopy)}}

Ответ 5

Почему бы не использовать только метод коллекции событий clipboardData e.clipboardData.setData(type, content)?

На мой взгляд, это самый простой метод для достижения чего-то внутри буфера обмена, проверьте это (я использовал это для изменения данных во время собственного действия копирования):

...

handleCopy = (e) => {
    e.preventDefault();
    e.clipboardData.setData('text/plain', 'Hello, world!');
}

render = () =>
    <Component
        onCopy={this.handleCopy}
    />

Я пошел по этому пути: https://developer.mozilla.org/en-US/docs/Web/Events/copy

Ура!

РЕДАКТИРОВАТЬ: для целей тестирования, я добавил codepen: https://codepen.io/dprzygodzki/pen/ZaJMKb

Ответ 6

Ваш код должен работать отлично, я использую его так же. Только убедитесь, что если событие щелчка вызывается из всплывающего экрана, такого как загрузочный мод или что-то еще, созданный элемент должен находиться внутри этого модального режима, иначе он не будет копироваться. Вы всегда можете указать идентификатор элемента в этом модальном режиме (в качестве второго параметра) и получить его с помощью getElementById, а затем добавить вновь созданный элемент к этому, а не к документу. Что-то вроде этого:

copyToClipboard = (text, elementId) => {
  const textField = document.createElement('textarea');
  textField.innerText = text;
  const parentElement = document.getElementById(elementId);
  parentElement.appendChild(textField);
  textField.select();
  document.execCommand('copy');
  parentElement.removeChild(textField);
}

Ответ 7

Я использовал очень похожий подход, как некоторые из вышеперечисленных, но, по-моему, он стал немного более конкретным. Здесь родительский компонент передаст URL (или любой другой текст, который вы хотите) в качестве реквизита.

import * as React from 'react'

export const CopyButton = ({ url }: any) => {
  const copyToClipboard = () => {
    const textField = document.createElement('textarea');
    textField.innerText = url;
    document.body.appendChild(textField);
    textField.select();
    document.execCommand('copy');
    textField.remove();
  };

  return (
    <button onClick={copyToClipboard}>
      Copy
    </button>
  );
};

Ответ 8

Для тех людей, которые пытаются выбрать из DIV вместо текстового поля, вот код. Код не требует пояснений, но прокомментируйте здесь, если вам нужна дополнительная информация:

     import React from 'react';
     ....

    //set ref to your div
          setRef = (ref) => {
            // debugger; //eslint-disable-line
            this.dialogRef = ref;
          };

          createMarkeup = content => ({
            __html: content,
          });

    //following function select and copy data to the clipboard from the selected Div. 
   //Please note that it is only tested in chrome but compatibility for other browsers can be easily done

          copyDataToClipboard = () => {
            try {
              const range = document.createRange();
              const selection = window.getSelection();
              range.selectNodeContents(this.dialogRef);
              selection.removeAllRanges();
              selection.addRange(range);
              document.execCommand('copy');
              this.showNotification('Macro copied successfully.', 'info');
              this.props.closeMacroWindow();
            } catch (err) {
              // console.log(err); //eslint-disable-line
              //alert('Macro copy failed.');
            }
          };

              render() {
                    return (
                        <div
                          id="macroDiv"
                          ref={(el) => {
                            this.dialogRef = el;
                          }}
                          // className={classes.paper}
                          dangerouslySetInnerHTML={this.createMarkeup(this.props.content)}
                        />
                    );
            }

Ответ 9

import React, { Component } from 'react';

export default class CopyTextOnClick extends Component {
    copyText = () => {
        this.refs.input.select();

        document.execCommand('copy');

        return false;
    }

    render () {
        const { text } = this.state;

        return (
            <button onClick={ this.copyText }>
                { text }

                <input
                    ref="input"
                    type="text"
                    defaultValue={ text }
                    style={{ position: 'fixed', top: '-1000px' }} />
            </button>
        )
    }
}

Ответ 10

вот мой код:

import React from 'react'

class CopyToClipboard extends React.Component {

  textArea: any

  copyClipBoard = () => {
    this.textArea.select()
    document.execCommand('copy')
  }

  render() {
    return (
      <>
        <input style={{display: 'none'}} value="TEXT TO COPY!!" type="text" ref={(textarea) => this.textArea = textarea}  />
        <div onClick={this.copyClipBoard}>
        CLICK
        </div>
      </>

    )
  }
}

export default CopyToClipboard