HandleClick inside and outside of DOM Element - reactjs

I want to close the menu when there's a click on <Burger /> and when there's a click outside <Burger />. With my current code only the click outside <Burger /> will be handled, but the click on <Burger /> will not close the menu.
When I reverse domNode to !domNode in the handleClickOutside function, clicking inside <Burger /> works, but clicking outside <Burger /> will no longer close the menu. Also I tried adding other conditions to the handleClickOutside function and that did not work also.
What am I missing?
Thanks!
export class NavBar extends React.Component {
constructor(props) {
super(props);
this.state = {
change: false
};
this.handleClick = this.handleClick.bind(this);
this.handleClickOutside = this.handleClickOutside.bind(this);
}
componentDidMount() {
document.addEventListener('click', this.handleClickOutside, true);
}
componentWillUnmount(){
document.removeEventListener('click', this.handleClickOutside, true)
}
handleClick() {
this.setState(state => ({
change: !state.change
}));
}
handleClickOutside(event) {
const domNode = ReactDOM.findDOMNode(this);
if(domNode || !domNode.contains(event.target)){
this.setState(state=> ({
change: false
}))
}
}
render() {
return (
<div>
<div className="mobile">
<Menu change={this.state.change} onClickOutside={this.handleClickOutside} />
<nav>
<Burger change={this.state.change} onClick={this.handleClick} />
</nav>
</div>
</div>
)
}
}
<Burger />
export class Burger extends React.Component {
render(){
return(
<a className={classnames('burger', {'change': this.props.change})} onClick={this.props.onClick}>
<div/>
<div/>
<div/>
</a>
)
}
}
<Menu />
export class Menu extends React.Component {
render(){
return(
<div className={classnames({change: !this.props.change})}>
<div className="menu" onClick={this.props.onClickOutside}>
<Link>
</Link>
<Link>
</Link>
</div>
</div>
)
}
}

I can't be sure since i haven't all your code, but I have a guess.
You added an event listener in componentDidMount:
componentDidMount() {
document.addEventListener('click', this.handleClickOutside, true);
}
With this code, depending on how Burger handle its onClick props, this may happen:
You click inside the Burger; its onClick is triggered which calls the props.onClick and thus you call the handleClick of NavBar;
Then, since the event propagates, it reaches the handleClickOutside, and that's because the event has reached the document. So, since you call handleClickOutside, inside of it you switch again the state.
Again, this is all a guess I'm having. If you wanna test this you can use the debug and notice if, when clicking inside the Burger, you reach both onClick and handleClickOutside.
Anyways, it would be appreciated the code of both Burger and Menu because we cannot know what is happening in there.

Related

How to set focus on button in componentDidMount

i have this component
class XY extends Component {
constructor(props) {
super(props)
this.state = {
open: false
}
}
componentDidMount() {
if(this.state.open === false && this.props.data.active === 1) {
this.button.focus()
}
}
render() {
return (
<div>
<button ref={c => (this.button = c)}>
button
</button>
</div>
)
}
}
I need to set focus on rendered button after component mounts and under some conditions, but it doesn't work for some reason. I tried to set it up in componentDidUpdate and it worked but not on first render obv. Is there anything I've done wrong?
Thanks for help
You need to use refs callback to focus on input button.
class XY extends React.Component{
componentDidMount(){
this.focusInput.focus();
}
render() {
return(
<div>
<input type="button"
ref={(input) => { this.focusInput = input; }}
defaultValue="button"
/>
</div>
);
}
}
ReactDOM.render(<XY />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.js"></script>
<div id="root"></div>

Calling an onclick event from a different Component

I am desperatly trying to make my code works between 2 components by using the onclick event of my modal component to my Avaibalities component but nothing happens.
How can i make the value of my state ShowModal works?
Avaibalities Component
import React from 'react';
import Calendar from 'react-calendar';
import Modal from '../pages/Modal';
class Avaibalities extends React.Component {
state = {
date: new Date(),
showDate: false,
showModal: false,
};
onChange = (date) => {
this.setState({ date });
};
validation = () => {
this.setState({
showDate: true,
});
};
togglePop = () => {
this.setState({
showModal: true
});
};
render() {
return (
<div>
<div className="home">
<div className="calendarContent">
<div>
<Calendar
onChange={this.onChange}
value={this.state.date}
locale="en-GB"
/>
<>
<button className={'button'}>Validate</button>
<div>
{this.state.showModal ? (
<Modal toggle={this.togglePop} />
) : null}
</>
{this.state.showDate ? (
<div>
<p>
From : {this.state.date[0].toLocaleDateString()} to :{' '}
{this.state.date[1].toLocaleDateString()}
</p>
</div>
) : null}
</div>
</div>
</div>
);
}
}
export default Avaibalities;
Modal Component
import React from 'react';
class Modal extends React.Component {
handleClick = () => {
this.props.toggle();
};
render() {
return (
<div className="modal">
<div className="modal_content">
<span className="close" onClick={this.handleClick}>×</span>
<p>I'm A Pop Up!!!</p>
</div>
</div>
);
}
}
export default Modal;
Looking for someone to help me,
Thank you very much
You do not render your modal in this code because I see that showModal initially false and to set it as true you call tooglePop function as a props in Modal. But render of Modal component depends on showModal state so it never renders because initially false
You can't expect something to happen here.
To display your modal, you need this.state.showModal = true. In your modal, you are setting showModal to true but it's not different than te previous state, so nothing happens.
Change your method as follow :
togglePop = () => {
this.setState(prevState => ({
...prevState,
showModal: !prevState.showModal
}));
};

React: Open Modal from a different component

I have a Modal component in the Main.js app and I want to trigger it from a different component (in this case Homepage, but I have one component for each page).
I don´t know how to pass a component to be rendered inside the modal.
If it helps I´m using Context API.
App.js
const App = () => {
return (
<div>this is the main app</div>
<Modal />
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Modal.js
class Modal extends Component {
constructor(props) {
super(props)
this.state = {
'open': false
}
}
componentWillReceiveProps(nextProps) {
this.setState({open: nextProps.open})
}
render() {
if (!this.state.open) {
return false
}
return(
<div className="modal">
{this.props.children}
</div>
)
}
}
export default Modal
Homepage.js
class Homepage extends Component {
constructor(props) {
super(props)
this.handleOpenModal = this.handleOpenModal.bind(this)
}
handleOpenModal() {
// here I want to open the modal and pass the <ModalContent /> component
// basically call the <Modal open="true"> from the Main component
}
render() {
return(
<div className="homepage">
<button onClick={this.handleOpenModal}>open the modal</button>
</div>
)
}
}
const ModalContent = () => {
return(
<div>this is the content I want to render inside the modal</div>
)
}
thank you.
I strongly recommend using something like react-modal (npm). It allows you to keep modal content right next to the trigger. It does this by appending a "portal" high up in the DOM and handles appending the content to is.
Your example may look like the following:
import Modal from 'react-modal';
class Homepage extends Component {
constructor(props) {
super(props)
this.state = { modalOpen: false };
this.handleOpenModal = this.handleOpenModal.bind(this)
}
handleOpenModal() {
this.setState({ modalOpen: true });
}
render() {
return(
<div className="homepage">
<button onClick={this.handleOpenModal}>open the modal</button>
<Modal open={this.state.modalOpen}>
<ModalContent />
</Modal>
</div>
)
}
}
const ModalContent = () => {
return(
<div>this is the content I want to render inside the modal</div>
)
}

How can I render the same modal component into a list array itens in React?

I need to render a modal/lightbox component dynamic into a list array component, but it only renders the last modal content.
How can I turn this modal component dynamic to call it from the main component and populate it with correct data from an object array?
My List component is:
import React, { Component } from 'react';
import LightBox from './LightBox';
class ListPrice extends Component {
constructor(props) {
super(props);
this.state = { isOpen: false };
}
toggleModal = () => {
this.setState({
isOpen: !this.state.isOpen
});
}
render() {
return (
<div>
{this.props.products.map(product => {
return(
<div>
<a key={product.id} onClick={this.toggleModal}>
<h3>{product.title}</h3>
<p>{product.description}</p>
</a>
<LightBox key={product.id} show={this.state.isOpen}
onClose={this.toggleModal}>
{product.modalContent}
</LightBox>
</div>
);
})}
</div>
);
}
}
export default ListPrice;
And my LightBox component is (I removed styles to display short code here):
import React from 'react';
import PropTypes from 'prop-types';
class LightBox extends React.Component {
render() {
if(!this.props.show) {
return null;
}
return (
<div>
<div>
{this.props.children}
<div>
<button onClick={this.props.onClose}>
Close
</button>
</div>
</div>
</div>
);
}
}
LightBox.propTypes = {
onClose: PropTypes.func.isRequired,
show: PropTypes.bool,
children: PropTypes.node
};
export default LightBox;
Thank you for any advice :)
With show={this.state.isOpen} you always display all the modals - only the last one is visible as other modals are displayed behind it.
In order to fix that you must show only the selected dialog. You can store opened dialog in state with construct like this.setState({ openedDialog: product.id }).
Then you can query if the dialog is open by using this.state.openedDialog === product.id. That should do the job.
openModal = (id) = () => {
this.setState({
openedDialog: id
});
}
closeModal = () => {
this.setState({
openedDialog: null
});
}
show={this.state.openedDialog === product.id}
onClick={this.openModal(product.id)}
onClose={this.closeModal}

How to access component function in a component in reactjs

I have different jsx files.I want to access Menu.jsx component function in Header.jsx function to open menu. I am using Material-UI also. So Here I have a function "handleToggle" in Menu.jsx and I want to trigger this function from a button "onLeftIconButtonTouchTap" which is available in Header.jsx. How can I access component internal function from any other component, should I need to maintain any hierarchy?
App.jsx
export default class App extends React.Component{
render(){
return(
<main>
<Menu/>
<Header/>
<Body/>
<Footer/>
</main>
)
}
}
Header.jsx
export default class Header extends BaseMUI{
render(){
return (
<header>
<AppBar
title="title"
onLeftIconButtonTouchTap={this.handleToggle}
iconClassNameRight="muidocs-icon-navigation-expand-more"
/>
</header>
)
}
}
Menu.jsx
export default class Menu extends BaseMUI{
constructor(props) {
super(props);
this.state = {
open: false
};
}
handleToggle = () => this.setState({open: !this.state.open});
handleClose = () => this.setState({open: false});
componentDidMount(){
console.log(this.refs);
}
render(){
return (
<nav>
<RaisedButton
label="Open Drawer"
onTouchTap={this.handleToggle}/>
<Drawer
docked={false}
width={200}
open={this.state.open}
ref="drawer"
onRequestChange={(open) => this.setState({open})}>
<MenuItem onTouchTap={this.handleClose}>Menu Item</MenuItem>
<MenuItem onTouchTap={this.handleClose}>Menu Item 2</MenuItem>
</Drawer>
</nav>
)
}
}
You need to create parent component, store there state of opening menu, and change props of the menu - here an example, how it can be implemented in only react, without Flux and Redux, will be more right to use them, if you have many idential situations.
class MenuContainer extends React.Component {
constructor(props) {
super(props)
this.state = {
isMenuOpen = false
}
}
shouldComponentUpdate(newProps, newState) {
return newState.isMenuOpen != this.state.isMenuOpen
}
openMenu = (isMenuOpen ) => {
this.setState({isMenuOpen : isMenuOpen });
}
render () {
return (
<Header : isMenuOpen={this.state.isMenuOpen}
openMenu= {this.openMenu}/>
<Menu isMenuOpen={this.state.isMenuOpen})
}
}
And then in your menu component
shouldComponentUpdate(newProps) {
return this.props.isMenuOpen != newProps.isMenuOpen
}
Since you Header.jxs and Menu.jsx are in same hierarchy, you can solve in 2 ways...
1) you can fire a event from Header.jxs and listen for the action in Menu.jsx
2) Using react-redux way, fire the action on click and observer the props changes in Menu.jsx

Resources