I'm currently using React v0.14.0 and I'm not able to click on any links generated by React on Safari v8.0.6 and IE. I've added cursor:pointer as suggested in this link but no luck. The same code seems to work very well on Safari (Version 9.0.1 (11601.2.7.2)) without cursor:pointer. Can you please help me figure out how to make it work ?
Below my code:
var React = require('react/addons');
var myComponent = React.createClass({
displayName: 'helloworld',
render: function () {
var style = {cursor : 'pointer'};
return (
<p><a onClick={() => alert('clicked')} style={ style }>Hello World</a></p>
);
}
});
module.exports = myComponent;
I just changed this one : myFunction (param) {} to myFunction: function (param) {} and now all is working as it should.
Related
I want to be able to intercept an elements on click event that get's triggered by a React component and then override the functionality with native javascript.
window.addEventListener('load', function() {
var el = document.getElementById('button');
el.addEventListener('click', function(e) {
e.stopPropagation();
window.location.href = 'http://www.google.com';
});
});
The above code is placed after the bundled react code and it looks as if it can't find the element? Is there a way I can wait for React to be loaded? Or is there a better way I can handle this? Or can I override/attach an event to a react element? (I cant use react as it's already bundled)
Since your React app ends up being a JS file that you reference in your index.html file, that means if you reference a node element that's generated inside of it, jQuery can't see it because it was dynamically generated.
To solve this issue you can use two React concepts: lifecycle methods, and refs.
One, create a reference of the button you're aiming to listen globally, in the parent component of that button:
class ParentComponent extends React.Component {
render() {
return (
<button ref={(button) => this.button = button}>
Click this button
</button>
);
}
}
Two, create a componentDidMount life cycle method to make use of the ref you've just created:
class ParentComponent {
componentDidMount = () => {
this.button.addEventListener('click', function (e) {
e.preventDefault();
console.log('it works?!');
});
}
render() {
return (
<button ref={(button) => this.button = button}>
Click this button
</button>
);
}
}
Keep in mind that in other lifecycle methods like componentWillMount, or componetWillUpdate refs are either not existent or are old, because the component hasn't made it's most recent render yet. So, if you want to integrate 3rd party DOM libraries, use didMount.
I actually managed to figure it out using plain javascript, here's my code:
window.addEventListener('load', function() {
var checkExist = setInterval(function() {
var el = document.getElementById('button');
if (el != null) {
clearInterval(checkExist);
el.addEventListener('click', function(e) {
e.stopPropagation();
window.location.href = 'http://www.google.com';
});
}
}, 100);
});
I am trying to learn React and came across this code on the website: https://www.reactenlightenment.com/react-state/8.2.html
I understood what the code does but could not understand what is parameter 'a' in changeMood function. I ran the code after removing it and code worked well.
var MoodComponent = React.createClass({
getInitialState: function() {
return {mood: ':|'};
},
changeMood:function(event, a){
const moods = [':)',':|',':('];
const current = moods.indexOf(event.target.textContent);
this.setState({mood: current === 2 ? moods[0] : moods[current+1]});
},
render: function() {
return (
<span style={{fontSize:'60',border:'1px solid #333',cursor:'pointer'}}
onClick={this.changeMood}>
{this.state.mood}
</span>
)
}
});
It doesn't mean anything.
The onClick only sends 1 parameter, the event, which is a Synthetic event created by React to handle cross browser compatibility.
For more info, check the React docs on handling events
I am trying to use refs to access a DOM node in React, however, I am getting the following error.
Uncaught TypeError: this.refs.card.getDomNode is not a function(…)
I am following this tutorial, and as far as I can tell this should work.
Here is the code I have written. Can anyone spot anything obvious i am doing wrong.
var App = React.createClass({
componentDidMount: function() {
var card = this.refs.card.getDomNode();
},
render: function() {
return(
<div>
<Card ref="card"/>
</div>
)
}
});
From 0.15 onwards React depricated .getDOMNode()
this.getDOMNode() is now deprecated and ReactDOM.findDOMNode(this) can
be used instead.
Use like this
componentDidMount: function() {
var card = ReactDOM.findDOMNode(this.refs.card);
},
For more details see here
According to the following link
You need to change the following
function() {
var card = this.refs.card.getDomNode();
},
TO THIS
function() {
var card = this.refs.card;
},
If it points to the input,select,div DOM node
and
function() {
var mmo = ReactDOM.findDOMNode(this.refs.card);
},
for the custom component you have created yourself
I have a special case where I need to encapsulate a React Component with a Web Component. The setup seems very straight forward. Here is the React Code:
// React Component
class Box extends React.Component {
handleClick() {
alert("Click Works");
}
render() {
return (
<div
style={{background:'red', margin: 10, width: 200, cursor: 'pointer'}}
onClick={e => this.handleClick(e)}>
{this.props.label} <br /> CLICK ME
</div>
);
}
};
// Render React directly
ReactDOM.render(
<Box label="React Direct" />,
document.getElementById('mountReact')
);
HTML:
<div id="mountReact"></div>
This mounts fine and the click event works. Now when I created a Web Component wrapper around the React Component, it renders correctly but the click event doesn't work. Here is my Web Component Wrapper:
// Web Component Wrapper
class BoxWebComponentWrapper extends HTMLElement {
createdCallback() {
this.el = this.createShadowRoot();
this.mountEl = document.createElement('div');
this.el.appendChild(this.mountEl);
document.onreadystatechange = () => {
if (document.readyState === "complete") {
ReactDOM.render(
<Box label="Web Comp" />,
this.mountEl
);
}
};
}
}
// Register Web Component
document.registerElement('box-webcomp', {
prototype: BoxWebComponentWrapper.prototype
});
And here is the HTML:
<box-webcomp></box-webcomp>
Is there something I'm missing? Or does React refuse to work inside a Web Component? I have seen a library like Maple.JS which does this sort of thing, but their library works. I feel like I'm missing one small thing.
Here is the CodePen so you can see the problem:
http://codepen.io/homeslicesolutions/pen/jrrpLP
As it turns out the Shadow DOM retargets click events and encapsulates the events in the shadow. React does not like this because they do not support Shadow DOM natively, so the event delegation is off and events are not being fired.
What I decided to do was to rebind the event to the actual shadow container which is technically "in the light". I track the event's bubbling up using event.path and fire all the React event handlers within context up to the shadow container.
I added a 'retargetEvents' method which binds all the possible event types to the container. It then will dispatch the correct React event by finding the "__reactInternalInstances" and seek out the respective event handler within the event scope/path.
retargetEvents() {
let events = ["onClick", "onContextMenu", "onDoubleClick", "onDrag", "onDragEnd",
"onDragEnter", "onDragExit", "onDragLeave", "onDragOver", "onDragStart", "onDrop",
"onMouseDown", "onMouseEnter", "onMouseLeave","onMouseMove", "onMouseOut",
"onMouseOver", "onMouseUp"];
function dispatchEvent(event, eventType, itemProps) {
if (itemProps[eventType]) {
itemProps[eventType](event);
} else if (itemProps.children && itemProps.children.forEach) {
itemProps.children.forEach(child => {
child.props && dispatchEvent(event, eventType, child.props);
})
}
}
// Compatible with v0.14 & 15
function findReactInternal(item) {
let instance;
for (let key in item) {
if (item.hasOwnProperty(key) && ~key.indexOf('_reactInternal')) {
instance = item[key];
break;
}
}
return instance;
}
events.forEach(eventType => {
let transformedEventType = eventType.replace(/^on/, '').toLowerCase();
this.el.addEventListener(transformedEventType, event => {
for (let i in event.path) {
let item = event.path[i];
let internalComponent = findReactInternal(item);
if (internalComponent
&& internalComponent._currentElement
&& internalComponent._currentElement.props
) {
dispatchEvent(event, eventType, internalComponent._currentElement.props);
}
if (item == this.el) break;
}
});
});
}
I would execute the "retargetEvents" when I render the React component into the shadow DOM
createdCallback() {
this.el = this.createShadowRoot();
this.mountEl = document.createElement('div');
this.el.appendChild(this.mountEl);
document.onreadystatechange = () => {
if (document.readyState === "complete") {
ReactDOM.render(
<Box label="Web Comp" />,
this.mountEl
);
this.retargetEvents();
}
};
}
I hope this works for future versions of React. Here is the codePen of it working:
http://codepen.io/homeslicesolutions/pen/ZOpbWb
Thanks to #mrlew for the link which gave me the clue to how to fix this and also thanks to #Wildhoney for thinking on the same wavelengths as me =).
I fixed a bug cleaned up the code of #josephvnu's accepted answer. I published it as an npm package here: https://www.npmjs.com/package/react-shadow-dom-retarget-events
Usage goes as follows
Install
yarn add react-shadow-dom-retarget-events or
npm install react-shadow-dom-retarget-events --save
Use
import retargetEvents and call it on the shadowDom
import retargetEvents from 'react-shadow-dom-retarget-events';
class App extends React.Component {
render() {
return (
<div onClick={() => alert('I have been clicked')}>Click me</div>
);
}
}
const proto = Object.create(HTMLElement.prototype, {
attachedCallback: {
value: function() {
const mountPoint = document.createElement('span');
const shadowRoot = this.createShadowRoot();
shadowRoot.appendChild(mountPoint);
ReactDOM.render(<App/>, mountPoint);
retargetEvents(shadowRoot);
}
}
});
document.registerElement('my-custom-element', {prototype: proto});
For reference, this is the full sourcecode of the fix https://github.com/LukasBombach/react-shadow-dom-retarget-events/blob/master/index.js
This answer is an update from five years after.
Bad news: answer by #josephnvu (accepted at the moment of writing) and the react-shadow-dom-retarget-events package no longer work correctly, at least with React 16.13.1 - haven't tested with earlier versions. Looks like something was changed in React internals, causing the code to invoke the wrong listener callback.
Good news:
In React 16.13.1 (again, not tested with earlier 16.x), it's possible to render directly into shadow root, without intermediate blocks. In this case, listeners would be attached to the shadow root and not to the document, so React is able to capture and dispatch all events correctly. The obvious tradeoff is that you can't add anything else to the same shadow root, since React will overwrite your elements with rendered JSX.
In React 17, React attaches its listeners to the rendering root, not to the document or shadow root, so everything works out of the box, no matter where we render to.
Replacing this.el = this.createShadowRoot(); with this.el = document.getElementById("mountReact"); just worked. Maybe because react has a global event handler and shadow dom implies event retargeting.
I've discovered another solution by accident. Use preact-compat instead of react. Seems to work fine in a ShadowDOM; Preact must bind to events differently?
I'm trying to unmount a React.js node with this._rootNodeID
handleClick: function() {
React.unmountComponentAtNode(this._rootNodeID)
}
But it returns false.
The handleClick is fired when I click on an element, and should unmount the root-node. Documentation on unmountComponentAtNode here
I've tried this as well:
React.unmountComponentAtNode($('*[data-reactid="'+this._rootNodeID+'"]')[0])
That selector works with jQuery.hide(), but not with unmounting it, while the documentation states it should be a DOMElement, like you would use for React.renderComponent
After a few more tests it turns out it works on some elements/selectors.
It somehow works with the selector: document.getElementById('maindiv'), where maindiv is an element not generated with React.js, and just plain html. Then it returns true.
But as soon as I try and select a different ElementById that is generated with React.js it returns false. And it wont work with document.body either, though they all essentially return the same thing if I console.log them (getElementsByClassName('bla')[0] also doesn't work)
There should be a simple way to select the node via this, without having to resort to jQuery or other selectors, I know it's in there somewhere..
Unmount components from the same DOM element that you mount them in. So if you did something like:
ReactDOM.render(<SampleComponent />, document.getElementById('container'));
Then you would unmount it with:
ReactDOM.unmountComponentAtNode(document.getElementById('container'));
Here is a simple JSFiddle where we mount the component and then unmount it after 3 seconds.
This worked for me. You may want to take extra precautions if findDOMNode returns null.
ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).parentNode);
The example I use:
unmount: function() {
var node = this.getDOMNode();
React.unmountComponentAtNode(node);
$(node).remove();
},
handleClick: function() {
this.unmount();
}
You don't need to unmount the component the simple solution it's change the state and render a empty div
const AlertMessages = React.createClass({
getInitialState() {
return {
alertVisible: true
};
},
handleAlertDismiss() {
this.setState({alertVisible: false});
},
render() {
if (this.state.alertVisible) {
return (
<Alert bsStyle="danger" onDismiss={this.handleAlertDismiss}>
<h4>Oh snap! You got an error!</h4>
</Alert>
);
}
return <div></div>
}
});
As mentioned in the GitHub issue you filed, if you want access to a component's DOM node, you can use this.getDOMNode(). However a component can not unmount itself. See Michael's answer for the correct way to do it.
First , i am new to reactjs ,too . Of course we can control the Component all by switch the state , but as I try and test , i get that , the React.unmountComponentAtNode(parentNode) can only unmount the component which is rendered by React.render(<SubComponent>,parentNode). So <SubComponent> to be removed must be appened by React.render() method , so I write the code
<script type="text/jsx">
var SubComponent = React.createClass({
render:function(){
return (
<div><h1>SubComponent to be unmouned</h1></div>
);
},
componentWillMount:function(){
console.log("componentWillMount");
},
componentDidMount:function(){
console.log("componentDidMount");
},
componentWillUnmount:function(){
console.log("componentWillUnmount");
}
});
var App = React.createClass({
unmountSubComponent:function(){
var node = React.findDOMNode(this.subCom);
var container = node.parentNode;
React.unmountComponentAtNode(container);
container.parentNode.removeChild(container)
},
componentDidMount:function(){
var el = React.findDOMNode(this)
var container = el.querySelector('.container');
this.subCom = React.render(<SubComponent/> , container);
},
render:function(){
return (
<div className="app">
<div className="container"></div>
<button onClick={this.unmountSubComponent}>Unmount</button>
</div>
)
}
});
React.render(<App/> , document.body);
</script>
Run the sample code in jsFiddle , and have a try .
Note: in the sample code React.findDOMNode is replaced by getDOMNode as the reactjs version problem .