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

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

Related

ScrollIntoView() can find the html element

I'm trying to create a scroll to element but I'm getting this error
"TypeError: Cannot read property 'scrollIntoView' of null".
By console logging mapRef I can see that I'm getting the correct div.
console.log
export class FinderComponent extends PureComponent {
constructor(props) {
super(props);
this.mapRef = React.createRef();
}
renderMap() {
return <div block="StoreFinder" ref={this.mapRef}></div>;
}
renderStoreCard(store) {
this.mapRef.current.scrollIntoView({ behavior: "smooth" });
//console.log(this.mapRef.current);
return (
<div
block="StoreFinder"
elem="Store"
key={store_name.replace(/\s/g, "")}
mods={{ isActive: store_name === selectedStoreName }}
>
{this.renderStoreCardContent(store)}
<button
block="Button"
mods={{ likeLink: true }}
onClick={() => changeStore(store)}
>
{__("Show on the map")}
</button>
</div>
);
}
}
I made this functional component that has a working example with ScrollIntoView(). If I understood you right, you want to add the scrollIntoView()-function to an element. This is how you do it with functional components:
import React, { useEffect, useRef } from 'react'
export const TestComponent = () => {
const inputEl = useRef(null) //"inputEl" is the element that you add the scroll function to
useEffect(() => {
inputEl.current.scrollIntoView() //use this if you want the scroll to happen when loading the page
}, [inputEl])
const onButtonClick = () => {
inputEl.current.scrollIntoView() //use this if you want the scroll to happen on click instead.
}
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
)
}

React scroll to anchor when opening URL in browser

Lets say I have component "Post" which holds multiple components "Comment". I want to make that application scrolls down on comment with that anchor when I enter URL like this:
/post/:postId/#commentId
I have already working postId route /post/:postId
I tried to implement it with react-hash-link npm package but it's not working as intended.
Every comment has it's own ID which is set on component, like this:
<div class="post">
<div class="post-header">
<div class="post-header-avatar">
SOME TEXT
</div>
<div class="post-header-info">
SOME TEXT
</div>
</div>
<div class="post-content">
<span>POST CONTENT</span>
</div>
<div class="post-likers-container">
<div class="post-likers-header label">People who like this post</div>
<div class="post-likers">
SOME TEXT
</div>
</div>
<div class="post-comments">
<div class="comments ">
<div class="comments-all label">Comments</div>
<div class="comments">
<div class="comment" id="5d27759edd51be1858f6b6f2">
<div class="comment-content">
COMMENT 1 TEXT
</div>
</div>
<div class="comment" id="5d2775b2dd51be1858f6b720">
<div class="comment-content">
COMMENT 2 TEXT
</div>
</div>
<div class="comment" id="5d2775ecdd51be1858f6b753">
<div class="comment-content">
COMMENT 3 TEXT
</div>
</div>
</div>
</div>
</div>
</div>
So for example if I open URL like:
/post/postId/#5d2775ecdd51be1858f6b753
I want to open page of post and that it scrolls down to the comment with # anchor.
Is there any way to implement this?
I managed to find simple solution for my use case, without creating refs for comments, passing them, etc. Since my hierarchy of components is like this:
Post --> render component Comments
Comments --> render
multiple components Comment with props data passed from Post
In Post component I created function:
scrollToComment= () => {
let currentLocation = window.location.href;
const hasCommentAnchor = currentLocation.includes("/#");
if (hasCommentAnchor) {
const anchorCommentId = `${currentLocation.substring(currentLocation.indexOf("#") + 1)}`;
const anchorComment = document.getElementById(anchorCommentId);
if(anchorComment){
anchorComment.scrollIntoView({ behavior: "smooth" });
}
}
}
Then I render Comments like this:
<Comments limit={limit} post={post} scrollToComment={this.scrollToComment} />
In Comments I generate comments after some sorting like this:
{sortedComments.map((comment, i) => <Comment key={i} {...comment} scrollToComment={this.props.scrollToComment}/> )}
and finally in Comment component I execute scrollToComment in ComponentDidMount():
if(this.props.scrollToComment)
this.props.scrollToComment(this.props._id);
After that when I go to some URL I get nice smooth scrolling to the comment specified in hash part of URL.
I tried #Christopher solution but it didn't worked for me.
I really liked your solution #SaltyTeemooo. Inspired by it I found an even simpler way without any callbacks.
My setup is very similar so lets say I am dealing with posts and comments.
In Post I create the Comments (simpified) like this and pass the anchorId:
<Comments anchorId={window.location.href.slice(window.location.href.indexOf("#") + 1)} props... />
In Comments I pass the anchor id along into Comment.js
<Comment anchorId={props.anchorId} props.../>
And then in the Comment, I scroll the current element into view, if it is the linked one
import React, { useRef, useEffect } from 'react';
function Comment () {
const comment = useRef(null); //to be able to access the current one
useEffect(() => {
if(props.anchorId === props.commentData.id)
{
comment.current.scrollIntoView({ behavior: "smooth" });
}
}, []) //same as ComponentDidMount
return(
<div id={props.commentData.id} ref={comment}> //here is where the ref gets set
...
</div>
)
}
Took a pretty solid amount of time but try this sandbox: https://codesandbox.io/s/scrollintoview-with-refs-and-redux-b881s
This will give you a ton of insight on how to scroll to an element using a URL param.
import React from "react";
import { connect } from "react-redux";
import { getPost } from "./postActions";
class Post extends React.Component {
constructor(props) {
super(props);
this.state = {
activeComment: null
};
this._nodes = new Map();
}
componentDidMount() {
this.props.getPost(this.props.match.params.id);
const path = window.location.href;
const commentId = path.slice(path.indexOf("#") + 1);
if (commentId) {
this.setState({
activeComment: commentId
});
}
}
componentDidUpdate(prevProps, prevState) {
if (this.state.activeComment !== prevState.activeComment) {
this.scrollToComment();
}
}
scrollToComment = () => {
const { activeComment } = this.state;
const { comments } = this.props.posts.post;
const nodes = [];
//Array.from creates a new shallow-copy of an array from an array-like or iterable object
Array.from(this._nodes.values()) //this._nodes.values() returns an iterable-object populated with the Map object values
.filter(node => node != null)
.forEach(node => {
nodes.push(node);
});
const commentIndex = comments.findIndex(
comment => comment.id == activeComment
);
if (nodes[commentIndex]) {
window.scrollTo({
behavior: "smooth",
top: nodes[commentIndex].offsetTop
});
}
};
createCommentList = () => {
const { post } = this.props.posts;
const { activeComment } = this.state;
if (post) {
return post.comments.map((comment, index) => {
return (
<div
key={comment.id}
className={
"comment " + (activeComment == comment.id ? "activeComment" : "")
}
ref={c => this._nodes.set(comment.id, c)}
>
{comment.text}
</div>
);
});
}
};
displayPost = () => {
const { post } = this.props.posts;
if (post) {
return (
<div className="post">
<h4>{post.title}</h4>
<p>{post.text}</p>
</div>
);
}
};
render() {
return (
<div>
<div>{this.displayPost()}</div>
<div>{this.createCommentList()}</div>
</div>
);
}
}
const mapStateToProps = state => {
return {
posts: state.posts
};
};
const mapDispatchToProps = dispatch => {
return {
getPost: postId => {
dispatch(getPost(postId));
}
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(Post);
In my simple case where there is no async content loading, I got the desired scrolling behavior by just adding this at the top of the page:
useEffect(() => {
const href = window.location.href
if (href.includes("#")) {
const id = `${href.substring(href.indexOf("#") + 1)}`
const anchor = document.getElementById(id)
if(anchor){
anchor.scrollIntoView({ behavior: "smooth" })
}
}
}, [])
FYI, this was for some FAQ pages consisting of a bunch of FaqEntry objects, each with a question and answer. The code below allows linking to individual entries that initialize with the answer open.
export default function FaqEntry({title, history, children}) {
if(!history) console.log("OOPS, you forgot to pass history prop", title)
const createName = title => title.toLowerCase().replace(/[^\sa-z]/g, "").replace(/\s\s*/g, "_")
const id = createName(title)
const href = window.location.href
const isCurrent = href.includes("#") && href.substring(href.indexOf("#") + 1) === id
const [open, setOpen] = useState(isCurrent)
function handleClick() {
setOpen(!open)
if (history && !open) {
const pathname = window.location.pathname + "#" + id
history.replace(pathname)
}
}
return <div id={id} className={`faqEntry ${open ? "open" : "closed"}`}>
<div className="question" onClick={handleClick}>{title}</div>
<div className="answer">{children}</div>
</div>
}
I pass the history object from React Router so that I can update the browser history without triggering a page reload.
Mensure...
import React, { useEffect } from 'react';
const MainApp = () => {
const MyRef = React.createRef();
useEffect(() => { // Same like ComponentDidMount().
scrollTo();
})
const scrollTo = () => {
window.scrollTo({
top:myRef.offsetTop,
behavior: "smooth" // smooth scroll.
});
}
return (
<div ref={MyRef}>My DIV to scroll to.</div>
)
}

Mobile number masking in React

Hi need to masked mobile number when user enter the mobile number in input box it should be 021 121 4544 in this format.
means there should be 021{auto space}121{auto space}4544
how can i build in react this functionality?
class NumberCheck extends Component {
state = {
disabled: true,
errorBlock: 'none',
mobileNumber: '',
error: ''
};
handleOnChange = (event) => {
let disabled = event.target.value ? disabled = false : disabled = true;
this.setState({
disabled: disabled,
mobileNumber: event.target.value
})
}
submit = (e) => {
e.preventDefault();
if (this.state.mobileNumber.match(/^02[0-9]{6,11}$/)) {
this.setState({
errorBlock: 'none'
})
const payload = {
msisdn: this.state.mobileNumber
}
this.props.checkNumber(payload);
} else {
this.setState({
error: ' Please enter valid mobile no',
errorBlock: 'block'
})
}
}
render() {
const { isLoading, isError } = this.props;
if (isLoading) {
return <Spinner />
}
if (isError) {
return <ChangePlanError />
}
return (
<form className="spring spring--sm">
<p className="heading heading--3 heading--center no-gutter--top">{"Already with Vodafone?"}</p>
<p className="heading--center">{"Sign in using a TXT code to check if you are eligible to upgrade or change your plan"}</p>
<div className="alert alert--light alert--warning alert--arrow validation__warning" style={{ display: this.state.errorBlock }}>
<div className="caption">
< div className="caption__media caption__media--top alert__media" >
<svg className="icon icon--extra-small icon--inherited" data-vft="icon-modifiers">
<use xmlnsXlink="http://www.w3.org/1999/xlink" xlinkHref="#icon-block" />
</svg>
</div>
<div className="caption__text caption__text--top alert__text">
{this.state.error}
</div>
</div>
</div>
<input
type="text"
onChange={this.handleOnChange}
className="form__input form__input--dark"
name="mobileno"
placeholder="021 1234567"
mask="000 000 0000" />
<div id="submit" className="form__row form__row--medium gutter--bottom">
<input
type="submit"
className={`button button--primary button--primary--grey button--full-width ${this.state.disabled ? 'button--disabled' : ''}`}
value=" Continue"
onClick={this.submit} />
</div>
</form >
);
}
}
You can create a new string with previous string and replace it in the input.
const number = '0211214544';
const num = `${number.substring(0, 3)} ${number.substring(3, 6)} ${number.substring(6, number.length)}`;
console.log(num);
Created a working example in React.
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
name: ''
};
}
change = (event) => {
let val = event.target.value;
val = val.replace(/ /gm, '');
console.log(val);
let num = `${val.substring(0, 3)} ${val.substring(3, 6)} ${val.substring(6, val.length)}`;
num = num.trim();
this.setState({
name: num
});
};
render() {
return (
<div className="App">
<input
ref="inputName"
value={this.state.name}
onChange={this.change}
/>
</div>
);
}
}
export default App;
The below function will mask the phone number , the basic operation to perform such functions is slicing/sub string and string concatenation
maskingFunction= (phoneNumber)=>{
let subNum = phoneNumber.toString().substring(0,3)
subNum = subNum + "XXXXXXXXXXXX"
return subNum
}
I created a package that exposes an input component that displays a masked value according to the mask it receives.
The mask will change keeping the cursor at the correct position (even if you change part of the value in the middle of the input, paste some characters, or delete a part of it, and even if the mask changes).
You can see a Demo with examples at:
https://lucasbasquerotto.github.io/react-masked-input
(The first example has an input with the mask asked in this SO question: 999 999 9999).
To install the package: npm i react-hook-mask
Use it:
import { MaskedInput, createDefaultMaskGenerator } from 'react-hook-mask';
const maskGenerator = createDefaultMaskGenerator('999 999 9999');
const MobileNumberMaskedInput = () => {
const [value, setValue] = React.useState('');
return (
<MaskedInput
maskGenerator={maskGenerator}
value={value}
onChange={setValue}
/>
);
};
This component wraps a default input, but the package also expose hooks to make you able to use it with any kind of component.
The function createDefaultMaskGenerator that always return a static string mask. You can also use dynamic masks that change according to the value, and even transforming the value (for example, making the characters uppercase).
The hook useMask is a generic hook that can be used with any component (even native components) as long as the component has a way to retrieve and to modify the cursor position.
The hook useRefMask wraps useMask and provides an easy way to forward a ref to the component, as well as using the ref element in the functions that manage the cursor position (Example).
The (more specific) hook useWebMask wraps useRefMask and can be used with any custom react-dom component that wraps an input and whose ref is an HTMLInputElement, without having to define a getter and a setter for the cursor position, and with an onChange function that sets the value received from the event object (Example).

Conditional rendering based on user input

I've just started learning React and am struggling with conditional rendering. I want to render components based on form input but i'm not sure what needs to be done or where it needs to be executed.
I have imported my Form component which has the input I want to use and have another component like this:
import React, {Component} from 'react';
import Form from './Form';
import CardOne from './CardOne';
import CardTwo from './CardTwo';
import CardThree from './CardThree';
export default class CardContainer extends Component {
render(){
return (
<div>
<CardOne />
<CardTwo />
<CardThree />
</div>
)
}
}
I basically want to be able to show certain Cards if the value of the input is greater than X when the form is submitted, but I don't know how to target an imported component.
This is my Form component:
export default class Form extends Component {
state = {
number: ''
};
change = (e) => {
this.setState({
[e.target.name]: e.target.value
});
};
onSubmit = e => {
e.preventDefault();
this.props.onSubmit(this.state);
this.setState({
number: ''
})
};
render(){
return (
<form>
<label>Number</label>
<input
type="number"
name="number"
placeholder="Number"
value={this.state.number}
onChange={e => this.change(e)} />
<button onClick={e => this.onSubmit(e)}>Submit</button>
</form>
)
}
}
Any help will be massively appreciated!
I have redesigned your Form component , Below is my code. . Let me know if u faced any issues on that .
import React, { Component } from 'react';
import CardOne from './CardOne';
import CardTwo from './CardTwo';
import CardThree from './CardThree';
export default class Form extends Component {
state = {
number: '',
showOne:true,
showTwo:false,
showThree:false,
userInputValue:''
};
change = (e) => {
this.setState({
userInputValue: e.target.value
});
};
onSubmit = e => {
e.preventDefault();
this.props.onSubmit(this.state);
if (this.state.userInputValue > 10 && this.state.userInputValue <20 ){
this.setState({
showTwo: true,
})
}
if (this.state.userInputValue > 20 && this.state.userInputValue < 30) {
this.setState({
showThree: true,
})
}
};
render() {
return (
<div>
<form>
<label>Number</label>
<input
type="number"
name="number"
placeholder="Number"
value={this.state.userInputValue}
onChange={e => this.change(e)} />
<button onClick={e => this.onSubmit(e)}>Submit</button>
</form>
<div>
{this.state.showOne ?
<CardOne />
:
<div></div>
}
{this.state.showTwo ?
<CardTwo />
:
<div></div>
}
{this.state.showThree ?
<CardThree />
:
<div></div>
}
</div>
</div>
)
}
}
// What i wrote above is your base functionality . You reedit the condition depends on ur requirement .
This is what I came up with following your logic of card rendering. I did not change Form coponent but rather worked on the Container
export default class CardContainer extends Component {
constructor(props) {
super(props);
state = {
number: 0,
}
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onFormSubmit=(number)=>{
this.setState({ number: number });
}
render(){
let i=Math.floor(this.state.number/10)
return (
<div>
<Form onSubmit={() => this.onFormSubmit(number)}
[<CardOne />, <CardTwo />, <CardThree/>].slice(0,i).map(card =>
{card}
)
</div>
)
}
}
I would use render prop for this kind of problem. You can research more about render props but basically your CardContainer component will not render those cards component statically as it is. It will return props.children instead.
And then you will have a function (i.e function TestX) that will have a conditional to check what the value of X is. This is the function that will return either , , based on what X is. The function TestX will receive props from CardContainer, including the value of X that is read from the state.
So I will just use CardContainer component with as its child.

In reactJS, how to copy text to clipboard?

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>

Categories