Currently, I'm making a modal system with React.
So I have a button in a sidebar and my modal is a div in the body.
I have two component that are rendered in the render function of the main component of my app:
MyModal
MyModalOpenButton
Here is what I need:
When the event onClick of the MyModalOpenButton is triggered, I need to execute a function stored in the MyModal component.
So I basically need to store the ref of MyModal in a var.
How can I do this ?
Codes:
import * as ReactDOM from "react-dom";
import * as React from "react";
import { MyModal } from "./component/modal/my-modal";
import { SettingsButtons } from "./component/settings-buttons";
import { CustomComponent } from "./custom-component";
class App extends CustomComponent<{}, {}>{
constructor() {
super();
}
render() {
return (
<div>
<div className="sidebar">
<ul>
<li>
1
</li>
<li>
2
</li>
<li>
3
</li>
</ul>
<SettingsButtons />
</div>
<div className="mainContent">
</div>
<footer>TODO</footer>
<MyModal />
</div>
);
}
}
const app: App = ReactDOM.render(<App />, document.querySelector("#root")) as App;
And
import * as ReactDOM from "react-dom";
import * as React from "react";
import { CustomComponent } from "./../../custom-component";
import { ButtonComponent } from "./button";
export class MyModalOpenButton extends ButtonComponent<{}, {}> {
constructor() {
super();
}
render() {
return (
<li id="open-button" onClick={this.onClick}>
<i className="fa fa-plus" aria-hidden="true"></i>
</li>
);
}
onClick(event: React.MouseEvent<HTMLElement>) {
// Should open the modal
}
}
You haven't shared your code, so I can't give you an answer that will match your case specifically, but in general:
class MyModal extends React.Component<any, any> {
public render() {
...
}
public onButtonClick() {
...
}
}
interface MyModalOpenButtonProps {
onClick: () => void;
}
class MyModalOpenButton extends React.Component<MyModalOpenButtonProps, any> {
public render() {
return <button onClick={ this.props.onClick } />
}
}
class Main extends React.Component<any, any> {
private myModal: MyModal;
public render() {
return (
<div>
<MyModalOpenButton onClick={ this.onButtonClick.bind(this) } />
<MyModal ref={ modal => this.myModal = modal } />
</div>
);
}
private onButtonClick() {
this.myModal.onButtonClick();
}
}
Related
I am trying to display/hide one component which is ItemMain and which is imported to the main App component using button in another component which is NavLogoNew. I tried to do this in many different ways but it looks like the button doesn't know if it's clicked, when I change true/false manually it works. In web I found a lot of stuff about situations when only two components are involved, but nothing like this. My code:
App
import React from 'react';
import './App.css';
import { tsPropertySignature } from '#babel/types';
import { statement } from '#babel/template';
import NavBar from './../Components/Navigation/NavBar/NavBar.js';
import ItemMain from './../Components/Item/ItemMain/ItemMain.js';
import ItemList from './../Components/Item/ItemList/ItemList.js';
import NavButtonTop from './../Components/Navigation/NavButton/NavButtonTop/NavButtonTop.js';
import NavLogoNew from './../Components/Navigation/NavButton/NavButtonNew/NavLogoNew.js';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
visible: !this.visible
})
}
render() {
return (
<div className="App">
<NavBar />
{this.state.visible ? <ItemMain /> : null}
<ItemList />
<NavButtonTop name='UP'/>
</div>
);
}
}
export default App;
NavLogoNew:
import React from 'react';
import './NavLogoNew.css';
import ItemMain from './../../../Item/ItemMain/ItemMain.js'
class NavLogoNew extends React.Component {
render() {
return (
<button
className='NavLogoNew'
onClick={this.props.click}
>
{this.props.name}
</button>
);
}
}
export default NavLogoNew;
Your handleClick function is lacking something
use !this.state.visible so change from the below
handleClick(){
this.setState({
visible: !this.visible
})
}
to
handleClick = () => {
this.setState({
visible: !this.state.visible
})
}
pass the handleClick function to the NavLogoNew as follows
<NavLogoNew onClick = {this.handleClick} />
inside of the NavLogoNew component you should invoke it as follows
class NavLogoNew extends React.Component {
render() {
return (
<button
className='NavLogoNew'
onClick={() => this.props.onClick()}
>
{this.props.name}
</button>
);
}
}
I have a component :
import React, { Component } from 'react';
import ImageItem from '../components/ImageItem';
class ImageList extends Component {
handleClick() {
console.log('Testing testing...'); // ---> This is not working.
}
render() {
const images = this.props.images.map(image => {
return (
<ImageItem
onClick={this.handleClick}
key={image.id}
image={image.src}
title={image.title}
/>
);
});
return (
<div className="image-list" ref={el => (this.el = el)}>
{images}
</div>
);
}
}
export default ImageList;
However, my onClick is not console logging anything out when it is inside the mapped function.
This is my ImageItem component:
import React, { Component } from 'react';
class ImageItem extends Component {
render() {
return (
<a href="#">
<img
className="portfolio-image"
src={this.props.image}
alt={this.props.title}
/>
</a>
);
}
}
export default ImageItem;
What am I doing wrong here?
You are not assigning the click handler to your component it should look like this :
class ImageItem extends Component {
render() {
return (
<a href="#" onClick={this.props.onClick}>
<img
className="portfolio-image"
src={this.props.image}
alt={this.props.title}
/>
</a>
);
}
}
export default ImageItem;
i want to change state different js but i can not , i have a sidebar.js with react-burger-menu
i want to call and change toggleMenu state in header.js
When I click the menu link, i want to toggle react-burger-menu but different js. this is not working.
sidebar.js
import React from "react";
import PropTypes from "prop-types";
import { reveal as Menu } from "react-burger-menu";
import * as FontAwesome from "react-icons/lib/fa";
export default class SidebarMenu extends React.Component {
constructor (props) {
super(props)
this.state = {
menuOpen: false
}
}
handleStateChange (state) {
this.setState({menuOpen: state.isOpen})
}
closeMenu () {
this.setState({menuOpen: false})
}
toggleMenu () {
this.setState({menuOpen: !this.state.menuOpen})
}
render () {
return (
<div>
<Menu
isOpen={this.state.menuOpen}
onStateChange={(state) => this.handleStateChange(state)}
>
// menu content
</Menu>
</div>
</div>
)
}
}
header.js have link for react-burger-menu
import React from 'react';
import PropTypes from 'prop-types';
import SidebarMenu from "../SidebarMenu";
export default class Header_Video extends React.Component {
render() {
return (
<Container>
<Row>
<Col md={5} sm={12} xs={12} className="text-left mobile-right">
<div className="bar__module">
<a onClick={this.toggleMenu}>Menu</a>
</div>
</Col>
</Row>
</Container>
);
}
}
thanks for help
note: i have a app.js all files import. I want to run toggleMenu in header.js
app.js
const TemplateWrapper = ({ children }) => (
<div id="outer-container">
<SidebarMenu />
<main id="page-wrap" className="page-wrap">
<HeaderVideo /> {children()}
<Footer />
</main>
</div>
);
menuOpen should be in a parent state of both components.
Example:
class App extends React.Component {
constructor (props) {
super(props)
this.state = {
menuOpen: false
}
}
closeMenu = () => {
this.setState({menuOpen: false})
}
toggleMenu = () => {
this.setState({menuOpen: !this.state.menuOpen})
}
render() {
return (
<div>
<SidebarMenu isMenuOpen={this.state.menuOpen} toggleMenu={this.toggleMenu} />
<Header toggleMenu={this.toggleMenu} />
</div>
)
}
}
You may have further errors beyond just this, but the glaring error to me is that toggleMenu() is not bound to the constructor.
https://reactjs.org/docs/handling-events.html
try:
import React from "react";
import PropTypes from "prop-types";
import { reveal as Menu } from "react-burger-menu";
import * as FontAwesome from "react-icons/lib/fa";
export default class SidebarMenu extends React.Component {
constructor (props) {
super(props)
this.state = {
menuOpen: false
}
this.toggleMenu = this.toggleMenu.bind(this);
// the above statement binds the function to the object constructor
}
handleStateChange (state) {
this.setState({menuOpen: state.isOpen})
}
closeMenu () {
this.setState({menuOpen: false})
}
toggleMenu () {
this.setState({menuOpen: !this.state.menuOpen})
}
render () {
return (
<div>
<Menu
isOpen={this.state.menuOpen}
onStateChange={(state) => this.handleStateChange(state)}
>
// menu content
</Menu>
</div>
</div>
)
}
}
You'll also want to use an HTML5 button tag instead of a link tag, the correct HTML semantic structure provides a bunch of underlying features and greatly improves accessibility out of the box.
Also, remove the arrow function and pass a reference to the function, not the returned value. This is so react doesn't call the function immediately but stores the function reference to execute upon the click event.
<button onClick={this.toggleMenu}>Menu</button>
// instead of
<a onClick={() => this.toggleMenu()}>Menu</a>
Hope this helps!
I'm currently working on a react application. I've a top bar with button and I want to render Registration component to the onClick function. There's no output with the below, I'm I missing something here?
'use strict'
import React from 'react'
import Registration from 'Registration'
export default class Header extends React.Component {
constructor (props) {
super (props)
this.handleClick = this.handleClick.bind(this)
}
handleClick () {
<Registration />
}
render () {
return (
<div className='top-bar'>
<div className='top-bar-left'>
<ul className='menu'>
<li className='menu-text'>SIS App</li>
<li>
<button className='button' type='button' onClick={this.handleClick}>Create Info</button>
</li>
</ul>
</div>
</div
)
}
}
Here is my Registration component:
'use strict'
import React, {Component} from 'react'
export default class Registration extends Component {
render () {
let props = this.props
let {edit} = props
let handleChange = props.handleChange()
return (
<fieldset>
<legend>Registration</legend>
<input name='name'
value={props['name']}
type='text'
placeholder='Your Name'
onChange={handleChange}
/>
<input name='email'
value={props['email']}
type='email'
placeholder='Email'
onChange={handleChange}
/>
<input name='phone'
value={props['phone']}
type='tel'
pattern={PhoneNumberPattern}
placeholder='Phone'
onChange={handleChange}
/>
</fieldset>
)
}
}
And my app.js:
'use strict'
import React from 'react'
import { render } from 'react-dom'
import Header from 'Header'
render(
<div>
<Header />
</div>,
document.getElementById('app')
)
You want to render the registration component, but you are not defining any place for it to render,
import React from 'react'
import Registration from 'Registration'
export default class Header extends React.Component {
constructor (props) {
super (props)
this.state = {
registration: false
}
this.handleClick = this.handleClick.bind(this)
}
handleClick () {
this.setState({registration: true})
}
render () {
return (
<div className='top-bar'>
<div className='top-bar-left'>
<ul className='menu'>
<li className='menu-text'>SIS App</li>
<li>
<button className='button' type='button' onClick={this.handleClick}>Create Info</button>
<li>
</ul>
{this.state.registration ? <Registration/>: null}
</div>
)
}
}
However I suppose ideally you would want to route to your registration component whenever the button is click, your could do that using dynamic routing after defining a route for your registration component.
Refer this solution, if you wish to do it this way
In handleClick you can change state like this this.setState({ renderRegistration: true }) and then in render function do the following
if (this.state.renderRegistration) {
return '<Registration />';
} else {
// your existing code
}
You have to correct two things, firstly you have to import Header correctly in main.js, as follows:
main.js
'use strict'
import React from 'react'
import { render } from 'react-dom'
import Header from './Header.jsx'
render(
<div>
<Header />
</div>,
document.getElementById('app')
)
Secondly, define and bind handleChange function you are using in Registration component and provide the area to render the same, a follows:
Header.jsx
'use strict'
import React from 'react'
import Registration from './Registration.jsx'
export default class Header extends React.Component {
constructor (props) {
super (props)
this.state = { showRegistration: false }
this.handleClick = this.handleClick.bind(this)
this.handleChange = this.handleChange.bind(this)
}
handleClick(e) {
this.setState({ showRegistration: true })
console.log("The link was clicked");
}
handleChange(e) {
console.log("handleChange was clicked");
}
render () {
let registration = null;
if (this.state.showRegistration) {
registration = <Registration handleChange = {() => this.handleChange()}/>
}
return (
<div className='top-bar'>
<div className='top-bar-left'>
<ul className='menu'>
<li className='menu-text'>SIS App</li>
<li>
<button className='button' type='button' onClick={this.handleClick}>Create Info</button>
</li>
</ul>
</div>
{registration}
</div>
)
}
}
Considering the layout of app, as below:
The only thing that your Header component will render to the screen is what's in the render function.
One approach here is to have a flag that controls whether to display the Registration component, which is controlled by the button click event.
I don't know where your registration component needs to go, but try something like this:
'use strict'
import React from 'react'
import Registration from 'Registration'
export default class Header extends React.Component {
constructor (props) {
super (props)
this.state = { showRegistration: false }
this.handleClick = this.handleClick.bind(this)
}
handleClick () {
this.setState({ showRegistration: true })
}
render () {
return (
<div className='top-bar'>
<div className='top-bar-left'>
<ul className='menu'>
<li className='menu-text'>SIS App</li>
<li>
<button className='button' type='button' onClick={this.handleClick}>Create Info</button>
<li>
</ul>
</div>
{this.state.showRegistration && <Registration />}
</div>
)
}
}
I'm close to getting this to work. Hoping someone can help me with this.
I have 2 React files. One being the Container and the other being the Nav component.
Inside my container component which is called _template.js, I'm importing a modal npm package. I created a showModal function inside this container file that I'm trying to access inside the Nav component.
This is what I have so far and receive this error Uncaught TypeError: Cannot read property 'modal' of undefined
_template.js:
import React from 'react';
import { Link } from 'react-router';
import { prefixLink } from 'gatsby-helpers';
import { config } from 'config';
import Headroom from 'react-headroom';
import Nav from './nav.js';
import '../css/main.scss';
import Modal from 'boron/DropModal';
const modalStyle = {
minHeight: '500px',
backgroundColor: '#303841'
};
const backdropStyle = {
backgroundColor: '#F6C90E'
};
const contentStyle = {
backgroundColor: '#303841',
padding: '3rem'
};
export default class RootTemplate extends React.Component {
static propTypes = {
location: React.PropTypes.object.isRequired,
children: React.PropTypes.object.isRequired,
}
static contextTypes = {
router: React.PropTypes.object.isRequired,
}
constructor() {
super();
}
showModal () {
this.refs.modal.show();
}
render() {
return (
<div>
<Headroom>
<Nav showModal={this.showModal}/>
</Headroom>
<Modal ref="modal" modalStyle={modalStyle} contentStyle={contentStyle} backdropStyle={backdropStyle}>
<form>
<label>Name:</label>
<input type="text"/>
<label>Email:</label>
<input type="email"/>
<label>Message:</label>
<input type="text-area"/>
<input type="submit" placeholder="Submit" />
</form>
</Modal>
{this.props.children}
</div>
);
}
}
And my Nav.js file:
import React from 'react';
import { Link } from 'react-router';
import { prefixLink } from 'gatsby-helpers';
import { Nav, NavGroup, NavItem, NavToggle, Icon } from 're-bulma';
export default class nav extends React.Component {
static propTypes = {
name: React.PropTypes.string,
};
constructor(props) {
super(props);
}
render() {
return (
<div>
<Nav>
<NavGroup align="left">
<NavItem>
<Link to={prefixLink('/')}>
<h2>Dillon Raphael</h2>
</Link>
</NavItem>
</NavGroup>
<NavToggle />
<NavGroup align="right" isMenu>
<NavItem>
</NavItem>
<NavItem>
</NavItem>
<NavItem>
<a href="#" onClick={::this.props.showModal}>Let's Work!</a>
</NavItem>
</NavGroup>
</Nav>
</div>
);
}
}
Notice the <a href="#" onClick={::this.props.showModal}>Let's Work!</a> this is where I'm trying to call the showModal function that I pass down from the _template.js file.
Add to your constructor, in _template.js file this code:
this.showModal = this.showModal.bind(this);
The constructor then should look like this:
constructor(props) {
super(props);
this.showModal = this.showModal.bind(this);
}