Im learning react and very new to this, I am tinkering with something to understand this more.
I would like to know if it is possible to console.log the state of the CHILD using a prop passed down by the parent.
Example :
Child component ( has its own state)
Parentcomponent ( has its own state)
Child Component
this.state={
animal:'Lion'
}
<button onClick{this.props.giveMeState}>
And that, I would want to console the state ( animal:Lion)
Parent Component
this.state={
name: 'John'
}
giveMeState(){ ? what can go here, or is it not that simple ?
)
}
Codepen of example
Parent component cannot query the state of the child component. At least, that's not the intended design of React.
What I think you're asking is how to coordinate the state of child with parent, and you're on the right track to use a prop to pass the state from child to parent.
Perhaps a complete example that does what you want would look like this:
class Parent extends React.Component {
state = { name: "John" }
handleChildAnimal = animal =>
this.setState({ animal });
handleClick = e =>
console.log(`Child animal: ${this.state.animal}`);
render() {
return (
<div>
<Child onAnimal={this.handleChildAnimal} />
<button onClick={this.handleClick}>Tell me Animal state</button>
</div>
);
}
}
class Child extends React.Component {
state = { animal: "Lion" }
handleClick = e => {
console.log(`Animal: ${this.state.animal}`);
this.props.onAnimal(this.state.animal);
}
render() {
return (
<button onClick={this.handleClick}>{this.state.animal}</button>
);
}
}
Demo on CodePen.io
if you want to pass the state value of the child to the parent,
you can do it like this,
In the child component add another function getState and call the reference function giveMeState through this function
...
constructor(props) {
super(props)
this.state={ animal:'Lion' }
this.getState = this.getState.bind(this)
}
getState(){
this.props.giveMeState(this.state)
}
....
<button onClick={this.getState}>
....
and also redefine the parent function so that it takes a parameter
and the console.log that parameter
Not sure if this is a good pattern though
Here is another answer just for giving another example.
It does not fulfill your question and as told fulfilling your question would not be the best approach. Maybe you should try to think differently while working React and states.
App
class App extends React.Component {
state = {
input: "initial input state",
childState: "right now I don't know child state",
};
handleInputChange = e => this.setState({ input: e.target.value });
handleChildState = ( childState ) => this.setState( { childState } )
render() {
return (
<div style={styles}>
<h4>This is parent component.</h4>
<p>Input state is: {this.state.input} </p>
<p>Child state is: {this.state.childState}</p>
<hr />
<Input
onInputChange={this.handleInputChange}
getState={this.handleChildState}
/>
</div>
);
}
}
Child component as Input
class Input extends React.Component {
state = {
myState: "some state"
};
handleSendState = () => this.props.getState(this.state.myState);
handleState = e => this.setState({ myState: e.target.value });
render() {
return (
<div>
<h4>This is Child coponent</h4>
<button onClick={this.handleSendState}>
Click me to get child state
</button>
<p>This is my state: {this.state.myState}</p>
<p>Write something to change child's state.</p>
<input type="text" onChange={this.handleState} />
<p>
Write something to change parent's input state
</p>
<input type="text" onChange={this.props.onInputChange} />
</div>
);
}
}
Related
Setup: I have set up a two react components in a parent child relationship. The parent has a state that can be changed by press of a button on parent itself.
Expected behaviour: In the child, I have an input field and I want the state to change to the value I send in the input field on the press of the submit button. I have set up the parent and the child as follows:
What I have tried: I going through this answer and this youtube video but I guess I am not smart enough to make sense of it.
This is what my code looks like
Parent:
class App extends Component {
state = {
value:"someValue"
};
changeValue = (value) => {
this.setState({
value
})
}
render() {
return (
<div>
<p>this is the value from state: {this.state.value}</p>
<button onClick={()=>this.changeValue("valueFromParentComponent")}>Press to change value from parent component</button>
<br/><br/>
<Child getChildInputOnSubmit={()=>this.changeValue()} />
</div>
);
}
}
And this is what the child looks like
Child:
class Child extends Component {
state = {
}
sendChildData = (childInputValue) => {
console.group("This is the data from the input of the child component")
console.log("==============")
console.log(childInputValue)
console.log("==============")
}
render() {
return (
<div>
This is the child component
<br /><br />
<form>
<input type="text" placeholder="Some placeholder"></input>
<button onSubmit={this.sendChildData()} type="submit">Send child's input to parent</button>
</form>
</div>);
}
}
The React behaviour encourages to implement an inverse data flow inside a component hierarchy. Meaning that the child components can receive parent methods through props, this methods will work as callbacks, allowing to receive data, trigger behaviours, update his state and more.
I attach a StackBlitz example, showing how this concept would work in your setup https://stackblitz.com/edit/react-jsv5jo
Edit: Here a few extra tips applied on the example:
To work with inputs on React, a common setup consists on listen the onChange event to receive new data and update the component state. Then, this state is used in the value attribute to update the input content on DOM.
Listen the onSubmit event on the form tag instead on submit button, and remember to add some logic to avoid reloading.
Another good practice on React components is initialize your state object inside the Constructor (In case to be working with a Class Component) and write methods to avoid bloat the render one (Be sure to bind the extra methods on your constructor to avoid invocation problems)
Callbacks are used to pass data from Child component to Parent component in React.
We wright function in Parent component that will receive value and pass this function to child component through Props.
class Parent extends Component {
state = {
value: 'someValue'
};
changeValue = value => {
this.setState({
value
});
};
render() {
return (
<div>
<p>this is the value from state: {this.state.value}</p>
<button onClick={() => this.changeValue('valueFromParentComponent')}>
Press to change value from parent component
</button>
<br></br>
<Child getChildInputOnSubmit={this.changeValue} />
</div>
);
}
}
Now in Child component we call Parents function that we passed in props and send value.
class Child extends Component {
constructor(props) {
super(props);
this.state = {
Childvalue: ''
};
}
handleChange = event => {
event.preventDefault();
this.setState({ Childvalue: event.target.value });
};
sendToParent = () => {
//here calling Parents changeValue
this.props.getChildInputOnSubmit(this.state.Childvalue);
};
render() {
return (
<div>
This is the child Component
<br></br>
<form action='#' onSubmit={this.sendToParent}>
<input
type='text'
placeholder='Some placeholder'
value={this.state.Childvalue}
onChange={this.handleChange}
></input>
<button type='submit'>Send child's input to parent</button>
</form>
</div>
);
}
}
I am having a Parent Component and a child component
export default class CityPart extends Component {
constructor(props)
{
super(props);
this.state ={
citylist: citylist
selectedflag:1
}
}
Clickme(idd){
this.setstate({selectedflag:idd})
}
render() {
return (
<div className="CityPartMain">
{this.state.citylist.map((item)=>{
return (
<EachCity name ={item.name} key={item.id}
id={item.id}
selected={this.state.selected}
Clickme ={this.Clickme}
/>
)
})}
</div>
);
}
}
export default class EachCity extends Component {
render() {
const {name,id,selected,Clickme} = this.props;
return (
<button
onClick={this.Clickme(id)}
className={selected===parseInt(id,10)?"selectedcity": "EachCityMain"}>
<p>{name}</p>
</button>
);
}
}
whenever i click on the button i want that id should come to my parent component and it should get updated in the selectedflag of the parent state.
Thanks for help in advance.
You have made two mistakes.
First one is in your parent - this is how you should implement method Clickme:
Clickme = (id) => {
this.setState({selectedflag:idd})
}
In your implementation this means object, which is calling method (child), so this.setState() will work for child.
When you use lambda expression - it will work for parent.
Second mistake is how are you calling Clickme in your child component.
You should make it like:
<button
onClick={() => Clickme(id)}
className={selected === parseInt(id,10) ? "selectedcity" : "EachCityMain"}
>
<p>{name}</p>
</button>
If you are calling function without parameters, you can just pass function to onClick. In your case you have to create anonymous function to call it with parameter. Additionaly this is unnecessary.
Your Clickme callback is getting called right away (remember, function() is called right then) and you are just storing the result of that call in the onClick handler. You're also using this. Try instead setting it as a callback using an anonymous arrow function.
export default class EachCity extends Component {
render() {
const { name, id, selected, Clickme} = this.props;
return (
<button
onClick={() => Clickme(id)}
className={selected===parseInt(id,10) ? "selectedcity" : "EachCityMain"}
>
<p>{name}</p>
</button>
);
}
}
If this is unclear to you and how and why it works, I can add a codesandbox example with more detail.
In the main component's render method, I have this line,
<BettingChips onClick={(betAmount) => this.handleClick(betAmount)} />
Which corresponds to this function,
handleClick(betAmount){
alert(betAmount);
}
However under the child component, I can't seem to pass arguments to the handleClick function,
class BettingChips extends Component{
render(){
return(
<div>
<button onClick={this.props.onClick} value={1} >1</button>
// ... etc
</div>
)
};
I can write this.props.onClick... But I can't write this.props.onClick(1) so that it passes in the integer value of 1 to the parent component. How do I work around that? Thanks.
It's quite simple. Just change
<button onClick={this.props.onClick} value={1} >1</button>
to
<button onClick={() => this.props.onClick(1)} value={1} >1</button>
Cheers!
Try to do this without using arrow function definition inside props (bad for child components performance, as will always be a new function reference more info here)
Parent class does not need the arrow function defined when passing onClick to BettingChips, we can do this at the class level instead, using arrow function to lexically bind this...
class SomeParentComponent extends Component {
handleClick = (betAmount) => {
alert(betAmount)
}
render() {
return <BettingChips onClick={this.handleClick} />
}
}
In BettingChips use event to extract the value from the target of the event (the button that was clicked), so you don't need to use arrow function with amount as parameter
class BettingChips extends Component {
onClick = (e) => {
this.props.onClick(e.target.value)
}
render() {
return(
<div>
<button onClick={this.onClick} value={1}>1</button>
// ... etc
</div>
)
}
}
This must work for you:
class BettingChips extends Component{
render(){
return(
<div>
<button onClick={() => this.props.onClick(1)} value={1} >1</button>
// ... etc
</div>
)
};
One way could be to create a new function in your child component and call your parent function from props there. This is useful if you need to perform some more complicated logic when the button is clicked. Here is a code example:
class BettingChips extends Component{
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
onClick() {
// do something else or prepare parameters for parent's `onClick`
this.props.onClick(1);
}
render(){
return(
<div>
<button onClick={this.onClick} value={1}>1</button>
</div>
);
};
I'm new to React & Redux so it is difficult for me to explain in plain English but I'll try my best. I have a parent/container component that forms the main 'page' of my app. In this component I am rendering a header and various fields like so:
What I want to achieve is for any user input in the title field to be reflected where it currently says 'Untitled Practice' in the header.
The parent component looks like this (excluding various imports for brevity):
export class DrillCreator extends Component {
render() {
return (
<div>
<EditHeader />
<div className="container-fluid max-width-container">
<InputWithTooltip
type={'text'}
placeholderText={'Title'}
tooltipText={'Title Tooltip'}
required
/>
<InputWithTooltip
type={'textarea'}
placeholderText={'Summary'}
tooltipText={'Summary Tooltip'}
/>
<InputWithTooltip
type={'file'}
placeholderText={'Hero Image/Video'}
tooltipText={'Hero Image/Video Tooltip'}
/>
<InputWithTooltip
type={'select'}
options={['7', '8', '9', '10']}
placeholderText={'Ages'}
tooltipText={'Ages Tooltip'}
required
/>
</div>
</div>
);
}
}
The <InputWithTooltip /> component is essentially a container that renders the appropriate input along with a tooltip component:
export default class InputWithTooltip extends Component {
constructor(props) {
super(props);
this.state = {
textEntered: '',
};
}
render() {
let input = null;
if (this.props.type === 'text') {
input = (
<TextInput
placeholderText={this.props.placeholderText}
updateText={textEntered => this.setState({ textEntered })}
/>
);
} else if (this.props.type === 'select') {
input = (
<SelectInput
placeholderText={this.props.placeholderText}
updateText={textEntered => this.setState({ textEntered })}
/>
);
} else if (this.props.type === 'textarea') {
input = (
<TextAreaInput
placeholderText={this.props.placeholderText}
updateText={textEntered => this.setState({ textEntered })}
/>
);
} else if (this.props.type === 'file') {
input = (
<FileInput
placeholderText={this.props.placeholderText}
updateText={textEntered => this.setState({ textEntered })}
/>
);
}
return (
<div>
<InputTooltip tooltipText={this.props.tooltipText} />
{input}
</div>
);
}
}
As you can see, I have a textEntered state which is updated via an onChange function passed via the updateText props.
I have set up Redux so that I am able to call a dispatch function to set the title field in my reducer. This works fine if I simplify my parent component and simply call the <TextInput /> component which has the updateText prop:
export class DrillCreator extends Component {
constructor() {
super();
this.state = {
textEntered: '',
};
}
render() {
return (
<TextInput
placeholderText="Title"
updateText={(textEntered) => {
this.setState({ textEntered });
this.props.setDrillTitleAction({ textEntered });
}}
/>
);
}
}
const mapDispatchToProps = dispatch => ({
setDrillTitleAction: drillCreator => dispatch(setDrillTitle(drillCreator)),
});
export default connect(null, mapDispatchToProps)(DrillCreator);
The issue I have is that I want to call setDrillTitleAction from <InputWithTooltip /> instead of <TextInput /> as this is the only field in the form that I want to do anything special with.
Like I said I'm new to React and Redux so could be massively overcomplicating something or completely missing the point so any pointers would be massively helpful. Thanks.
You can pass the dispatch function from the container component in as a prop to the display component, and then call it when the value is changed.
In your case you need to pass setTitleDrillAction in as a prop to InputWithTooltip and then call if from within your updateText callbacks.
One thing to point out is that you will be storing the text box value multiple times - on the redux state and the InputWithTooltip state. You might choose to just make InputWithTooltip a stateless component which receives its value as a prop and dispatches updates to redux (via its parent container as described above).
I'm trying to wrap bootstrap into components with integrated form validation.
short:
Let's say I have
<Form>
<FieldGroup>
<Field rules={'required'}/>
</FieldGroup>
</Form>
Once Field pases validation, how can I notify FieldGroup (parent node) to add a class?
I created a simplified codepen version here
I would like depending on validation status, then change the state of FieldGroup So I can properly change the class name. (add has-warning has-danger etc) and ultimately add class to the Form component.
You need to pass a callback to the child component. I just forked your codepen and added some snippet as below.
http://codepen.io/andretw/pen/xRENee
Here is the main concept,
Make a callback function in "parent" component and pass it to the "child" component
i.e. The child component needs an extra prop to get the callback:
<Form>
<FieldGroup>
<Field rules={'required'} cb={yourCallbackFunc}/>
</FieldGroup>
</Form>
In <FieldGroup /> (parent):
class FieldGroup extends React.Component{
constructor(props){
super(props);
this.state = {
color: 'blue'
}
}
cb (msg) {
console.log('doing things here', msg)
}
render() {
const childrenWithProps = React.Children.map(this.props.children,
child => React.cloneElement(child, {
cb: this.cb
})
)
return (
<div class='fields-group'>
<label> field </label>
{ childrenWithProps }
</div>
);
}
};
In <Field /> (child):
class Field extends React.Component{
constructor(props){
super(props);
this.state = {
empty: true
}
this.validate = this.validate.bind(this);
}
validate(e){
let val = e.target.value;
console.log(!val);
this.setState({empty: !val});
//here to notify parent to add a color style!
// do call back here or you may no need to return.
this.props.cb(val)
return !val;
}
render() {
return (
<div>
<input type='text' onBlur ={(event) => this.validate(event)}/>
{this.state.empty && 'empty'}
</div>
);
}
};
And you can do the things you want in the callback function. (You can also pass a callback from <Form /> to the grandson and get it work, but you need to rethink the design of it is good or not.)