I have two components. the App component which is the parent component and the Btn component which is the child component. How can i change the value of property text from the Btn component?
export default class App extends Component<Props> {
constructor() {
super();
this.text = 'testing';
this.onClicked = this.onClicked.bind(this);
}
onClicked() {
this.text = 'changed';
}
render() {
return (
<View style = { styles.container }>
<Text style = { styles.welcome }> { this.text } </Text>
<Btn />
</View>
);
}
}
class Btn extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Button
title = 'Button'
onPress = { ? }
/>
)
}
}
You can pass the parent component's onClicked function as a prop to the child component.
export default class App extends Component<Props> {
...
render() {
return (
<View style = { styles.container }>
<Text style = { styles.welcome }> { this.text } </Text>
<Btn onClicked={this.onClicked}/>
</View>
);
}
}
class Btn extends Component {
...
render() {
return (
<Button
title = 'Button'
onPress = {this.props.onClicked}
/>
)
}
}
You are holding your text value in the wrong place. Hold it in your state. constructor runs once in the initial render and it does not run again like that. But, as a React way, if you hold your data in your state, whenever state changes your component rerenders and you see the updated data.
Then, you can pass your onClick handler to the child component and use it there. Here is the working snippet. I used class-fields so no need to write a constructor and an arrow function (no need to bind it).
class App extends React.Component {
state = {
text: "",
}
onClick = () =>
this.setState({text: "foo bar" });
render() {
return (
<div>
<p>Text is now: { this.state.text } </p>
<Btn onClick={this.onClick} />
</div>
);
}
}
const Btn = ( props ) => (
<button onClick={props.onClick}>Click</button>
)
ReactDOM.render(
<App />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Related
Is it possible to create a method in Parent class in React and then pass it on to the Child and use it there.
So basically I would create a button in my Parent class, pass the function on to the Child and when the Button is clicked, the child will know about it and Parent will not really care for it?
class App extends Component {
clickMade = () => {
//This should be left empty
};
render() {
return (
<div className="App">
<Button onClick={this.clickMade}>Click me </Button>
<Child clickMade={this.clickMade} />
</div>
);
}
}
export default App;
And the Child:
class Child extends Component {
constructor(props) {
super(props);
this.handleClick = this.props.clickMade.bind(this);
}
handleClick = () => {
console.log("Click in child");
}
render() {
return null;
}
}
export default Child;
And a sandbox for this: CodeSandbox
App.js
class App extends Component {
clickMade = () => {
this.childRef.handleClick();
};
render() {
return (
<div className="App">
<Button onClick={this.clickMade}>Click me </Button>
<Child
ref={ref => {
this.childRef = ref;
}}
/>
</div>
);
}
}
I'm trying to pass input value from one child to another child using setState. It renders the first element, but not the second element in the first child...
class App extends Component {
constructor(props) {
super(props);
this.state = {};
};
showTekst = () => {
const inpValue = document.querySelector('.inpValue').value;
this.setState({
name: 'Bruce Wayne',
namefromvalue: inpValue.value
});
}
render() {
return (
<div className="app">
<Button showTekst={this.showTekst.bind(this)} />
<Text name={this.state.name} namefromvalue={this.state.namefromvalue}/>
<Inputvalue />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
class Inputvalue extends Component {
render() {
return (
<input className="inpValue"></input>
);
}
}
class Text extends Component {
render() {
return (
<div>
<h1>This is text..</h1>
<h2>{this.props.namefromvalue}</h2>
<p>{this.props.name}</p>
</div>
);
}
}
If you want to show the text from the Inputvalue component in the Text component only when the Button component is clicked you can store the input value in the App instance and update the App state when the Button is clicked.
To do so, we define an onChange prop in the Inputvalue component so that every time the text in the input changes, the prop is called with the new value. With that prop we update the value in the App instance. This value will always be in sync with the input value.
Last, when the button is clicked we update the App state with the input value so that the Text component renders its value.
class App extends Component {
constructor(props) {
super(props);
this.state = {};
// This variable will hold the input value.
this.inputValue = "";
}
showTekst = () => {
// Update with the current input value.
this.setState({
name: "Bruce Wayne",
namefromvalue: this.inputValue
});
};
onInputChange = value => {
// Update the input value.
this.inputValue = value;
};
render() {
return (
<div className="app">
<Button showTekst={this.showTekst.bind(this)} />
<Text name={this.state.name} namefromvalue={this.state.namefromvalue} />
<Inputvalue onChange={this.onInputChange} />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
class Button extends Component {
render() {
return <button onClick={this.props.showTekst}>Show text</button>;
}
}
class Inputvalue extends Component {
render() {
return (
<input
className="inpValue"
onChange={ev => this.props.onChange(ev.target.value)}
/>
);
}
}
class Text extends Component {
render() {
return (
<div>
<h1>This is text..</h1>
<h2>{this.props.namefromvalue}</h2>
<p>{this.props.name}</p>
</div>
);
}
}
I want to call child function from parent component so I found a question here
Call child method from parent
So I used this way to to called it (From 1st Answer and 2nd approach).
Now issue is how to set state in the child getAlert function
class Parent extends Component {
render() {
return (
<div>
<Child ref={instance => { this.child = instance; }} />
<button onClick={() => { this.child.getAlert(); }}>Click</button>
</div>
);
}
}
class Child extends Component {
constructor(){
super();
this.state = {message:""};
}
getAlert() {
alert('clicked');
//HERE I NEED TO SETSTATE
}
render() {
return (
{this.state.message!=""?(
<h1>{this.state.message}</h1>
):(
<h1>Hello</h1>
)}
);
}
}
In the getAlert function of child I need to setState but I couldn't able to do it. Please provide any solution
The problem seems to be that when calling setState inside the getAlert function of child, this.setState will come undefined. This happens because this inside your getAlert function doesn't refer to the context of the React Component and setState is defined for the Component. You can solve this by binding the getAlert function.
You can do it in two ways.
First: using .bind(this) in constructor
class Child extends Component {
constructor() {
super();
this.getAlert = this.getAlert.bind(this);
}
getAlert() {
alert('clicked');
//HERE I NEED TO SETSTATE
}
render() {
return (
<h1>Hello</h1>
);
}
}
Second: use Arrow function
getAlert = () => {
alert('clicked');
//HERE I NEED TO SETSTATE
}
Check this answer on Why do we need to bind React functions
Check the working snippet
class Parent extends React.Component {
render() {
return (
<div>
<Child ref={instance => { this.child = instance; }} />
<button onClick={() => { this.child.getAlert(); }}>Click</button>
</div>
);
}
}
class Child extends React.Component {
constructor(){
super();
this.state = {message:""};
}
getAlert = () => {
alert('clicked');
this.setState({message: "somemessage"});
}
render() {
return (
<div>{this.state.message!=""?(
<h1>{this.state.message}</h1>
):(
<h1>Hello</h1>
)}</div>
);
}
}
ReactDOM.render(<Parent/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"/>
class Parent extends React.Component {
render() {
return (
<div>
<Child ref={instance => { this.child = instance; }} />
<button onClick={() => { this.child.getAlert(); }}>Click</button>
</div>
);
}
}
class Child extends React.Component {
constructor(){
super()
this.state = {
message: 'google'
}
this.getAlert = this.getAlert.bind(this)
}
getAlert() {
alert('clicked');
this.setState ({
message: 'yahoo'
}, () => {
console.log(this.state.message) //the state value will be printed
});
}
render() {
console.log(this.state.message)
return (
<h1>Hello {this.state.message}</h1>
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>
you have bind the getAlert() method to scope of the class ie). 'this'. I have added an sample code..pls check
I created a custom Accordion component which again consist of two child components called AccordionTitle and AccordionContent:
The AccordionTitle component has a button. When clicked, the AccordionContent part toggles its style from display:none to block and back when clicked again.
AccordionTitle.js
class AccordionTitle extends Component {
constructor() {
super();
this.show = false;
}
toggle() {
this.show = !this.show;
if (this.props.onToggled) this.props.onToggled(this.show);
}
render() {
return (
<div style={this.props.style}>
<Button onClick={e => this.toggle(e)} />
{this.props.children}
</div>
);
}
}
export default AccordionTitle;
AccordionContent.js
class AccordionContent extends Component {
render() {
let style = this.props.style ? this.props.style : {};
style = JSON.parse(JSON.stringify(style));
style.display = this.props.show ? 'block' : 'none';
return (
<div style={style}>
{this.props.children}
</div>
);
}
}
export default AccordionContent;
Also, I use the following parent component:
Accordion.js
class Accordion extends Component {
render() {
return (
<div>
{this.props.children}
</div>
);
}
}
Accordion.Title = AccordionTitle;
Accordion.Content = AccordionContent;
export default Accordion;
Now, when I use the Accordion component, it's possible that I might need multiple accordions in a row which would look like this:
ProductAccordion.js
import React, { Component } from 'react';
import Accordion from '../Accordion/Accordion';
class ProductAccordion extends Component {
constructor() {
super();
this.state = {
show: false,
};
}
toggled() {
this.setState({
show: !this.state.show,
});
}
render() {
this.productsJsx = [];
const products = this.props.products;
for (let i = 0; i < products.length; i += 1) {
this.productsJsx.push(
<Accordion.Title onToggled={e => this.toggled(e, this)}>
{products[i].name}
<img src="{products[i].imgsrc}" />
</Accordion.Title>,
<Accordion.Content show={this.state.show}>
{products[i].name}<br />
{products[i].grossprice} {products[i].currency}<br />
<hr />
</Accordion.Content>,
);
}
return (
<Accordion style={styles.container}>
{this.productsJsx}
</Accordion>
);
}
}
export default ProductAccordion;
As you can see, I am grabbing the toggled Event from Accordion.Title and I bind it to the prop show of Accordion.Content via the toggled() method.
Now, this works perfectly fine as long as there is just one product, but if there are more of them, clicking on the button will toggle all AccordionContent instances.
How can I change this so that only the content-part which belongs to the title that contains the clicked button will be toggled?
I also have the feeling that the component Accordion should take care of this (rather than ProductAccordion) by allowing Accordion.Title to delegate the toggled event directly to its sibling Accordion.Content. How can I achieve this?
I would suggest storing the index of the open item in state, instead of a boolean. Then in your render, show={this.state.show} would be something like show={this.state.show === i}.
Full example:
import React, { Component } from 'react';
import Accordion from '../Accordion/Accordion';
class ProductAccordion extends Component {
constructor() {
super();
this.state = {
show: null,
};
}
toggled(event, ind) {
const index = this.state.index;
this.setState({ show:ind === index ? null : ind });
}
render() {
this.productsJsx = [];
const products = this.props.products;
for (let i = 0; i < products.length; i += 1) {
this.productsJsx.push(
<Accordion.Title onToggled={e => this.toggled(e, i)}>
{products[i].name}
<img src="{products[i].imgsrc}" />
</Accordion.Title>,
<Accordion.Content show={this.state.show === i}>
{products[i].name}<br />
{products[i].grossprice} {products[i].currency}<br />
<hr />
</Accordion.Content>,
);
}
return (
<Accordion style={styles.container}>
{this.productsJsx}
</Accordion>
);
}
}
export default ProductAccordion;
and this
class AccordionTitle extends Component {
constructor() {
super();
}
render() {
return (
<div style={this.props.style}>
<Button onClick={this.props.onToggled} />
{this.props.children}
</div>
);
}
}
export default AccordionTitle;
i'm using ReactJS without FLux or Redux. I want the grand child component can communicate (read/update data) with his grandparents component.
Here's the parent component App:
export default class App extends React.Component {
static propTypes = {
children: React.PropTypes.object.isRequired
};
constructor(props) {
super(props);
this.state = {
tabActive: 0,
};
}
setTabActive(item) {
this.setState({
tabActive: item,
});
}
render() {
return (
<div>
<Header tabActive={this.state.tabActive} />
<Content>
{this.props.children}
</ Content>
<Footer />
</div>
);
}
}
child component Header:
export default class Header extends React.Component {
render() {
return (
<div>
....
<SettingTabBar tabActive={this.props.tabActive} />
</div>
);
}
}
Child of child component SettingTabBar:
export default class SettingTabBar extends React.Component {
constructor(props) {
super(props);
this.state = { activeTab: this.props.tabActive };
}
render() {
if (location.pathname.indexOf('setting') > 0) {
return (
<Tabs activeTab={this.state.activeTab} onChange={tabId => this.setState({ activeTab: tabId })} ripple>
<Tab>SETTING</Tab>
<Tab>CHARTS</Tab>
<Tab>HELP</Tab>
</Tabs>
);
}
return null;
}
}
Are there anyway to make SettingTabBar component can update data to App/Header component via function setTabActive() when onChange?
For communication with grandparent and grandchild, you can use context. It's not recomended, but it's working.
// root component
class App extends React.Component {
constructor(props){
super(props);
this.state = {
activeMenu: "none"
};
}
getChildContext() {
return {
rootCallback: menuName => {
this.setState({activeMenu: menuName});
}
}
}
render() {
return (
<div>
<div>Current active menu is: <strong>{this.state.activeMenu}</strong></div>
<Child />
</div>
);
}
}
// declare childContextTypes at context provider
App.childContextTypes = {
rootCallback: React.PropTypes.function
}
// intermediate child
class Child extends React.Component {
render() {
return (
<div>
<GrandChild />
</div>
);
}
}
// grand child
class GrandChild extends React.Component {
render() {
return (
<div>
{/* get context by using this.context */}
<button
type="button"
onClick={()=>this.context.rootCallback("one")}
>
Activate menu one
</button>
<button
type="button"
onClick={()=>this.context.rootCallback("two")}
>
Activate menu two
</button>
<button
type="button"
onClick={()=>this.context.rootCallback("three")}
>
Activate menu three
</button>
</div>
)
}
}
// also declare contextTypes at context consumer
GrandChild.contextTypes = {
rootCallback: React.PropTypes.function
}
// render it to DOM
ReactDOM.render(<App /> , document.getElementById('app-mount'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app-mount"></div>