I have a requirement where in I am making a reusable modal, for which the structure is as shown below:
--Parent.js
--ModalComponent.js
In parent, I am creating the content for modal on a click,
getModalContent = () => {
let ModalBody = (
<div onClick={this.clickableHandler}> //I want this clickableHandler to trigger in child(modal component)
This is clickable
</div>
);
this.setState({modalContent: ModalBody});
}
render(){
return(
<button onClick={this.getModalContent}>Open modal</button>
<ModalComponent modalContent={this.state.ModalContent}/>
);
}
In child(Modal) component, on click of that div, I want the function clickableHandler to get triggered.
ModalComponent.js----
import React, {Component} from 'react';
class ModalComponent extends Component{
clickableHandler = () => {
console.log('I am clicked');
}
render(){
return (
<div>
{this.props.modalContent}
</div>
);
}
}
But by doing so, I not getting the function clickableHandler() function to get triggered in modal, ie, the child component. Let me know where I am going wrong.
You need to define the clickableHandler in the parent not in the modal. You are registering the event callback in your parent and refer to it as this.clickableHandler. In this case 'this' refers to parent but there is no class method called clickable handler on the parent. I hope it helps.
Since the clickHandler is actually defined inside ModalComponent and you are providing the content from the Parent component, its probably a good idea to make use of renderProps in ModalComponent like
import React, {Component} from 'react';
class ModalComponent extends Component{
clickableHandler = () => {
console.log('I am clicked');
}
render(){
return this.props.children(this.clickableHandler);
}
}
and use it like
getModalContent = (clickableHandler) => {
return (
<div onClick={clickableHandler}>
This is clickable
</div>
);
}
render(){
return(
<button onClick={this.getModalContent}>Open modal</button>
<ModalComponent>
{getModalContent}
</ModalComponent>
);
}
You need to bind the clickHandler function in the parent component. To dynamically change the child components, you can update the Parent state, and pass this needed values down as props. Every time state/props update the component affected will re-render. Give this a go.
Parent Component
class Parent extends React.Component {
constructor () {
super()
this.state = {
modalContent: '',
childComponent: ''
}
this.getModalContent = this.getModalContent.bind(this)
this.clickableHandler = this.clickableHandler.bind(this)
}
getModalContent () {
let ModalBody = (
<div onClick={this.clickableHandler}>
</div>
)
this.setState({modalContent: ModalBody})
}
clickableHandler () {
this.setState({
childComponent: yourData
})
}
render () {
return (
<div>
<button onClick={this.getModalContent}>Open Modal</button>
<ModalComponent modalContent={this.state.modalContent}
childComponent={this.state.childComponent} />
</div>
)
}
}
export default Parent
class ModalComponent extends Component {
render () {
return (
<div>
{this.props.modalContent}
<AnotherChild childBody={this.props.childComponent} />
</div>
)
}
}
export default ModalComponent
Related
import React from 'react'
export default () => {
function clickHandler() {
console.log('Button clicked')
}
return (
<div>
<button onClick={clickHandler}>Click</button>
</div>
)
}
In the above code we see that a function has been passed to the onClick.In the same way to the onClick I need to pass a diffrent component which is present in the same src. This component consists of a .js and a .css file.Could you please help me out with it. Thanks in advance
If you don't mind using classes instead of functions, your other component should look like this:
import React from 'react'
class ShowThisAfterClick extends React.Component {
return (
<div>
<p>This is what you want to show</p>
</div>
)
}
export default ShowThisAfterClick
And now you should update the component you've shown:
import React from 'react'
import ShowThisAfterClick from './where/you/put/the/ShowThisAfterClick.js'
class Main extends React.Component {
constructor(props){
super(props)
this.state = { isButtonClicked: false }
this.clickHandler = this.clickhandler.bind(this)
}
clickHandler() {
this.setState({ isButtonClicked: true })
}
render() {
const { isButtonClicked } = this.state
return (
<div>
<button onClick={ this.clickHandler }>Click</button>
{ isButtonClicked ? <ShowThisAfterClick /> : ''}
</div>
)
}
}
export default Main
If you want to keep using functions, then I would kindly suggest to read the manual, it is more than well written.
In my app I have a child component, 'Menu', where a 'select' state is updated by a click event, like so:
Menus.jsx (child):
import React, { Component } from 'react';
import { Redirect } from 'react-router-dom';
import Brewing from './Brewing.jsx';
import { withRouter } from 'react-router-dom';
class Menus extends Component{
constructor (props) {
super(props);
this.state = {
select: '',
isLoading: false,
redirect: false
};
};
(...)
gotoCoffee = (index) => {
this.setState({isLoading:true, select:this.state.coffees[index]})
setTimeout(()=>{
this.setState({isLoading:false,redirect:true})
},5000)
console.log(this.state.coffees[index])
}
renderCoffee = () => {
if (this.state.redirect) {
return (<Redirect to={'/coffee/'+this.state.select} />)
}
}
render(){
const coffees = this.state.coffees;
return (
<div>
<h1 className="title is-1"><font color="#C86428">Menu</font></h1>
<hr/><br/>
{coffees.map((coffee, index) =>
<span key={coffee}>
<div>
{this.state.isLoading && <Brewing/>}
{this.renderCoffee()}
<div onClick={() => this.gotoCoffee(index)}
style={{textDecoration:'underline',cursor:'pointer'}}>
<strong><font color="#C86428">{coffee}</font></strong></div>
<div>
</div>
</div>
</span>)
}
</div>
);
}
}
export default withRouter(Menus);
the above works.
However, let's say I have another child component, 'Coffee', which should inherit this changed state.
I have learned that passing this event change, and state, from child to another child component, is an anti-pattern. Considering the ways of React, data can only flow from top-to-bottom i.e., from parent-to-child.
So have I tried to manage 'select' state from top to bottom, like so:
App.jsx (parent)
class App extends Component {
constructor() {
super();
this.state = {
select: '',
};
this.onSelectChange = this.onSelectChange.bind(this);
};
then I would use a callback here at 'App.jsx', like so:
onSelectChange(newSelect){
this.setState({ select: newSelect });
}
and pass it to 'Menus' component, like so:
<Route exact path='/menus' render={() => (
<Menus
onSelectChange={this.onSelectChange}
/>
)} />
finally, at child 'Menus', I would user event change to change props, which could be passed to other childs etc:
gotoCoffee = (index) => {
this.setState({isLoading:true})
this.props.onSelectChange(index)
setTimeout(()=>{
this.setState({isLoading:false,redirect:true})
},5000)
console.log(this.props.select)
}
but I'm getting console.log(this.props.select) 'undefined'.
what am I missing?
You are only passing onSelectChange method as a prop to Menu component right now, to access this.props.select, you need to pass select as prop to Menu.
<Route exact path='/menus' render={() => (
<Menus
onSelectChange={this.onSelectChange}
select={this.state.select}
/>
)} />
Whenever this.onSelectChange method gets called and state changes in your App.jsx, your Menu component will be rendered. You can use the updated this.props.select in your render method or in any non static method of your Menu component.
class Menu extends Component {
render() {
console.log(this.props.select);
return (
...
);
}
}
I'm trying to pass a click handler function to a child component but I cant figure out why its not working.
I tried making my click handler into a lambda function, binding it to the parent component, and making my onClick in the child component a lambda function but nothing seems to work.
Parent Component
import React from 'react';
import NavMenuButton from './navMenuButton.js';
export default class NavBar extends React.Component {
constructor(props) {
super(props);
this.state = {
navBarOpen: true
};
this.toggleNavbar = this.toggleNavbar.bind(this);
}
toggleNavbar() {
console.log('Toggled!');
this.setState({
navBarOpen: this.state.navBarOpen ? false : true
});
}
render() {
return (
<NavMenuButton
handleClick={this.toggleNavBar}
navBarOpen={this.state.navBarOpen}
/>
);
}
}
Child Component
import React from 'react';
export default class NavMenuButton extends React.Component{
constructor(props){
super(props)
}
render(){
const styles = {
navButton: this.props.navBarOpen
? {fontSize:26, cursor:'pointer', userSelect:'none'}
: {fontSize:26, cursor:'pointer', userSelect:'none'}
}
return(
<div>
<span
style={styles.navButton}
onClick={this.props.handleClick}
>
{'\u2630'}
</span>
{this.props.navBarOpen
? <p>its open</p>
: <p>its closed</p>}
</div>
);
}
}
This configuration is what I thought should work, but if I log the props in navMenuButton to the console, handleClick is undefined which really confuses me
toggleNavBar !== toggleNavbar - case sensitivity is very important :)
I have an App component and a function 'modalToggled' inside its.
I want to pass the function to multiple child components until I get to the last one, the 'interiores' component.
Like this:
<App> -> <Coluna1> -> <MenuPrincipal> -> <Portfolio> -> <PortfolioMenu> -> <interiores>
App Component, the parent of all components:
import React, { Component } from 'react';
import Coluna1 from './Coluna1'
class App extends Component {
constructor(props) {
super(props)
this.state = {
modalOn: false
}
this.modalToggled = this.modalToggled.bind(this)
}
modalToggled = (on) => {
this.setState({modalOn: on});
}
render() {
return (
<div>
<Coluna1 onModalToggle={this.modalToggled}/>
</div>
)
}
}
export default App;
This is the 'Coluna1' the first child component. I did the same thing in the another ones: 'MenuPrincipal', 'Portfolio', 'PortfolioMenu'
class Coluna1 extends Component {
constructor(props){
super(props)
}
render() {
return (
<div>
<Header />
<MenuPrincipal onModalToggle={this.props.modalToggled} />
</div>
)
}
}
export default Coluna1
Therefore here is the last component interiores, when I click on the button there appears an error message:
TypeError: _this.props.onModalToggle is not a function
import React, { Component } from 'react'
import Modal from 'react-responsive-modal';
class Interiores extends Component {
constructor(props) {
super(props)
this.state = {
open: false
}
}
onOpenModal = () => {
this.setState({ open: true });
this.props.onModalToggle(true);
};
onCloseModal = () => {
this.setState({ open: false });
this.props.onModalToggle(false);
};
render() {
const { open } = this.state;
return (
<div>
<button onClick={this.onOpenModal}>Open modal</button>
<Modal open={open} onClose={this.onCloseModal} center></Modal>
</div>
)
}
}
export default Interiores;
Does anybody know how to solve it? Thank you
It happens, because in App class you pass prop with name onModalToggle:
<Coluna1 onModalToggle={this.modalToggled}/>
But in Coluna1 you receive this props with wrong name, modalToggled:
<MenuPrincipal onModalToggle={this.props.modalToggled} />
Just make the names of props equal. In Coluna1 and other intermediate components pass and receive this props as onModalToggle:
<MenuPrincipal onModalToggle={this.props.onModalToggle} />
This is the problem
modalToggled = (on) => {
this.setState({modalOn: on});
}
Since this is a class function it needs to be defined like
modalToggled(on) {
this.setState({modalOn: on});
}
I am working on React application and making something like a framework where I have a wrapper component some thing like this.
class FrameworkComponent extends React.Component {
someFunction() {
// send data to child data using childs function
// something like this.some.thing.childFunction("mydata");
...
}
render() {
return (
<div>
<div><button onClick={this.someFunction}>Click me</button></div>
<div>{this.props.child}</div>
</div>
)
}
}
and using it like this :
class SecondComponent extends React.Component {
childFunction(dataRecived) {
alert(dataRecived);
}
render() {
return <div>Hello world</div>;
}
}
import FrameworkComponent from '../FrameworkComponent';
import SecondComponent from '../SecondComponent';
class OtherComponet extends React.Component {
render() {
return (
<div>
<FrameworkComponent>
<div><SecondComponent /></div>
</FrameworkComponent>
</div>
)
}
}
So here I want child's component receive data from parent wrapper component either by updating its child props : componentWillReceiveProps() or calling its child method.
Your best bet would be to use HOC - Higher - https://reactjs.org/docs/higher-order-components.html