Differentiate between single and double click in React - reactjs

Trying to find out what is the best way of differentiating between single and double click in a React component.
The built-in mouse events (onClick and onDoubleClick) work fine if you don't want apply them on the same DOM element. There is the jQuery dblclick which I don't want to use. There is also a React Timer Mixin which could be used, but I use ES6 classes so then I would need to use something like React Mixin and I just think it's an overkill for doing such a simple thing.
From what I found out people would set the timeout on the click event, so I followed that direction and came with this:
import React, { Component } from 'react'
export default class Click extends Component {
constructor() {
super()
this.handleClick = this.handleClick.bind(this)
this.state = {
clickCount: 0
}
}
handleClick() {
let clickCount = this.state.clickCount
let singleClickTimer
clickCount++
this.setState({ clickCount: this.state.clickCount + 1 })
if (clickCount === 1) {
singleClickTimer = setTimeout(() => {
if (this.state.clickCount === 1) {
clickCount = 0
this.setState({ clickCount: 0 })
console.log('single click')
}
}, 300)
} else if (clickCount === 2) {
clearTimeout(singleClickTimer)
clickCount = 0
this.setState({ clickCount: 0 })
console.log('double click')
}
}
render() {
return (
<div>
<button onClick={this.handleClick}>Click me</button>
</div>
)
}
}
It works, but I'm trying to find out if there is a better way of doing it.
Edit:
The big problem with this is that setTimeout is async, so I added another check (if (this.state.clickCount === 1) {), but that seems wrong to me.
Another problem with this is the delay, the double click is called immediately, but the single click will be called after whatever timeout was set (around 300ms).

I know this is an old question but I spun my wheels on this problem a bit also, so thought I'd share my solution for those who also end up here.
const DOUBLECLICK_TIMEOUT = 300;
handleImagePress = () => {
this.setState({ pressCount: this.state.pressCount+1 });
setTimeout(() => {
if (this.state.pressCount == 0) { return }
if (this.state.pressCount == 1) {
// Do single press action
} else if (this.state.pressCount == 2) {
// Do double press action
}
this.setState({ pressCount: 0 });
}, DOUBLECLICK_TIMEOUT)
}

Related

Why Is this function producing NaN in the DOM?(React)

So I'm working on this portion of an app where I want the number displayed on the page to increase or decrease depending on the hour of the day. At this point I'm using componentDidMount() to use a setInterval() method that takes in a function ( hourCheck ) and an interval.
However, hourCheck is having issues, specifically it's returning NaN in the DOM instead of an actual number. Seems like the state isn't being changed correctly but I'm exactly sure in what way.
Here's the app.js file I'm working with. I set the interval to 8000ms for the sake of time. This is ultimately a counter app.
import React, { Component, useState } from "react";
import { Wrapper } from "./components/Wrapper";
import Title from "./components/Title";
import Count from "./components/Count";
import "./App.css";
class App extends Component {
state = {
CountNum: 0,
};
setCountNum = (val) => {
this.setState({CountNum: val})
}
hourCheck = (val) => {
let hourCount = new Date().getHours()
if (hourCount >= 9 && hourCount <= 17) {
this.setState({ CountNum: val + 1 });
} else if (hourCount <= 8) {
this.setState({ CountNum: val - 1});
} else if (hourCount >= 18) {
this.setState({ CountNum: val - 1});
}
console.log(hourCount)
};
loginSomething = (val) => {
console.log(this.state.HourNum)
};
render()
{
return (
<Wrapper setCountNum={this.setCountNum} CountNum={this.state.CountNum}>
<Title>Click Counter</Title>
<Title>Count:</Title>
<Count>{this.state.CountNum}</Count>
</Wrapper>
);
}
componentDidMount() {
setInterval(this.hourCheck, 8000);
};
}
export default App;
hourCheck function has val parameter and you are not passing that argument in setInterval in componentDidMount. Bind "this" obj to the hourCheck function in setInterval.

useEffect alternate for Class Component

I just learned that in functional components I can use useEffect to keep an eye on any side effect (ex: when using localStorage) which makes sure my state is hooked up with the effect.
I want to have similar functionality in my class based Component for localStorage. How can I make sure that my state updates itself as soon as there is any change into the localStorage?
This is how I did it.
class Login extends Component {
constructor(props) {
super(props);
this.state = {
// Get value from localStorage or use default
isLoggedIn: localStorage.getItem('isLoggedIn') || 0
}
// Listen to storage event
window.addEventListener('storage', (e) => this.storageChanged(e));
// Bind this to storageChanged()
this.storageChanged = this.storageChanged.bind(this);
}
storageChanged(e) {
if(e.key === 'isLoggedIn') {
this.setState({isLoggedIn: e.newValue})
}
}
render() {
return <p>{this.state.isLoggedIn}</p>
}
}
That's how I could hook into the localStorage changes using class based component.
You can try a hook that check for localStorage changes like in the docs.
useEffect(() => {
function checkStorage(e){
if('keyStateYouWantToSync' == e.key && stateYouWantToSync != JSON.parse(e.newValue)){
setStateYouWantToSync(JSON.parse(e.newValue))
}
}
window.addEventListener('storage', checkStorage)
return () => window.removeEventListener('storage', checkStorage)
})
You can also change the key checking to what ever key you want to check.
Edit:
For class components
checkStorage = (e) => {
if('keyStateYouWantToSync' == e.key && this.state.stateYouWantToSync != JSON.parse(e.newValue)){
this.setState({stateYouWantToSync: JSON.parse(e.newValue)})
}
}
componentDidMount(){
window.addEventListener('storage', this.checkStorage)
}
componentWillUnmount(){
window.removeEventListener('storage', this.checkStorage)
}

Why is setState being reset?

I'm working on a community site where users can share and comment on websites (here it is)[https://beta.getkelvin.com/]. Websites can be filtered by category, but when a user goes into a specific page then backs out, the filter is removed.
Here's the process in steps:
User selects filter from drop down list, and this.state.topicSelected reflects the new value
User clicks a link to see a show page of an instance (a summary of a website), this.state.topicSelected reflects the correct value
User goes back to main page, and this.state.topicSelected is reverted back to 0
Instead of reverting back to 0, I want the value to still apply so the filter remains on the same category that the user selected before.
The problem seems to be that getInitialState is resetting the value of this.state.topicSelected back to 0 (as it's written in the code). When I try to put a dynamic value in 0's place, I get an undefined error.
Here's the getInitialState code:
var SitesArea = React.createClass({
getInitialState: function () {
return {
sortSelected: "most-recent",
topicSelected: 0 // need to overwrite with value of this.state.topicSelected when user selects filter
// I removed other attributes to clean it up for your sake
}
}
On here's the event:
onTopicClick: function (e) {
e.preventDefault();
this.setState({topicSelected: Number(e.target.value)});
if (Number(e.target.value) == 0) {
if (this.state.sortSelected == 'highest-score') {
this.setState({sites: []}, function () {
this.setState({sites: this.state.score});
});
} else if (this.state.sortSelected == 'most-remarked') {
this.setState({sites: []}, function () {
this.setState({sites: this.state.remarkable});
});
} else if (this.state.sortSelected == 'most-visited') {
this.setState({sites: []}, function () {
this.setState({sites: this.state.visited});
});
} else if (this.state.sortSelected == 'most-recent') {
this.setState({sites: []}, function () {
this.setState({sites: this.state.recent});
});
}
} else {
this.getSites(this.state.sortSelected, Number(e.target.value));
this.setState({sites: []}, function () {
this.setState({sites: this.state.filter_sites});
});
}
And lastly, the dropdown menu:
<select
value={this.state.topicSelected}
onChange={this.onTopicClick}
className="sort"
data-element>
{
// Add this option in the .then() when populating siteCategories()
[<option key='0'value='0'>Topics</option>].concat(
this.state.topics.map(function (topic) {
return (<option
key={topic.id}
value={topic.id}>{topic.name}</option>);
}))
}
How do I get it so that this.state.topicSelected doesn't get reset when a user goes back to the main page?
I think your main page is getting unmounted (destroyed) when the user navigates from the main page to the summary page. React creates a brand new instance of the main page component when they return. That reconstruction initializes the selected topic back to 0.
Here is a codepen that shows what might be happening. Foo == your main page, Bar == summary page. Click Increment topic a couple times, then navigate from Foo to Bar and back again. Console logs show that the Foo component gets unmounted when you navigate away, and reconstructed on return.
Note You seem to be using an older version of react, as evidenced by the presence of getInitialState and React.createClass. My pen follows the more modern approach of initializing state in the class constructor.
To solve the problem, you have to save that state outside the main component in something that isn't getting deleted and re-created as they navigate. Here are some choices for doing that
Expose an onTopicSelected event from your main page. The parent of the main page would pass in a function handler as a prop to hook that event. The handler should save the selected topic in the component state of the parent. This is kind of messy solution because the parent usually should not know or care about the internal state of its children
Stuff the selected topic into something that isn't react, like window.props. This is an ugly idea as well.
Learn about redux and plug it into your app.
Redux is the cleanest way to store this state, but it would require a bit of learning. I have implemented the first solution in this codepen if you want to see what it would look like.
The original codepen showing the problem is pasted below as a snippet. Switch to Full page mode if you try to run it here.
//jshint esnext:true
const Header = (props) => {
const {selectedPage, onNavigationChange} = props;
const disableFoo = selectedPage == 'foo'
const disableBar = selectedPage == 'bar';
return (
<div>
<h1>Header Component : {selectedPage}</h1>
<button disabled={disableFoo} onClick={() => onNavigationChange('foo')}>Foo page</button>
<button disabled={disableBar} onClick={() => onNavigationChange('bar')}>Bar page</button>
<hr/>
</div>
);
};
class Foo extends React.Component {
constructor(props) {
console.log('Foo constructor');
super(props);
this.state = {
topicSelected: 0
};
this.incrementTopic = this.incrementTopic.bind(this);
}
incrementTopic() {
const {topicSelected} = this.state
const newTopic = topicSelected + 1
console.log(`incrementing topic: old=${topicSelected} new=${newTopic}`)
this.setState({
topicSelected: newTopic
})
}
render() {
console.log('Foo::render');
return (<div>
<h2>The Foo content page : topicSelected={this.state.topicSelected}</h2>
<button onClick={this.incrementTopic}>Increment topic</button>
</div>
);
}
componentWillMount() {
console.log('Foo::componentWillMount');
}
componentDidMount() {
console.log('Foo::componentDidMount');
}
componentWillUnmount() {
console.log('Foo::componentWillUnmount');
}
componentWillUpdate() {
console.log('Foo::componentWillUpdate');
}
componentDidUpdate() {
console.log('Foo::componentDidUpdate');
}
}
const Bar = (props) => {
console.log('Bar::render');
return <h2>The Bar content page</h2>
}
const Body = (props) => {
console.log('Body::render');
const {selectedPage} = props;
if (selectedPage == 'foo') {
return <Foo/>;
} else if (selectedPage == 'bar') {
return <Bar/>
}
};
class Application extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedPage: 'foo'
};
}
render() {
console.log('Application::render');
const {selectedPage} = this.state;
const navigationChange = (nextPage) => {
this.setState({
selectedPage: nextPage
})
}
return (
<div>
<Header selectedPage={selectedPage} onNavigationChange={navigationChange}/>
<Body selectedPage={selectedPage}/>
</div>
);
}
}
ReactDOM.render(<Application/>,
document.getElementById('main')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="main"></div>

Get meteor to unsubscribe from data

I'm not sure if this is possible due to the way meteor works. I'm trying to figure out how to unsubscribe and subscribe to collections and have the data removed from mini mongo on the client side when the user clicks a button. The problem I have in the example below is that when a user clicks the handleButtonAllCompanies all the data is delivered to the client and then if they click handleButtonTop100 it doesn't resubscribe. So the data on the client side doesn't change. Is it possible to do this?
Path: CompaniesPage.jsx
export default class CompaniesPage extends Component {
constructor(props) {
super(props);
this.handleButtonAllCompanies = this.handleButtonAllCompanies.bind(this);
this.handleButtonTop100 = this.handleButtonTop100.bind(this);
}
handleButtonAllCompanies(event) {
event.preventDefault();
Meteor.subscribe('companiesAll');
}
handleButtonTop100(event) {
event.preventDefault();
Meteor.subscribe('companiesTop100');
}
render() {
// console.log(1, this.props.companiesASX);
return (
<div>
<Button color="primary" onClick={this.handleButtonAllCompanies}>All</Button>
<Button color="primary" onClick={this.handleButtonTop100}>Top 100</Button>
</div>
);
}
}
Path: Publish.js
Meteor.publish('admin.handleButtonAllCompanies', function() {
return CompaniesASX.find({});
});
Meteor.publish('admin.handleButtonTop100', function() {
return CompaniesASX.find({}, { limit: 100});
});
This is definitely possible, but the way to do that is to fix your publication. You want to think MVC, i.e., separate as much as possible the data and mode from the way you are going to present it. This means that you should not maintain two publications of the same collection, for two specific purposes. Rather you want to reuse the same publication, but just change the parameters as needed.
Meteor.publish('companies', function(limit) {
if (limit) {
return CompaniesASX.find({}, { limit });
} else {
return CompaniesASX.find({});
}
});
Then you can define your button handlers as:
handleButtonAllCompanies(event) {
event.preventDefault();
Meteor.subscribe('companies');
}
handleButtonTop100(event) {
event.preventDefault();
Meteor.subscribe('companies', 100);
}
This way you are changing an existing subscription, rather than creating a new one.
I'm not yet super familiar with react in meteor. But in blaze you wouldn't even need to re-subscribe. You would just provide a reactive variable as the subscription parameter and whenever that would change, meteor would update the subscription reactively. The same may be possible in react, but I'm not sure how.
Ok, first thanks to #Christian Fritz, his suggestion got me onto the right track. I hope this helps someone else.
I didn't realise that subscriptions should be controlled within the Container component, not in the page component. By using Session.set and Session.get I'm able to update the Container component which updates the subscription.
This works (if there is a better or more meteor way please post) and I hope this helps others if they come across a similar problem.
Path CompaniesContainer.js
export default UploadCSVFileContainer = withTracker(({ match }) => {
const limit = Session.get('limit');
const companiesHandle = Meteor.subscribe('companies', limit);
const companiesLoading = !companiesHandle.ready();
const companies = Companies.find().fetch();
const companiesExists = !companiesLoading && !!companies;
return {
companiesLoading,
companies,
companiesExists,
showCompanies: companiesExists ? Companies.find().fetch() : []
};
})(UploadCSVFilePage);
Path: CompaniesPage.jsx
export default class CompaniesPage extends Component {
constructor(props) {
super(props);
this.handleButtonAllCompanies = this.handleButtonAllCompanies.bind(this);
this.handleButtonTop100 = this.handleButtonTop100.bind(this);
}
handleButtonAllCompanies(event) {
event.preventDefault();
// mongodb limit value of 0 is equivalent to setting no limit
Session.set('limit', 0)
}
handleButtonTop100(event) {
event.preventDefault();
Session.set('limit', 100)
}
render() {
return (
<div>
<Button color="primary" onClick={this.handleButtonAllCompanies}>All</Button>
<Button color="primary" onClick={this.handleButtonTop100}>Top 100</Button>
</div>
);
}
}
Path: Publish.js
Meteor.publish('companies', function() {
if (limit || limit === 0) {
return Companies.find({}, { limit: limit });
}
});
Path CompaniesContainer.js
export default CompaniesContainer = withTracker(() => {
let companiesHandle; // or fire subscribe here if you want the data to be loaded initially
const getCompanies = (limit) => {
companiesHandle = Meteor.subscribe('companies', limit);
}
return {
getCompanies,
companiesLoading: !companiesHandle.ready(),
companies: Companies.find().fetch(),
};
})(CompaniesPage);
Path: CompaniesPage.jsx
export default class CompaniesPage extends Component {
constructor(props) {
super(props);
this.handleButtonAllCompanies = this.handleButtonAllCompanies.bind(this);
this.handleButtonTop100 = this.handleButtonTop100.bind(this);
}
getCompanies(limit) {
this.props.getCompanies(limit);
}
handleButtonAllCompanies(event) {
event.preventDefault();
// mongodb limit value of 0 is equivalent to setting no limit
this.getCompanies(0);
}
handleButtonTop100(event) {
event.preventDefault();
this.getCompanies(100);
}
render() {
return (
<div>
<Button color="primary" onClick={this.handleButtonAllCompanies}>All</Button>
<Button color="primary" onClick={this.handleButtonTop100}>Top 100</Button>
</div>
);
}
}
Path: Publish.js
Meteor.publish('companies', function(limit) {
if (!limit) { limit = 0; }
return Companies.find({}, { limit: limit });
});

How to listen for click events that are outside of a component

I want to close a dropdown menu when a click occurs outside of the dropdown component.
How do I do that?
Using the life-cycle methods add and remove event listeners to the document.
React.createClass({
handleClick: function (e) {
if (this.getDOMNode().contains(e.target)) {
return;
}
},
componentWillMount: function () {
document.addEventListener('click', this.handleClick, false);
},
componentWillUnmount: function () {
document.removeEventListener('click', this.handleClick, false);
}
});
Check out lines 48-54 of this component: https://github.com/i-like-robots/react-tube-tracker/blob/91dc0129a1f6077bef57ea4ad9a860be0c600e9d/app/component/tube-tracker.jsx#L48-54
In the element I have added mousedown and mouseup like this:
onMouseDown={this.props.onMouseDown} onMouseUp={this.props.onMouseUp}
Then in the parent I do this:
componentDidMount: function () {
window.addEventListener('mousedown', this.pageClick, false);
},
pageClick: function (e) {
if (this.mouseIsDownOnCalendar) {
return;
}
this.setState({
showCal: false
});
},
mouseDownHandler: function () {
this.mouseIsDownOnCalendar = true;
},
mouseUpHandler: function () {
this.mouseIsDownOnCalendar = false;
}
The showCal is a boolean that when true shows in my case a calendar and false hides it.
Look at the target of the event, if the event was directly on the component, or children of that component, then the click was inside. Otherwise it was outside.
React.createClass({
clickDocument: function(e) {
var component = React.findDOMNode(this.refs.component);
if (e.target == component || $(component).has(e.target).length) {
// Inside of the component.
} else {
// Outside of the component.
}
},
componentDidMount: function() {
$(document).bind('click', this.clickDocument);
},
componentWillUnmount: function() {
$(document).unbind('click', this.clickDocument);
},
render: function() {
return (
<div ref='component'>
...
</div>
)
}
});
If this is to be used in many components, it is nicer with a mixin:
var ClickMixin = {
_clickDocument: function (e) {
var component = React.findDOMNode(this.refs.component);
if (e.target == component || $(component).has(e.target).length) {
this.clickInside(e);
} else {
this.clickOutside(e);
}
},
componentDidMount: function () {
$(document).bind('click', this._clickDocument);
},
componentWillUnmount: function () {
$(document).unbind('click', this._clickDocument);
},
}
See example here: https://jsfiddle.net/0Lshs7mg/1/
For your specific use case, the currently accepted answer is a tad over-engineered. If you want to listen for when a user clicks out of a dropdown list, simply use a <select> component as the parent element and attach an onBlur handler to it.
The only drawbacks to this approach is that it assumes the user has already maintained focus on the element, and it relies on a form control (which may or may not be what you want if you take into account that the tab key also focuses and blurs elements) - but these drawbacks are only really a limit for more complicated use cases, in which case a more complicated solution might be necessary.
var Dropdown = React.createClass({
handleBlur: function(e) {
// do something when user clicks outside of this element
},
render: function() {
return (
<select onBlur={this.handleBlur}>
...
</select>
);
}
});
I have written a generic event handler for events that originate outside of the component, react-outside-event.
The implementation itself is simple:
When component is mounted, an event handler is attached to the window object.
When an event occurs, the component checks whether the event originates from within the component. If it does not, then it triggers onOutsideEvent on the target component.
When component is unmounted, the event handler is detacthed.
import React from 'react';
import ReactDOM from 'react-dom';
/**
* #param {ReactClass} Target The component that defines `onOutsideEvent` handler.
* #param {String[]} supportedEvents A list of valid DOM event names. Default: ['mousedown'].
* #return {ReactClass}
*/
export default (Target, supportedEvents = ['mousedown']) => {
return class ReactOutsideEvent extends React.Component {
componentDidMount = () => {
if (!this.refs.target.onOutsideEvent) {
throw new Error('Component does not defined "onOutsideEvent" method.');
}
supportedEvents.forEach((eventName) => {
window.addEventListener(eventName, this.handleEvent, false);
});
};
componentWillUnmount = () => {
supportedEvents.forEach((eventName) => {
window.removeEventListener(eventName, this.handleEvent, false);
});
};
handleEvent = (event) => {
let target,
targetElement,
isInside,
isOutside;
target = this.refs.target;
targetElement = ReactDOM.findDOMNode(target);
isInside = targetElement.contains(event.target) || targetElement === event.target;
isOutside = !isInside;
if (isOutside) {
target.onOutsideEvent(event);
}
};
render() {
return <Target ref='target' {... this.props} />;
}
}
};
To use the component, you need wrap the target component class declaration using the higher order component and define the events that you want to handle:
import React from 'react';
import ReactDOM from 'react-dom';
import ReactOutsideEvent from 'react-outside-event';
class Player extends React.Component {
onOutsideEvent = (event) => {
if (event.type === 'mousedown') {
} else if (event.type === 'mouseup') {
}
}
render () {
return <div>Hello, World!</div>;
}
}
export default ReactOutsideEvent(Player, ['mousedown', 'mouseup']);
I voted up one of the answers even though it didn't work for me. It ended up leading me to this solution. I changed the order of operations slightly. I listen for mouseDown on the target and mouseUp on the target. If either of those return TRUE, we don't close the modal. As soon as a click is registered, anywhere, those two booleans { mouseDownOnModal, mouseUpOnModal } are set back to false.
componentDidMount() {
document.addEventListener('click', this._handlePageClick);
},
componentWillUnmount() {
document.removeEventListener('click', this._handlePageClick);
},
_handlePageClick(e) {
var wasDown = this.mouseDownOnModal;
var wasUp = this.mouseUpOnModal;
this.mouseDownOnModal = false;
this.mouseUpOnModal = false;
if (!wasDown && !wasUp)
this.close();
},
_handleMouseDown() {
this.mouseDownOnModal = true;
},
_handleMouseUp() {
this.mouseUpOnModal = true;
},
render() {
return (
<Modal onMouseDown={this._handleMouseDown} >
onMouseUp={this._handleMouseUp}
{/* other_content_here */}
</Modal>
);
}
This has the advantage that all the code rests with the child component, and not the parent. It means that there's no boilerplate code to copy when reusing this component.
Create a fixed layer that spans the whole screen (.backdrop).
Have the target element (.target) outside the .backdrop element and with a greater stacking index (z-index).
Then any click on the .backdrop element will be considered "outside of the .target element".
.click-overlay {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 1;
}
.target {
position: relative;
z-index: 2;
}
Here is an example: http://jsfiddle.net/LHmwd/
More on the discussion: https://github.com/facebook/react/issues/579
You could use refs to achieve this, something like the following should work.
Add the ref to your element:
<div ref={(element) => { this.myElement = element; }}></div>
You can then add a function for handling the click outside of the element like so:
handleClickOutside(e) {
if (!this.myElement.contains(e)) {
this.setState({ myElementVisibility: false });
}
}
Then finally, add and remove the event listeners on will mount and will unmount.
componentWillMount() {
document.addEventListener('click', this.handleClickOutside, false); // assuming that you already did .bind(this) in constructor
}
componentWillUnmount() {
document.removeEventListener('click', this.handleClickOutside, false); // assuming that you already did .bind(this) in constructor
}
Super late to the party, but I've had success with setting a blur event on the parent element of the dropdown with the associated code to close the dropdown, and also attaching a mousedown listener to the parent element that checks if the dropdown is open or not, and will stop the event propagation if it is open so that the blur event won't be triggered.
Since the mousedown event bubbles up this will prevent any mousedown on children from causing a blur on the parent.
/* Some react component */
...
showFoo = () => this.setState({ showFoo: true });
hideFoo = () => this.setState({ showFoo: false });
clicked = e => {
if (!this.state.showFoo) {
this.showFoo();
return;
}
e.preventDefault()
e.stopPropagation()
}
render() {
return (
<div
onFocus={this.showFoo}
onBlur={this.hideFoo}
onMouseDown={this.clicked}
>
{this.state.showFoo ? <FooComponent /> : null}
</div>
)
}
...
e.preventDefault() shouldn't have to be called as far as I can reason but firefox doesn't play nice without it for whatever reason. Works on Chrome, Firefox, and Safari.
I found a simpler way about this.
You just need to add onHide(this.closeFunction) on the modal
<Modal onHide={this.closeFunction}>
...
</Modal>
Assuming you have a function to close the modal.
Use the excellent react-onclickoutside mixin:
npm install --save react-onclickoutside
And then
var Component = React.createClass({
mixins: [
require('react-onclickoutside')
],
handleClickOutside: function(evt) {
// ...handling code goes here...
}
});

Resources