First of all, thanks and congrats for _clipboard.js_, it is a big enhancement for every web developer.
I am using _clipboard.js_ with React and it works. I would like to add style from material-ui.
At first I was using the data-clipboard-target attribute, then I noticed the last part of documentation that explaing how to set target dynamically.
So I solved my issue while I was opening it, just reading the docs. I just want to report my working example, to give back to the community.
import React from 'react'
class ResultURL extends React.Component {
componentDidMount () {
const {
buttonId,
inputId
} = this.props
const buttonIdSelector = `#${buttonId}`
const Clipboard = this.props.clipboard
this.clipboard = new Clipboard(
buttonIdSelector, {
target: () => document.getElementById(inputId)
}
)
}
render () {
const {
buttonId,
inputId,
value
} = this.props
const inputIdSelector = `#${inputId}`
return (
<div>
<input
id={inputId}
type={'text'}
value={value}
readOnly
/>
<button
id={buttonId}
> Copy
</button>
</div>
)
}
}
ResultURL.propTypes = {
clipboard: React.PropTypes.func.isRequired,
value: React.PropTypes.string,
inputId: React.PropTypes.string.isRequired,
buttonId: React.PropTypes.string.isRequired
}
export default ResultURL
Where this this.props.clipboard comes from?
Can you post an example working on JSFiddle or CodePen?
Thanks
It is just
import Clipboard from 'clipboard'
passed as a React prop.
It is worth putting this in the Wiki.
@brunogenaro I wrote a Codepen. The input text is not editable cause you need to pass an onChange prop that modifies the state, but the Clipboard implementation works.
See the Pen Clipboard.js + React by fibo (@fibo) on CodePen.
Done: https://github.com/zenorocha/clipboard.js/wiki/Guides ;)
Thanks @fibo for sharing your experience!
I had a hard time destroy()-ing my clipboard object at componentWillUnmount but finally was able to do it with this.clipboard instance. In my Meteor app, not destroying it when component unmounts results to x times action is made with x as the number of times Clipboard instance is instantiated, in my case, at componentDidMount.
Perhaps adding that declaration to React guide would help others. it's as simple as:
componentWillUnmount() {
this.clipboard.destroy();
}
Cheers!
@jayjaydluffy thanks
I added it to the codepen http://codepen.io/fibo/pen/wWKGNK?editors=0010 as
componentWillUnmount() {
this.props.clipboard.destroy()
}
I also used React ref which is much cleaner.
@fibo Hello, thanks for the react example.
I try your implementation in my project, instead of <input> and <button>, I use the material-ui react component <TextField> and <FlatButton> .
import TextField from 'material-ui/TextField'
import FlatButton from 'material-ui/FlatButton'
import Clipboard from 'clipboard'
````
componentDidMount() {
const copyButton = this.copyButton
const passwordTextField = this.passwordTextField
this.clipboard = new Clipboard(
copyButton, {
target: () => passwordTextField
}
)
}
````
in render()
<TextField disabled={true}
ref={(input) => {
console.info(input)
return this.passwordTextField = input
}}
hintText='Password'
style={style.textField.field}
fullWidth={true}
underlineShow={false}
value={this.state.generatedPassword}
/>
<FlatButton style={style.generateButton}
primary={true}
label="Generate"
onClick={this.handleGeneratePassword}
/>
<FlatButton style={style.copyButton}
ref={(element) => {
console.info(element)
return this.copyButton = element
}}
disabled={copyButtonDisabled}
primary={true}
label="Copy"
/>
and I get this error
frontend.bbd2615c67e904c490d0.js:1 Uncaught TypeError: First argument must be a String, HTMLElement, HTMLCollection, or NodeList
at r (frontend.bbd2615c67e904c490d0.js:1)
at t.value (frontend.bbd2615c67e904c490d0.js:1)
at new t (frontend.bbd2615c67e904c490d0.js:1)
at t.value (frontend.bbd2615c67e904c490d0.js:1)
at vendor.bbd2615c67e904c490d0.js:28
at l (vendor.bbd2615c67e904c490d0.js:28)
at vendor.bbd2615c67e904c490d0.js:28
at e.notifyAll (vendor.bbd2615c67e904c490d0.js:23)
at o.close (vendor.bbd2615c67e904c490d0.js:28)
at o.closeAll (vendor.bbd2615c67e904c490d0.js:9)
r @ frontend.bbd2615c67e904c490d0.js:1
value @ frontend.bbd2615c67e904c490d0.js:1
t @ frontend.bbd2615c67e904c490d0.js:1
value @ frontend.bbd2615c67e904c490d0.js:1
(anonymous) @ vendor.bbd2615c67e904c490d0.js:28
l @ vendor.bbd2615c67e904c490d0.js:28
(anonymous) @ vendor.bbd2615c67e904c490d0.js:28
e.notifyAll @ vendor.bbd2615c67e904c490d0.js:23
close @ vendor.bbd2615c67e904c490d0.js:28
closeAll @ vendor.bbd2615c67e904c490d0.js:9
perform @ vendor.bbd2615c67e904c490d0.js:9
l @ vendor.bbd2615c67e904c490d0.js:23
perform @ vendor.bbd2615c67e904c490d0.js:9
batchedUpdates @ vendor.bbd2615c67e904c490d0.js:28
a @ vendor.bbd2615c67e904c490d0.js:9
_renderNewRootComponent @ vendor.bbd2615c67e904c490d0.js:23
_renderSubtreeIntoContainer @ vendor.bbd2615c67e904c490d0.js:23
render @ vendor.bbd2615c67e904c490d0.js:23
324 @ frontend.bbd2615c67e904c490d0.js:1
t @ vendor.bbd2615c67e904c490d0.js:1
window.webpackJsonp @ vendor.bbd2615c67e904c490d0.js:1
(anonymous) @ frontend.bbd2615c67e904c490d0.js:1
I understand that the first argument of Clipboard is not string or html element, but I don't know how to pass it from material-ui react component.
Do you have same example to show how to use Material-ui component with clipboard please ?
Thanks a lot
Hi @leialexisjiang, I have no example or snippet already working with material ui. It looks like TextField and FlatButton do not support ref props that is available, as far as I know, in builtin React components.
@fibo Shouldn't the example be invoking destroy on this.clipboard (as opposed to this.props.clipboard)?
@Clee681 Yes it makes more sense, codepen updated, 10x
Facing the same issue @leialexisjiang is facing. I use Material UI copy Icon as button
import Clipboard from 'clipboard';
import IconCopy from '@material-ui/icons/FileCopy';
class CopyClass extends React.Component {
componentDidMount () {
const button = this.copyIcon
const input = this.email
this.clipboard = new Clipboard(
button, {
target: () => input
}
)
}
componentWillUnmount() {
聽 聽this.clipboard.destroy()
}
renderUserCaption () {
return (
<div>
<input
ref={el => this.email = el}
value={this.props.user.email_primary}
type="text"
readOnly={true}
/>
{document.queryCommandSupported('copy') && canCopy && (
<Tooltip title="Copy this email address">
<IconCopy
ref={(el) => this.copyIcon = el}
/>
</Tooltip>
)}
</div>
)
}
render () {
return (
<div>
<SomeOtherComponent
titleCaption={this.renderUserCaption()}
>
{this.props.children}
</SomeOtherComponent>
</div>
)
}
}
I am getting the this error
clipboard.js:736 Uncaught TypeError: First argument must be a String, HTMLElement, HTMLCollection, or NodeList
at listen (clipboard.js:736)
at Clipboard.listenClick (clipboard.js:183)
at new Clipboard (clipboard.js:151)
at UserCard.componentDidMount (index.js:107)
at commitLifeCycles (react-dom.development.js:16227)
at commitAllLifeCycles (react-dom.development.js:17592)
at HTMLUnknownElement.callCallback (react-dom.development.js:149)
at Object.invokeGuardedCallbackDev (react-dom.development.js:199)
at invokeGuardedCallback (react-dom.development.js:256)
at commitRoot (react-dom.development.js:17788)
at completeRoot (react-dom.development.js:19240)
at performWorkOnRoot (react-dom.development.js:19169)
at performWork (react-dom.development.js:19077)
at performSyncWork (react-dom.development.js:19051)
at requestWork (react-dom.development.js:18920)
at scheduleWork (react-dom.development.js:18729)
at Object.enqueueSetState (react-dom.development.js:12457)
at AsyncComponent../node_modules/react/cjs/react.development.js.Component.setState (react.development.js:375)
at AsyncComponent._callee$ (index.js:20)
at tryCatch (runtime.js:62)
at Generator.invoke [as _invoke] (runtime.js:296)
at Generator.prototype.(anonymous function) [as next] (http://localhost:4000/static/js/bundle.js:17522:21)
at step (index.js:79)
at index.js:79
listen @ clipboard.js:736
listenClick @ clipboard.js:183
Clipboard @ clipboard.js:151
componentDidMount @ index.js:107
commitLifeCycles @ react-dom.development.js:16227
commitAllLifeCycles @ react-dom.development.js:17592
callCallback @ react-dom.development.js:149
invokeGuardedCallbackDev @ react-dom.development.js:199
invokeGuardedCallback @ react-dom.development.js:256
commitRoot @ react-dom.development.js:17788
completeRoot @ react-dom.development.js:19240
performWorkOnRoot @ react-dom.development.js:19169
performWork @ react-dom.development.js:19077
performSyncWork @ react-dom.development.js:19051
requestWork @ react-dom.development.js:18920
scheduleWork @ react-dom.development.js:18729
enqueueSetState @ react-dom.development.js:12457
./node_modules/react/cjs/react.development.js.Component.setState @ react.development.js:375
_callee$ @ index.js:20
tryCatch @ runtime.js:62
invoke @ runtime.js:296
prototype.(anonymous function) @ runtime.js:114
step @ index.js:79
(anonymous) @ index.js:79
Promise.then (async)
step @ index.js:79
(anonymous) @ index.js:79
(anonymous) @ index.js:79
componentDidMount @ index.js:9
commitLifeCycles @ react-dom.development.js:16227
commitAllLifeCycles @ react-dom.development.js:17592
callCallback @ react-dom.development.js:149
invokeGuardedCallbackDev @ react-dom.development.js:199
invokeGuardedCallback @ react-dom.development.js:256
commitRoot @ react-dom.development.js:17788
completeRoot @ react-dom.development.js:19240
performWorkOnRoot @ react-dom.development.js:19169
performWork @ react-dom.development.js:19077
performSyncWork @ react-dom.development.js:19051
requestWork @ react-dom.development.js:18920
scheduleWork @ react-dom.development.js:18729
enqueueSetState @ react-dom.development.js:12457
./node_modules/react/cjs/react.development.js.Component.setState @ react.development.js:375
onStateChange @ connectAdvanced.js:205
dispatch @ redux.js:214
(anonymous) @ middleware.js:13
(anonymous) @ index.js:11
dispatch @ redux.js:611
(anonymous) @ index.js:128
Promise.then (async)
(anonymous) @ index.js:125
(anonymous) @ index.js:121
(anonymous) @ index.js:8
(anonymous) @ redux.js:462
componentDidMount @ index.js:16
commitLifeCycles @ react-dom.development.js:16227
commitAllLifeCycles @ react-dom.development.js:17592
callCallback @ react-dom.development.js:149
invokeGuardedCallbackDev @ react-dom.development.js:199
invokeGuardedCallback @ react-dom.development.js:256
commitRoot @ react-dom.development.js:17788
completeRoot @ react-dom.development.js:19240
performWorkOnRoot @ react-dom.development.js:19169
performWork @ react-dom.development.js:19077
performSyncWork @ react-dom.development.js:19051
requestWork @ react-dom.development.js:18920
scheduleWork @ react-dom.development.js:18729
scheduleRootUpdate @ react-dom.development.js:19397
updateContainerAtExpirationTime @ react-dom.development.js:19425
updateContainer @ react-dom.development.js:19482
./node_modules/react-dom/cjs/react-dom.development.js.ReactRoot.render @ react-dom.development.js:19774
(anonymous) @ react-dom.development.js:19914
unbatchedUpdates @ react-dom.development.js:19280
legacyRenderSubtreeIntoContainer @ react-dom.development.js:19910
render @ react-dom.development.js:19971
./src/index.js @ index.js:21
__webpack_require__ @ bootstrap c0f3913402f91ebe9253:707
fn @ bootstrap c0f3913402f91ebe9253:112
0 @ index.js:21
__webpack_require__ @ bootstrap c0f3913402f91ebe9253:707
./node_modules/@babel/runtime/helpers/arrayWithoutHoles.js.module.exports @ bootstrap c0f3913402f91ebe9253:805
(anonymous) @ bootstrap c0f3913402f91ebe9253:805
index.js:115 Uncaught TypeError: Cannot read property 'destroy' of undefined
at UserCard.componentWillUnmount (index.js:115)
at callComponentWillUnmountWithTimer (react-dom.development.js:16065)
at HTMLUnknownElement.callCallback (react-dom.development.js:149)
at Object.invokeGuardedCallbackDev (react-dom.development.js:199)
at invokeGuardedCallback (react-dom.development.js:256)
at safelyCallComponentWillUnmount (react-dom.development.js:16072)
at commitUnmount (react-dom.development.js:16446)
at commitNestedUnmounts (react-dom.development.js:16477)
at unmountHostComponents (react-dom.development.js:16738)
at commitDeletion (react-dom.development.js:16790)
at commitAllHostEffects (react-dom.development.js:17544)
at HTMLUnknownElement.callCallback (react-dom.development.js:149)
at Object.invokeGuardedCallbackDev (react-dom.development.js:199)
at invokeGuardedCallback (react-dom.development.js:256)
at commitRoot (react-dom.development.js:17753)
at completeRoot (react-dom.development.js:19240)
at performWorkOnRoot (react-dom.development.js:19169)
at performWork (react-dom.development.js:19077)
at performSyncWork (react-dom.development.js:19051)
at requestWork (react-dom.development.js:18920)
at scheduleWork (react-dom.development.js:18729)
at Object.enqueueSetState (react-dom.development.js:12457)
at AsyncComponent../node_modules/react/cjs/react.development.js.Component.setState (react.development.js:375)
at AsyncComponent._callee$ (index.js:20)
at tryCatch (runtime.js:62)
at Generator.invoke [as _invoke] (runtime.js:296)
at Generator.prototype.(anonymous function) [as next] (http://localhost:4000/static/js/bundle.js:17522:21)
at step (index.js:79)
at index.js:79
Can someone tell me where I have done wrong?
Most helpful comment
Done: https://github.com/zenorocha/clipboard.js/wiki/Guides ;)
Thanks @fibo for sharing your experience!