I am wondering if it is at all possible to fire an event in the grandchild and trigger something in the overall parent, without having to go through the middle step child.
I have created a simple example that will simply console.log the layer
So I am wanting to go Grandchild => Parent (Fire event) instead of going Grandchild => Child => Parent (Fire event)
If this is not possible in a simple way such as going up the chain, then do let me know.
Parent
export default class Parent extends React.Component {
render() {
return (
<div>
Parent
<Child clickMe={this.fireEvent.bind(this)}/>
</div>
)
}
fireEvent() {
console.log("Parent")
}
}
Child
export default class Child extends React.Component {
render() {
return(
<div>
child
<Grandchild fireEvent={this.handleClick.bind(this)}/>
</div>
)
}
handleClick() {
console.log('Child')
this.props.clickMe();
}
}
Grandchild
export default class Grandchild extends React.Component {
render() {
return(
<div>
grandchild
<button onClick={this.handleClick.bind(this)}>GC Click</button>
</div>
)
}
handleClick() {
console.log('Grandchild')
this.props.fireEvent();
}
}
To interact with components that are not direct children, you can use redux or context. Context is an experimental API and should be used if you have no other choice.
Here is an example:
import * as React from 'react';
import User from './User';
import PropTypes from 'prop-types';
class Small extends React.Component {
static contextTypes = {
color: PropTypes.string.isRequired,
user: PropTypes.object.isRequired
};
render() {
return <div style={{backgroundColor: this.context.color, height: '125px', width: '33%'}}>
Small, user = {this.context.user.id}-{this.context.user.login}
</div>;
}
}
class Medium extends React.Component {
render() {
return <div style={{backgroundColor: 'red', height: '250px', width: '50%'}}>
Medium
<Small/>
</div>;
}
}
export default class Large extends React.Component {
static childContextTypes = {
color: PropTypes.string.isRequired,
user: PropTypes.object.isRequired
};
getChildContext() {
return {color: 'purple', user: new User(1, 'admin')};
}
render() {
return <div style={{backgroundColor: 'blue', color: 'white', height: '300px', width: '75%'}}>
Large
<Medium/>
</div>;
}
}
I found that the way I had once seen is almost a cheating method of bypassing a certain layer, bu passing the parent props themselves as props within the child to the grandchild...
Parent
<child propsToChild={this.someFunction.bind(this)}/>
someFunction() { console.log("parent"); }
Child
<grandchild propsToGrandchild={this.props}/>
Grandchild
<button onClick={ this.grandChildClick.bind(this) }>Click me</button>
grandChildClick() {
console.log("Grandchild");
this.props.propsToGrandchild.propsToChild()
}
Demonstration
ALSO...
I have found that it is much easier to spread your props through the layers. This can be shown by editing the Child class as such:
render() {
const props = this.props;
return (
<div>
Child
<Grandchild {...props}/>
</div>
)
}
Related
I know that this is probably the most asked question about React, but none of the answers helped me.
I have 2 classes:
Child
class Preview extends Component {
constructor(...args) {
super(...args);
this.state = {
isCommentOpen: false
};
this.handleComment = ::this.handleComment;
render() {
return(
button type="button" onClick={this.handleComment}>Comment</button>
)}
handleComment(){
this.setState({isCommentOpen: !this.state.isCommentOpen});
}
export default Preview;
Parent
class Profile extends Component {
render(){
return(
<div>
<_.Preview />
//the place where I want to add validation from the component above
{this.state.isCommentOpen ? <span>Cool</span> : null}
</div>
}
You should not mutate or directly assign this.props as shown in the other answer:
this.props.isCommentOpen = !this.props.isCommentOpen // <-- DON'T DO THIS! 🎃
Instead, you should have a callback function to let the Parent component update the child component:
class Profile extends Component {
constructor(props) {
super(props);
this.state = {
isCommentOpen: false;
}
this.handleComment = this.handleComment.bind(this); // <-- important!
}
handleComment() {
this.setState({ isCommentOpen: !this.state.isCommentOpen });
}
render() {
return (
<div>
<Preview handleComment={this.handleComment} />
{ this.state.isCommentOpen ? <span>Cool</span> : null }
</div>
)
}
}
export default Profile
The child component then only needs to call this.props.handleComment:
// Child Component:
class Preview extends Component {
render() {
return(
<button type="button" onClick={this.props.handleComment}>Comment</button>
}
}
export default Preview;
Very new to React, and coming from a jQuery background, the first thing I want to be able to do is toggle classes.
I understand how I can toggle a class within the same react component like this:
class ButtonParent extends React.Component {
constructor(props) {
super(props)
this.state = {
condition: false
}
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState({
condition: !this.state.condition
})
}
render() {
return (
<ButtonChild
className={ this.state.condition ? "button toggled" : "button" }
toggleClassName={ this.handleClick }
>
Click me if you dare!
</ButtonChild>
)
}
}
class ButtonChild extends React.Component {
render() {
return (
<div
className={ this.props.className }
onClick={ this.props.toggleClassName }
>
{ this.props.children }
</div>
)
}
}
ReactDOM.render(<ButtonParent />, document.getElementById('app'))
But what if I have a a separate component that I want to use in order to toggle the class of the component? Is there no easy way to do this in React?
Thanks!
Just create an ButtonChild.js file near your ButtonParent.js file and export your component
export default class ButtonChild extends React.Component {
render() {
return (
<div
className={ this.props.className }
onClick={ this.props.toggleClassName }
>
{ this.props.children }
</div>
)
}
}
Import it in you ButtonParent.js file like this
import ButtonChild from './ButtonParent.js'
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 currently have this as my "index" for my Meteor app:
import React from 'react';
export const App = React.createClass({
propTypes: {
children: React.PropTypes.element.isRequired,
},
render() {
return <div className="app-div">
**<Foo/>**
**<Bar/>**
{ this.props.children }
</div>;
},
});
I am wondering if I can somehow change content of "Foo", through code in "Bar".
Essentially, "Foo" will have code like this:
export class Foo extends React.Component {
render() {
return(
<div className="test">TEXT TO BE REPLACED</div>
);
}
}
Bar, will also have similar code:
export class Bar extends React.Component {
render() {
// Code goes here to change that div "test"'s content in Foo^^^^^^
return(
<div>...</div>
);
}
}
But I need to have some sort of code that changes the "TEXT TO BE REPLACED". Is there a way to do this somehow? Maybe with the react DOM or something? I am kind of brute forcing my way through this, so I may not know basic fundamentals, sorry
Thanks in advance
In React+Meteor, communication between two siblings component should be done through their parent.
For your case, I would use a state in App component to store the content of "TEXT TO BE REPLACE", and a function inside App component to update that state content:
import React from 'react';
export const App = React.createClass({
propTypes: {
children: React.PropTypes.element.isRequired,
},
getInitialState() {
return {
fooText: '',
};
},
updateFooText(value) {
this.setState({
fooText: value,
});
},
render() {
return (
<div className="app-div">
<Foo text={this.state.fooText} />
<Bar updateFooText={this.updateFooText} />
{ this.props.children }
</div>
);
},
});
export class Foo extends React.Component {
render() {
return (
<div className="test">{this.props.text}</div>
);
}
}
export class Bar extends React.Component {
onChangeHandler(e) {
this.props.updateFooText(e.target.value);
},
render() {
// Code goes here to change that div "test"'s content in Foo^^^^^^
return (
<div>
<input type="text" onChange={this.onChangeHandler} />
</div>
);
}
}
I have a Component like this:
<MyTimer props={+new Date()}>
<span className="year" formatter="yyyy" />
<span className="mon" formatter="mm" />
<span className="day" formatter="dd" />
</MyTimer>
MyTimer is Component like this(I just ignore some other code):
render() {
/**
* this code may not work, I just do what I think.
*/
return (<div>
{ this.props.children.map((child, i) =>
// child.children=[util.date(child.formatter)])
// set child.props.children as "util.date(child.formatter)"
}
</div>)
}
I want to change the content of span in component, which should result like this:
<span class="year">2016</span>
<span class="mon">07</span>
<span class="day">06</span>
So why do I need this, because I can change the child element type as I like:
<MyTimer props={+new Date()}>
<div className="year" formatter="yyyy" />
<span className="mon" formatter="mm" />
<p className="day" formatter="dd" />
</MyTimer>
I just do not care about what the child element is, and also className.
React has a feature called "contexts", which allow a parent component to implicitly pass some data to children.
The actual formatting then can happen in a special "formatter" component. Resulting in something like this:
import React, { Component, PropTypes } from 'react';
import moment from 'moment';
class MyTimer extends Component {
static childContextTypes = {
date: PropTypes.object
};
// expose some props to children
getChildContext() {
return {
date: this.props.date
};
}
render() {
return <div>{this.props.children}</div>;
}
}
class TimerPart extends Component {
// this is REQUIRED to receive the date from the parent
static contextTypes = {
date: PropTypes.object
};
render() {
// use it like `this.context.date`
return <span>{moment(this.context.date).format(this.props.format)}</span>;
}
}
export default class App extends Component {
render() {
return (
<MyTimer date={new Date()}>
<TimerPart format="YYYY"/>
<TimerPart format="MM"/>
<TimerPart format="DD"/>
</MyTimer>
)
}
}