In reactJS, how to copy text to clipboard? - javascript

I'm using ReactJS and when a user clicks a link I want to copy some text to the clipboard.
I am using Chrome 52 and I do not need to support any other browsers.
I can't see why this code does not result in the data being copied to the clipboard. (the origin of the code snippet is from a Reddit post).
Am I doing this wrong? Can anyone suggest is there a "correct" way to implement copy to clipboard using reactjs?
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()
}

Use this simple inline onClick function on a button if you want to programatically write data to the clipboard.
onClick={() => {navigator.clipboard.writeText(this.state.textToCopy)}}

I personally don't see the need for a library for this. Looking at http://caniuse.com/#feat=clipboard it's pretty widely supported now, however you can still do things like checking to see if the functionality exists in the current client and simply hide the copy button if it doesn't.
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 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;
Update: Rewritten using React Hooks in 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 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>
);
}

You can get this done without the need of an external library, e.g: within a button
<button
onClick={() => navigator.clipboard.writeText('Copy this text to clipboard')}
>
Copy
</button>
for internet explorer 11 and older browsers you might need to change the code a bit here is an example:
<button
onClick={() => window.clipboardData.setData("Text", 'Copy this text to clipboard')}>
Copy
</button>

You should definitely consider using a package like #Shubham above is advising, but I created a working codepen based on what you described: http://codepen.io/dtschust/pen/WGwdVN?editors=1111 . It works in my browser in chrome, perhaps you can see if there's something I did there that you missed, or if there's some extended complexity in your application that prevents this from working.
// 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'))

The simplest way will be use the react-copy-to-clipboard npm package.
You can install it with the following command
npm install --save react react-copy-to-clipboard
Use it in the following manner.
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'));
A detailed explanation is provided at the following link
https://www.npmjs.com/package/react-copy-to-clipboard
Here is a running fiddle.

Best solution with react hooks, no need of external libraries for that
import React, { useState } from 'react';
const MyComponent = () => {
const [copySuccess, setCopySuccess] = useState('');
// your function to copy here
const copyToClipBoard = async copyMe => {
try {
await navigator.clipboard.writeText(copyMe);
setCopySuccess('Copied!');
} catch (err) {
setCopySuccess('Failed to copy!');
}
};
return (
<div>
<Button onClick={() => copyToClipBoard('some text to copy')}>
Click here to copy
</Button>
// after copying see the message here
{copySuccess}
</div>
)
}
check here for further documentation about navigator.clip board , navigator.clipboard documentation
navigotor.clipboard is supported by a huge number of browser look here supported browser

Clipboard is well supported by major browser in 2021. One approach would be to first build a copy to clipboard function and then call it using the onClick event handler.
function copy(text){
navigator.clipboard.writeText(text)
}
To prevent hard coding, let's suppose the string is assigned to a variable named someText
<span onClick={() => copy(someText)}>
{someText}
</span>

You can use event clipboardData collection method e.clipboardData.setData(type, content).
In my opinion is the most straightforward method to achieve pushing something inside the clipboard, check this out (I've used that to modify data while native copying action):
...
handleCopy = (e) => {
e.preventDefault();
e.clipboardData.setData('text/plain', 'Hello, world!');
}
render = () =>
<Component
onCopy={this.handleCopy}
/>
I followed that path: https://developer.mozilla.org/en-US/docs/Web/Events/copy
Cheers!
EDIT: For testing purposes, I've added codepen: https://codepen.io/dprzygodzki/pen/ZaJMKb

I've taken a very similar approach as some of the above, but made it a little more concrete, I think. Here, a parent component will pass the url (or whatever text you want) as a prop.
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>
);
};

Here's another use case, if you would like to copy the current url to your clipboard:
Define a method
const copyToClipboard = e => {
navigator.clipboard.writeText(window.location.toString())
}
Call that method
<button copyToClipboard={shareLink}>
Click to copy current url to clipboard
</button>

Your code should work perfectly, I use it the same way. Only make sure that if the click event is triggered from within a pop up screen like a bootstrap modal or something, the created element has to be within that modal otherwise it won't copy. You could always give the id of an element within that modal (as a second parameter) and retrieve it with getElementById, then append the newly created element to that one instead of the document. Something like this:
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);
}

This work for me:
const handleCopyLink = useCallback(() => {
const textField = document.createElement('textarea')
textField.innerText = url
document.body.appendChild(textField)
if (window.navigator.platform === 'iPhone') {
textField.setSelectionRange(0, 99999)
} else {
textField.select()
}
document.execCommand('copy')
textField.remove()
}, [url])

No need to install third party packages. I try to keep it as simple as possible. This worked well for me.
import React, { useState } from "react"
function MyApp() {
const [copySuccess, setCopySuccess] = useState(null);
const copyToClipBoard = async copyMe => {
try {
await navigator.clipboard.writeText(copyMe);
setCopySuccess('Copied!');
}
catch (err) {
setCopySuccess('Failed to copy!');
}
};
return (
<button onClick={(e) => copyToClipBoard(what you want to copy goes here)} >
My button
</button>
)
}

The plain simple answer will be
navigator.clipboard.writeText("value")

Fully working React component using Material UI
For a better understanding, I've prepared a CodeSandbox as well. Hope that helps.
import { useState } from "react";
import { IconButton, Snackbar } from "#mui/material";
import ShareIcon from "#mui/icons-material/Share";
const CopyToClipboardButton = () => {
const [open, setOpen] = useState(false);
const handleClick = () => {
setOpen(true);
navigator.clipboard.writeText(window.location.toString());
};
return (
<>
<IconButton onClick={handleClick} color="primary">
<ShareIcon />
</IconButton>
<Snackbar
message="Copied to clibboard"
anchorOrigin={{ vertical: "top", horizontal: "center" }}
autoHideDuration={20000}
onClose={() => setOpen(false)}
open={open}
/>
</>
);
};
export default CopyToClipboardButton;
Here's what the button looks like:
And when you click it:
Source: https://fwuensche.medium.com/react-button-to-copy-to-clipboard-75ef5ecdc708

first create BTN then add this onClick:
onClick={() => navigator.clipboard.writeText(textState)}
or
onClick={() => navigator.clipboard.writeText('Your text for copy')}

use this command to pass your value to the function
var promise = navigator.clipboard.writeText(newClipText)

For those people who are trying to select from the DIV instead of the text field, here is the code. The code is self-explanatory but comment here if you want more information:
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)}
/>
);
}

navigator.clipboard doesn't work over http connection according to their document. So you can check if it's coming undefined and use document.execCommand('copy') instead, this solution should cover almost all the browsers
const defaultCopySuccessMessage = 'ID copied!'
const CopyItem = (props) => {
const { copySuccessMessage = defaultCopySuccessMessage, value } = props
const [showCopySuccess, setCopySuccess] = useState(false)
function fallbackToCopy(text) {
if (window.clipboardData && window.clipboardData.setData) {
// IE specific code path to prevent textarea being shown while dialog is visible.
return window.clipboardData.setData('Text', text)
} else if (document.queryCommandSupported && document.queryCommandSupported('copy')) {
const textarea = document.createElement('textarea')
textarea.innerText = text
// const parentElement=document.querySelector(".up-CopyItem-copy-button")
const parentElement = document.getElementById('copy')
if (!parentElement) {
return
}
parentElement.appendChild(textarea)
textarea.style.position = 'fixed' // Prevent scrolling to bottom of page in MS Edge.
textarea.select()
try {
setCopySuccess(true)
document.execCommand('copy') // Security exception may be thrown by some browsers.
} catch (ex) {
console.log('Copy to clipboard failed.', ex)
return false
} finally {
parentElement.removeChild(textarea)
}
}
}
const copyID = () => {
if (!navigator.clipboard) {
fallbackToCopy(value)
return
}
navigator.clipboard.writeText(value)
setCopySuccess(true)
}
return showCopySuccess ? (
<p>{copySuccessMessage}</p>
) : (
<span id="copy">
<button onClick={copyID}>Copy Item </button>
</span>
)
}
And you can just call and reuse the component anywhere you'd like to
const Sample=()=>(
<CopyItem value="item-to-copy"/>
)

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>
)
}
}

If you want to select from the DIV instead of the text field, here is the code. The "code" is the value that has to be copied
import React from 'react'
class CopyToClipboard extends React.Component {
copyToClipboard(code) {
var textField = document.createElement('textarea')
textField.innerText = code
document.body.appendChild(textField)
textField.select()
document.execCommand('copy')
textField.remove()
}
render() {
return (
<div onClick={this.copyToClipboard.bind(this, code)}>
{code}
</div>
)
}
}
export default CopyToClipboard

Found best way to do it. i mean the fastest way: w3school
https://www.w3schools.com/howto/howto_js_copy_clipboard.asp
Inside a react functional component. Create a function named handleCopy:
function handleCopy() {
// get the input Element ID. Save the reference into copyText
var copyText = document.getElementById("mail")
// select() will select all data from this input field filled
copyText.select()
copyText.setSelectionRange(0, 99999)
// execCommand() works just fine except IE 8. as w3schools mention
document.execCommand("copy")
// alert the copied value from text input
alert(`Email copied: ${copyText.value} `)
}
<>
<input
readOnly
type="text"
value="exemple#email.com"
id="mail"
/>
<button onClick={handleCopy}>Copy email</button>
</>
If not using React, w3schools also have one cool way to do this with tooltip included: https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_copy_clipboard2
If using React, a cool think to do: Use a Toastify to alert the message.
https://github.com/fkhadra/react-toastify This is the lib very easy to use.
After installation, you may be able to change this line:
alert(`Email copied: ${copyText.value} `)
For something like:
toast.success(`Email Copied: ${copyText.value} `)
If you want to use it, dont forget to Install toastify. import ToastContainer and also toasts css:
import { ToastContainer, toast } from "react-toastify"
import "react-toastify/dist/ReactToastify.css"
and add the toast container inside return.
import React from "react"
import { ToastContainer, toast } from "react-toastify"
import "react-toastify/dist/ReactToastify.css"
export default function Exemple() {
function handleCopy() {
var copyText = document.getElementById("mail")
copyText.select()
copyText.setSelectionRange(0, 99999)
document.execCommand("copy")
toast.success(`Hi! Now you can: ctrl+v: ${copyText.value} `)
}
return (
<>
<ToastContainer />
<Container>
<span>E-mail</span>
<input
readOnly
type="text"
value="myemail#exemple.com"
id="mail"
/>
<button onClick={handleCopy}>Copy Email</button>
</Container>
</>
)
}

copyclip = (item) => {
var textField = document.createElement('textarea')
textField.innerText = item
document.body.appendChild(textField)
textField.select()
document.execCommand('copy')
this.setState({'copy':"Copied"});
textField.remove()
setTimeout(() => {
this.setState({'copy':""});
}, 1000);
}
<span className="cursor-pointer ml-1" onClick={()=> this.copyclip(passTextFromHere)} >Copy</span> <small>{this.state.copy}</small>

You can also use react hooks into function components or stateless components with this piece of code:
PS: Make sure you install useClippy through npm/yarn with this command:
npm install use-clippy or
yarn add use-clippy
import React from 'react';
import useClippy from 'use-clippy';
export default function YourComponent() {
// clipboard is the contents of the user's clipboard.
// setClipboard('new value') wil set the contents of the user's clipboard.
const [clipboard, setClipboard] = useClippy();
return (
<div>
{/* Button that demonstrates reading the clipboard. */}
<button
onClick={() => {
alert(`Your clipboard contains: ${clipboard}`);
}}
>
Read my clipboard
</button>
{/* Button that demonstrates writing to the clipboard. */}
<button
onClick={() => {
setClipboard(`Random number: ${Math.random()}`);
}}
>
Copy something
</button>
</div>
);
}

const handleCopy = async () => {
let copyText = document.getElementById('input') as HTMLInputElement;
copyText.select();
document.execCommand('copy');
};
return (
<TextField
variant="outlined"
value={copyText}
id="input"
/>
);
This is work for me.

here is my code:
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

<input
value={get(data, "api_key")}
styleName="input-wrap"
title={get(data, "api_key")}
ref={apikeyObjRef}
/>
<div
onClick={() => {
apikeyObjRef.current.select();
if (document.execCommand("copy")) {
document.execCommand("copy");
}
}}
styleName="copy"
>
复制
</div>

you can use useRef to select text inside an element then copy it to clipboard
import React, { useRef } from "react";
const Comp = () => {
const copyTxt = useRef();
const handleCopyTxt = () => {
let txtDiv = copyTxt.current;
if (document.selection) {
// IE
var range = document.body.createTextRange();
range.moveToElementText(txtDiv);
range.select();
} else if (window.getSelection) {
// other browsers
var range = document.createRange();
range.selectNode(txtDiv);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
}
document.execCommand("copy");
}
return ( <div ref={copyTxt} > some text to copied </div> )
}

Inspired by #nate 's answer I have created a withCopyText react hook. And, added navigator.clipboard.writeText support with execCommand fallback.
The hook means that it can be reused across many components without repeating code. See the example component CopyText for implementation.
import React, { useRef, useState } from 'react';
const withCopyText = (textElementRef) => {
if (!textElementRef) throw 'withCopyText: ref is required';
const [copyStatus, setCopyStatus] = useState('');
const [support, setSupport] = useState({
navigatorClipboard: !!navigator.clipboard,
exec: !!document.queryCommandSupported('copy'),
});
const copyToClipboard = (e) => {
if ('' !== copyStatus) {
setCopyStatus('');
await new Promise((resolve) => setTimeout(resolve, 200));
}
// clipboard.writeText has wide but not 100% support
// https://caniuse.com/?search=writeText
if (support.navigatorClipboard) {
try {
navigator.clipboard.writeText(textElementRef.current.value);
return setCopyStatus('success');
} catch (e) {
setSupport({ ...support, navigatorClipboard: false });
}
}
// execCommand has > 97% support but is deprecated, use it as a fallback
// https://caniuse.com/?search=execCommand
// https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand
if (!support.navigatorClipboard) {
try {
textElementRef.current.select();
document.execCommand('copy');
e.target.focus();
setCopyStatus('success');
} catch (e) {
setSupport({ ...support, exec: false });
return setCopyStatus('fail');
}
}
};
return {
copyStatus,
copyToClipboard,
support: Object.values(support).includes(true),
};
};
const CopyText = ({ text }) => {
const textElementRef = useRef(null);
const { copyStatus, copyToClipboard, support } = withCopyText(textElementRef);
return (
<span>
{support && <button onClick={copyToClipboard}>Copy</button>}
{'success' === copyStatus && <span>Copied to clipboard!</span>}
{'fail' === copyStatus && <span>Sorry, copy to clipboard failed</span>}
<input type="text" ref={textElementRef} value={text} readOnly={true} />
</span>
);
};
export { CopyText, withCopyText };

For React Developers
const preventCopyPasteBody = (state) => {
document.addEventListener(state, (evt) => {
if (evt.target.id === 'body') {
evt.preventDefault();
return false;
}
return false;
}, false);
}
preventCopyPasteBody ("contextmenu")
preventCopyPasteBody ("copy")
preventCopyPasteBody ("paste")
preventCopyPasteBody ("cut")
<Typography id="body" variant="body1" component="div" className={classes.text} style={{ fontSize: fontSize }}>{story}</Typography>

Related

Cannot read property of undefined how to work around this in the DOM?

I execute a component and this component fills the value profilePicRef once. However, I only want to display the Upload button when profilePicRef.current.preview is also no longer zero. However, I always get the error message TypeError: Cannot read property 'preview' of undefined. My question is, how can I now say if it is undefined, then don't take it into account and if it is not zero show it.
<PhotoFileHandler ref={profilePicRef} />
{
profilePicRef.current.preview !== null &&
<button className="button is-primary is-outlined" type="butto"
onClick={() => { onClickUpload(profilePicRef);
setActiveModal(''); }}>
<i className="fas fa-file-image"></i> Upload</button>
}
PhotoFileHandler
import React, { useState, forwardRef, useImperativeHandle, } from "react";
function PhotoFileHandler(props, ref) {
const [picName, setPicName] = useState(null);
const [preview, setPreview] = useState(null);
const [isPreview, setIsPreview] = useState(true);
const fileSelectedHandler = (event) => {
....
setPicName(event.target.files[0].name);
setPreview(reader.result);
setIsPreview(true);
}
}
catch (err) {
}
};
useImperativeHandle(ref, () => ({
isPreview,
preview,
picName,
checkProfilPicture() {
if (!preview) {
setIsPreview(false);
return false;
}
else {
setIsPreview(true);
return true;
}
},
getImage() {
return preview
},
removePreview() {
setIsPreview(false)
setPreview(null);
setPicName(null);
}
}),
);
return (
<div>
<input class="file-input" type="file" name="resume" accept=".png,.jpg,.jpeg, .jfif"
onChange={fileSelectedHandler} />
</div>
);
};
// eslint-disable-next-line
PhotoFileHandler = forwardRef(PhotoFileHandler);
export default PhotoFileHandler;
Is important to do with ref?
Alternative 0: Without Ref, Parent State
Alternative with state, you can see how it works here: https://codesandbox.io/s/admiring-gates-ctw6m?file=/src/App.js) :
Include one variable "file" const [selectedFile, setSelectedFile] = React.useState(null)
Send the setter function to PhotoFileHandler, I did something like: <PhotoFileHandler onImage={setSelectedFile} />
In PhotoFileHandler I did:
const fileSelectedHandler = (event) => {
props.onImage(event.target.files[0]);
}
Alternative 1: Force update with Parent State
If you need the ref, one workaround can be the trigger it when change it, like: https://codesandbox.io/s/sleepy-butterfly-iqmw3?file=/src/App.js,
Define in your parent component one state: const [wasUpdated, setWasUpdated] = React.useState("");
Include this in your validation to show the preview and upload button:
profilePicRef.current &&
profilePicRef.current.preview !== null &&
wasUpdated && (
<AllHtml />
)
)
Send the setter to the children <PhotoFileHandler ref={profilePicRef} onChange={setWasUpdated} />
In the children component you can trigger the event after updating preview.
useEffect(() => {
props.onChange(preview);
}, [preview]);

How to pass useRef props to two components to focus an input ( textarea ) - react.js and typescript

I have this component
const Pubs = () => {
const selectArea = useRef<HTMLInputElement | null>(null);
return (
<LikesComments selectArea={selectArea} publication={publication} />
<DisplayComments selectArea={selectArea} publication={publication} />
</PubHero>
);
};
export default Pubs;
And, when i pass the useRef to likesComments, i have this function who will use the useRef as props
interface IlikesCommentsProps {
selectArea: Idontknow
}
const LikesComments: React.FC<IlikesCommentsProps> = ({ selectArea }) => {
const focusComment = () => {
selectArea.current.focus()
}
return (
<div onClick={focusComment}>
<CommentIcon />
<LikeCommentText separation='0.7rem'>Comentar</LikeCommentText>
</div>
)
}
export default LikesComments
And when i click that function in the first comment, i want it to focus an input (textarea) in the second component
interface IcreateComments {
selectArea: Idontknow
}
const CreateComments: React.FC<IcreateComments> = ({ selectArea }) => {
return <textarea ref={selectArea} />
}
export default CreateComments
How can i make this happen?
I really haven't tried before to pass useRef as props, so, i really don't know what to do exactly, i've tried to look for answers, but found nothing at all
Can you help me with this?
Thanks for your time!
There are many ways to do it. I am showing one.
Firstly you can use a single ref for that. Just you have to take help of an menthod. The method will focus to the textarea.
const handleSelectArea = () => {
selectArea.current.focus()
}
Now pass them to the two component that you want
<LikesComments handleSelectArea={handleSelectArea} publication={publication} />
Inside the component onCLick call the method
const focusComment = () => {
handleSelectArea();
};
As you are already passing the ref to the text area so it will focus that.
You have to use the ref as bellow
type TextareaProps = React.HTMLProps<HTMLTextAreaElement>
const CreateComments = React.forwardRef<HTMLTextAreaElement, TextareaProps>((props, ref) => {
return <textarea ref={ref}/>
})
Simple Demo

How to get initial and setState value of contentEditable Input in ReactJS?

I am trying to build a comment box using contentEditable div and have
the following issues:
Not able to get the initial value of the text property.
Not getting the setState value from contentEditable.
Having issues with emoji's which are working fine when using the input element but does not work with contentEditable.
With the code provided below, I am getting undefined when console.log,
not sure what I am missing.
Expected Outcome.
get the typed value from contentEditable including the emoji's.
// Content Input Model
import React, { Fragment } from 'react'
import '../../assets/css/comment.css'
const ContentInput = ({valueChange,contentInputId,ref,onClick,onPhClick,placeholder, ...props }) => {
return (
<Fragment>
<div
spellCheck="false"
role="textbox"
id={contentInputId}
ref={ref}
contentEditable="true"
onChange={valueChange}
aria-multiline="true"
data-placeholder={placeholder}
{...props}
/>
<div className="comment_submit_button" onClick={onClick}>
<span className='submit_arrow_light'> </span>
</div>
</Fragment>
)
};
export default ContentInput
// Comment Modal///
import React , {Component} from 'react';
import EmojiPicker from 'emoji-picker-react'
import '../../../../assets/css/comment.css'
import JSEMOJI from 'emoji-js'
import ContentInput from "../../../../UIElements/Inputs/ContentInput";
//emoji set up
let jsemoji = new JSEMOJI();
// set the style to emojione (default - apple)
jsemoji.img_set = 'emojione';
// set the storage location for all emojis
jsemoji.img_sets.emojione.path = 'https://cdn.jsdelivr.net/emojione/assets/3.0/png/32/';
class CommentModal extends Component {
constructor(props) {
super(props);
this.state = {
text : ' ' ,
emojiShown : false ,
};
this.desChange = this.desChange.bind(this);
}
desChange = (e) => {
// let text = this.state.text;
this.setState({text : e.target.value});
};
comment = (e) => {
e.preventDefault();
let {text} = this.state;
let {back , incrementComments , ...rest} = this.props;
const updatedText = this.setState({text : e.target.value});
console.log(updatedText);
};
//displays emoji inside the input window
handleEmojiClick = (n , e) => {
let emoji = jsemoji.replace_colons(`:${e.name}:`);
this.setState({
text : e.target.value + emoji ,
emojiShown : !this.state.emojiShown
});
console.log(this.emojiShown)
};
toggleEmojiState = () => {
this.setState({
emojiShown : !this.state.emojiShown
});
};
render() {
return (
<div className='comment_model_container display_none'>
<div className="comment_content_container global_bt">
<div className="comment_text_area_container ">
<ContentInput
valueChange={this.desChange}
contentInputId='comment_box'
onClick={this.comment}
spellcheck="true"
className='comment_text_area global_bt'
placeholder='Leave a comment...'
/>
</div>
<div>
</div>
<div className='emoji_container'>
<span id="show-emoji-yes" onClick={!this.toggleEmojiState}><span
className='grey_smiley_icon'> </span></span>
<div className="emoji-table">
<EmojiPicker onEmojiClick={this.handleEmojiClick} className='emoji_popup'/>
</div>
</div>
</div>
</div>
);
}
}
export default CommentModal;
```
Use onInput on ContentInput instead of onChange
And use e.target.innerHTML on desChange instead of e.target.value
contentEditable event should be 'onInput'
Content Editable change events

ReactJs: Prevent multiple times button press

In my React component I have a button meant to send some data over AJAX when clicked. I need to happen only the first time, i.e. to disable the button after its first use.
How I'm trying to do this:
var UploadArea = React.createClass({
getInitialState() {
return {
showUploadButton: true
};
},
disableUploadButton(callback) {
this.setState({ showUploadButton: false }, callback);
},
// This was simpler before I started trying everything I could think of
onClickUploadFile() {
if (!this.state.showUploadButton) {
return;
}
this.disableUploadButton(function() {
$.ajax({
[...]
});
});
},
render() {
var uploadButton;
if (this.state.showUploadButton) {
uploadButton = (
<button onClick={this.onClickUploadFile}>Send</button>
);
}
return (
<div>
{uploadButton}
</div>
);
}
});
What I think happens is the state variable showUploadButton not being updated right away, which the React docs says is expected.
How could I enforce the button to get disabled or go away altogether the instant it's being clicked?
The solution is to check the state immediately upon entry to the handler. React guarantees that setState inside interactive events (such as click) is flushed at browser event boundary. Ref: https://github.com/facebook/react/issues/11171#issuecomment-357945371
// In constructor
this.state = {
disabled : false
};
// Handler for on click
handleClick = (event) => {
if (this.state.disabled) {
return;
}
this.setState({disabled: true});
// Send
}
// In render
<button onClick={this.handleClick} disabled={this.state.disabled} ...>
{this.state.disabled ? 'Sending...' : 'Send'}
<button>
What you could do is make the button disabled after is clicked and leave it in the page (not clickable element).
To achieve this you have to add a ref to the button element
<button ref="btn" onClick={this.onClickUploadFile}>Send</button>
and then on the onClickUploadFile function disable the button
this.refs.btn.setAttribute("disabled", "disabled");
You can then style the disabled button accordingly to give some feedback to the user with
.btn:disabled{ /* styles go here */}
If needed make sure to reenable it with
this.refs.btn.removeAttribute("disabled");
Update: the preferred way of handling refs in React is with a function and not a string.
<button
ref={btn => { this.btn = btn; }}
onClick={this.onClickUploadFile}
>Send</button>
this.btn.setAttribute("disabled", "disabled");
this.btn.removeAttribute("disabled");
Update: Using react hooks
import {useRef} from 'react';
let btnRef = useRef();
const onBtnClick = e => {
if(btnRef.current){
btnRef.current.setAttribute("disabled", "disabled");
}
}
<button ref={btnRef} onClick={onBtnClick}>Send</button>
here is a small example using the code you provided
https://jsfiddle.net/69z2wepo/30824/
Tested as working one: http://codepen.io/zvona/pen/KVbVPQ
class UploadArea extends React.Component {
constructor(props) {
super(props)
this.state = {
isButtonDisabled: false
}
}
uploadFile() {
// first set the isButtonDisabled to true
this.setState({
isButtonDisabled: true
});
// then do your thing
}
render() {
return (
<button
type='submit'
onClick={() => this.uploadFile()}
disabled={this.state.isButtonDisabled}>
Upload
</button>
)
}
}
ReactDOM.render(<UploadArea />, document.body);
You can try using React Hooks to set the Component State.
import React, { useState } from 'react';
const Button = () => {
const [double, setDouble] = useState(false);
return (
<button
disabled={double}
onClick={() => {
// doSomething();
setDouble(true);
}}
/>
);
};
export default Button;
Make sure you are using ^16.7.0-alpha.x version or later of react and react-dom.
Hope this helps you!
If you want, just prevent to submit.
How about using lodash.js debounce
Grouping a sudden burst of events (like keystrokes) into a single one.
https://lodash.com/docs/4.17.11#debounce
<Button accessible={true}
onPress={_.debounce(async () => {
await this.props._selectUserTickets(this.props._accountId)
}, 1000)}
></Button>
If you disable the button during onClick, you basically get this. A clean way of doing this would be:
import React, { useState } from 'react';
import Button from '#material-ui/core/Button';
export default function CalmButton(props) {
const [executing, setExecuting] = useState(false);
const {
disabled,
onClick,
...otherProps
} = props;
const onRealClick = async (event) => {
setExecuting(true);
try {
await onClick();
} finally {
setExecuting(false);
}
};
return (
<Button
onClick={onRealClick}
disabled={executing || disabled}
{...otherProps}
/>
)
}
See it in action here: https://codesandbox.io/s/extended-button-that-disabled-itself-during-onclick-execution-mg6z8
We basically extend the Button component with the extra behaviour of being disabled during onClick execution. Steps to do this:
Create local state to capture if we are executing
Extract properties we tamper with (disabled, onClick)
Extend onClick operation with setting the execution state
Render the button with our overridden onClick, and extended disabled
NOTE: You should ensure that the original onClick operation is async aka it is returning a Promise.
By using event.target , you can disabled the clicked button.
Use arrow function when you create and call the function onClick. Don't forget to pass the event in parameter.
See my codePen
Here is the code:
class Buttons extends React.Component{
constructor(props){
super(props)
this.buttons = ['A','B','C','D']
}
disableOnclick = (e) =>{
e.target.disabled = true
}
render(){
return(
<div>
{this.buttons.map((btn,index) => (
<button type='button'
key={index}
onClick={(e)=>this.disableOnclick(e)}
>{btn}</button>
))}
</div>
)}
}
ReactDOM.render(<Buttons />, document.body);
const once = (f, g) => {
let done = false;
return (...args) => {
if (!done) {
done = true;
f(...args);
} else {
g(...args);
}
};
};
const exampleMethod = () => console.log("exampleMethod executed for the first time");
const errorMethod = () => console.log("exampleMethod can be executed only once")
let onlyOnce = once(exampleMethod, errorMethod);
onlyOnce();
onlyOnce();
output
exampleMethod executed for the first time
exampleMethod can be executed only once
You can get the element reference in the onClick callback and setAttribute from there, eg:
<Button
onClick={(e) => {
e.target.setAttribute("disabled", true);
this.handler();
}}
>
Submit
</Button>
Keep it simple and inline:
<button type="submit"
onClick={event => event.currentTarget.disabled = true}>
save
</button>
But! This will also disable the button, when the form calidation failed! So you will not be able to re-submit.
In this case a setter is better.
This fix this set the disabled in the onSubmit of the form:
// state variable if the form is currently submitting
const [submitting, setSubmitting] = useState(false);
// ...
return (
<form onSubmit={e => {
setSubmitting(true); // create a method to modify the element
}}>
<SubmitButton showLoading={submitting}>save</SubmitButton>
</form>
);
And the button would look like this:
import {ReactComponent as IconCog} from '../../img/icon/cog.svg';
import {useEffect, useRef} from "react";
export const SubmitButton = ({children, showLoading}) => {
const submitButton = useRef();
useEffect(() => {
if (showLoading) {
submitButton.current.disabled = true;
} else {
submitButton.current.removeAttribute("disabled");
}
}, [showLoading]);
return (
<button type="submit"
ref={submitButton}>
<main>
<span>{children}</span>
</main>
</button>
);
};
Another approach could be like so:
<button onClick={this.handleClick} disabled={isLoading ? "disabled" :""}>Send</button>
My approach is if event on processing do not execute anything.
class UploadArea extends React.Component {
constructor(props) {
super(props)
this.state = {
onProcess:false
}
}
uploadFile() {
if (!this.state.onProcess){
this.setState({
onProcess: true
});
// then do your thing
this.setState({
onProcess: false;
});
}
}
render() {
return (
<button
type='submit'
onClick={() => this.uploadFile()}>
Upload
</button>
)
}
}
ReactDOM.render(<UploadArea />, document.body);
Try with this code:
class Form extends React.Component {
constructor() {
this.state = {
disabled: false,
};
}
handleClick() {
this.setState({
disabled: true,
});
if (this.state.disabled) {
return;
}
setTimeout(() => this.setState({ disabled: false }), 2000);
}
render() {
return (
<button type="submit" onClick={() => this.handleClick()} disabled={this.state.disabled}>
Submit
</button>
);
}
}
ReactDOM.render(<Form />, document.getElementById('root'));

How to trigger INPUT FILE event REACTJS by another DOM

I have a INPUT BUTTON and INPUT FILE, I want to click the BUTTON and it will trigger the INPUT FILE event in REACT JS.
React.createElement('input',{type:'file', name:'myfile'})
then the button
React.createElement('a',{onClick: this.doClick},'Select File')
So how to define and trigger the INPUT FILE click event when we click the A HREF?
Your help is appreciate.
:-)
Update: Sep 18, 2021
Note: On NextJS, I was facing onChange event is not trigged from input file element. For that, we can use onInputCapture or onChangeCapture. For more detailed information, Stackoverflow - onChange event is not firing
Basic example on onChangeCapture as per our requirement. Requires React ^16.8,
const Dummy = () => {
const inputFileRef = React.useRef();
const onFileChangeCapture = ( e: React.ChangeEvent<HTMLInputElement> ) {
/*Selected files data can be collected here.*/
console.log(e.target.files);
};
const onBtnClick = () => {
/*Collecting node-element and performing click*/
inputFileRef.current.click();
};
return (
<form>
<input
type="file"
ref={inputFileRef}
onChangeCapture={onFileChangeCapture}
/>
<button onClick={onBtnClick}>Select file</button>
</form>
);
};
Using useRef Hook in functional components. Requires React ^16.8,
const Dummy = () => {
const inputFileRef = useRef( null );
const onFilechange = ( e ) => {
/*Selected files data can be collected here.*/
console.log( e.target.files );
}
const onBtnClick = () => {
/*Collecting node-element and performing click*/
inputFileRef.current.click();
}
return (
<form className="some-container">
<input
type="file"
ref={inputFileRef}
onChange={onFileChange}
/>
<button onClick={onBtnClick}>Select file</button>
</form>
)
}
Class Implementation with React.createRef() and handling click with node element.
class Dummy extends React.Component {
constructor( props ) {
super( props );
this.inputFileRef = React.createRef();
this.onFileChange = this.handleFileChange.bind( this );
this.onBtnClick = this.handleBtnClick.bind( this );
}
handleFileChange( e ) {
/*Selected files data can be collected here.*/
console.log( e.target.files );
}
handleBtnClick() {
/*Collecting node-element and performing click*/
this.inputFileRef.current.click();
}
render() {
return (
<form className="some-container">
<input
type="file"
ref={this.inputFileRef}
onChange={this.onFileChange}
/>
<button onClick={this.onBtnClick}>Select file</button>
</form>
)
}
}
You don't need jQuery for this. You don't even need an event handler. HTML has a specific element for this, called label.
First, make sure your input element has an id attribute:
React.createElement('input',{type:'file', name:'myfile', id:'myfile'})
Then, instead of:
React.createElement('a',{onClick: this.doClick},'Select File')
Try:
React.createElement('label',{htmlFor: 'myfile'},'Select File')
(Instead of adding htmlFor and id attributes, another solution is to make the input element a child of the label.)
Now clicking the label should trigger the same behaviour as clicking the input itself.
You could trigger the input type file with ref, f.e:
on your class component:
<input
ref={fileInput => this.fileInput = fileInput}
type="file"
/>
<button onClick={this.triggerInputFile}> Select File </button>
and make a function on that class component too:
triggerInputFile = () => this.fileInput.click()
Using Hooks with useref:
import React, {useRef} from 'react';
const FancyInput = () => {
const fileInput = useRef(null)
const handleClick = () => {
fileInput.current.click()
}
const handleFileChange = event => {
console.log("Make something")
}
return(
<div className="patientactions-container">
<input
type="file"
onChange={(e) => handleFileChange(e)}
ref={fileInput}
/>
<div onClick={() => handleClick()}></div>
</div>
)
}
export default FancyInput;
Building on the answer from #YÒGÎ , here is an implementation using TypeScript:
class Dummy extends React.Component {
fileInputRef: React.RefObject<HTMLInputElement> = React.createRef();
forwardClickToInputElement = () => {
this.fileInputRef.current!.click();
};
handleUploadDemand = (ie: ChangeEvent<HTMLInputElement>) => {
const fileList: FileList = ie.target.files;
// do something with the FileList, for example:
const fileReader = new FileReader();
fileReader.onload = () => {
const str = String(fileReader.result);
try {
const parsedContent = YOUR_OWN_PARSING(str);
} catch (error) {
// YOUR OWN ERROR HANDLING
}
};
fileReader.readAsBinaryString(fileList[0])
}
render() {
return (
<div className="some-container">
<button onClick={this.forwardClickToInputElement}>Select File</button>
<input ref={this.fileInputRef} type="file" onChange={this.handleSelectFile} hidden={true}/>
</div>
)
}
}
References:
Solution for how to use refs in React with Typescript https://stackoverflow.com/a/50505931/2848676
Use ! operator for ref type narrowing https://medium.com/#martin_hotell/react-refs-with-typescript-a32d56c4d315
const CustomInput = () => {
const handleClick = () => {
document.getElementById("file_upload").click();
};
const handleFileChange = (event) => {
console.log("Make something");
};
return (
<div className="patientactions-container">
<input type="file" id="file_upload" onChange={(e) => handleFileChange(e)} />
<div onClick={() => handleClick()}></div>
</div>
);
};
export default CustomInput;
EDIT: This is a question I answered a long time ago not knowing very much react at this time. The fun thing is that it has been considered valid ^^.
So for anyone reading this answer; this answer is wrong and is a very good example of something you shouldn't do in react.
Please find below a nice anti-pattern, again, don't do it.
=================================================
You can achieve this using jQuery:
this.doClick: function() {
$('input[type=file]').trigger('click');
}
React does not provide specific functions to trigger events, you can use jQuery or simply native Javascript: see Creating and triggering events on MDN

Categories