I have the following parent component:
class NewPetForm extends React.Component {
state = {
name: '',
age: '',
animal: '',
breed: ''
};
render() {
return (
<StyledNewPetForm >
<Input label="name" />
<Input label="age" />
<Input label="animal"/>
<Input label="breed"/>
<Button type="submit" />
</StyledNewPetForm>
);
}
}
And the following child component:
class Input extends React.Component {
render() {
return (
<StyledWrapper>
<StyledInput
value={this.props.test}
type="text"
/>
</StyledWrapper>
);
}
}
Is it possible to listen to changes in all inputs in children components and update the state accordingly?
What I want to achieve is basically passing handlers to different Inputs and update state dynamically, so there is a possibility to reuse Input component.
changeHandler = (event,stateName) =>{
this.setState({[stateName]:event.target.value]})
}
<Input changed={changeHandler(event,'name')}></Input>
<Input changed={changeHandler(event,'age')}></Input>
<Input changed={changeHandler(event,'breed')}></Input>
// Inside Input
<input onChange={this.props.changed}/>
Thanks for help!
Yes you can do that, you can pass a function to the child element as a property and call it when a change occurs in the child.
Here is an example:
class NewPetForm extends React.Component {
state = {
name: ''
};
onValueChange(key, event) {
this.setState({[key]: event.target.value})
}
render() {
return (
<StyledNewPetForm >
<Input value={this.state.name} onValueChange={this.onValueChange.bind(this, 'name')} />
</StyledNewPetForm>
);
}
}
and the child
class Input extends React.Component {
render() {
return (
<StyledWrapper>
<StyledInput
onChange={this.props.onValueChange}
value={this.props.value}
type="text"
/>
</StyledWrapper>
);
}
}
{[key]: event.target.value} may seem confusing, is just the new syntax that lets you use a string as a property name in an object literal.
The important part is onChange={this.props.onValueChange}, here, I'm calling the parent function NewPetForm.onValueChange when the input's value changes.
Related
How can I do a 2 way binding of a variable from the parent component (Form.js), such that changes occurred in the child component (InputText.js) will be updated in the parent component?
Expected result: typing values in the input in InputText.js will update the state of Form.js
In Form.js
render() {
return (
<div>
<InputText
title="Email"
data={this.state.formInputs.email}
/>
<div>
Value: {this.state.formInputs.email} // <-- this no change
</div>
</div>
)
}
In InputText.js
export default class InputText extends React.Component {
constructor(props) {
super(props);
this.state = props;
this.handleKeyChange = this.keyUpHandler.bind(this);
}
keyUpHandler(e) {
this.setState({
data: e.target.value
});
}
render() {
return (
<div>
<label className="label">{this.state.title}</label>
<input type="text" value={this.state.data} onChange={this.handleKeyChange} /> // <-- type something here
value: ({this.state.data}) // <-- this changed
</div>
)
}
}
You can manage state in the parent component itself instead of managing that on child like this (lifting state up):
In Form.js
constructor(props) {
super(props);
this.handleKeyChange = this.keyUpHandler.bind(this);
}
keyUpHandler(e) {
const { formInputs } = this.state;
formInputs.email = e.target.value
this.setState({
formInputs: formInputs
});
}
render() {
// destructuring
const { email } = this.state.formInputs;
return (
<div>
<InputText
title="Email"
data={email}
changed={this.handleKeyChange}
/>
<div>
Value: {email}
</div>
</div>
)
}
In InputText.js
export default class InputText extends React.Component {
render() {
// destructuring
const { title, data, changed } = this.props;
return (
<div>
<label className="label">{title}</label>
<input type="text" value={data} onChange={changed} />
value: ({data})
</div>
)
}
}
You can also make your InputText.js a functional component instead of class based component as it is stateless now.
Update: (How to reuse the handler method)
You can add another argument to the function which would return the attribute name like this:
keyUpHandler(e, attribute) {
const { formInputs } = this.state;
formInputs[attribute] = e.target.value
this.setState({
formInputs: formInputs
});
}
And from your from you can send it like this:
<input type="text" value={data} onChange={ (event) => changed(event, 'email') } />
This assumes that you have different inputs for each form input or else you can pass that attribute name also from parent in props to the child and use it accordingly.
You would need to lift state up to the parent
parent class would look something like
onChangeHandler(e) {
this.setState({
inputValue: e.target.value // you receive the value from the child to the parent here
})
}
render() {
return (
<div>
<InputText
title="Email"
onChange={this.onChangeHandler}
value={this.state.inputValue}
/>
<div>
Value: {this.state.inputValue}
</div>
</div>
)
}
children class would look something like
export default class InputText extends React.Component {
constructor(props) {
super(props);
this.state = props;
}
render() {
return (
<div>
<label className="label">{this.state.title}</label>
<input type="text" value={this.state.value} onChange={this.props.onChange} />
value: ({this.state.value})
</div>
)
}
}
You can simply pass a callback from Form.js to InputText and then call that callback in InputText on handleKeyChange
I want to access the state of my parent component SubmitForm and return the result by returning a span element with the query result.
The fake DB or JSON file is:
const citi = [
{
pc: '13000',
city: 'berlin',
population: '10million'
}, {
pc: '81000',
city: 'munich'
}
];
These are the change and submit event handlers:
handleChange(event) {
this.setState({ value: event.target.value });
}
handleSubmit(event) {
alert('We will find the city for postal code: ' + this.state.value);
event.preventDefault();
var result = citi.filter(obj => obj.pc === this.state.value)[0].city;
console.log(result);
this.setState({ value: result });
}
And the render method
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Search by postcode:
<input type="text" value={this.state.value} onChange={this.handleChange}
/>
</label>
<button type="submit" className="btn btn-default">Search</button>
<ComponentB />
</form>
);
}
This is the child component
class ComponentB extends React.Component {
constructor(props) {
super(props);
// this.state = {val: 'hdgfh'};
}
render() {
return <span>The postcode is in : {this.props.value}</span>;
}
}
When i render all in the parent component, everything works, but how can I render in the parent and display the result?
Console logs the right result, but all questions regarding accessing parent/children state were not helpful enough to me.
Codepen link:
https://codepen.io/damPop/pen/ReXwoo?editors=0010
You would need to pass down the value through props (they are not inherited)
so
<ComponentB value={this.state.value} />
Updated pen: https://codepen.io/anon/pen/jQNvOe?editors=0010
You should pass the parent's state as a prop like so:
<ComponentB value={this.state.value}/>
I have 2 React parent/child components. The Child Component has a button that adds +1 to the previous state of the Parent Component, and a Form that triggers a handleChange function for the onChange event.
The Problem
From the Form input, I want to trigger a function that sets the State to the previous State, + the input in the Form.
For example, if I write 50 in input and hit submit I want the new state be 100
Here is a codesandbox: https://codesandbox.io/s/30mz2vvyo1
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 50
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState((prevState) => {
return { value: prevState.value + 1 }
});
}
handleSubmit(event) {
event.preventDefault();
}
render() {
return (
<div>
<Child value={this.state.value} handleChange={this.handleChange} handleSubmit={this.handleSubmit} />
</div>
)
}
}
class Child extends React.Component {
render() {
return (
<div>
<button onClick={this.props.handleChange}>Count + 1</button>
<div>{this.props.value}</div>
<form onSubmit={this.props.handleSubmit}>
<label>
Name:
<input type="text" onChange={this.props.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
</div>
)
}
}
The problem you are facing can be mitigated by;
You need to have two different variables in state. value, can hold your value. You also need to hold the current value of the input, let's call it inputNumber.
You need to provide an onClick function to your button. In said function, set your state in the following fashion;
Code:
this.setState({
value: this.state.value + this.state.inputNumber,
})
After doing these things, it should work as expected.
I have updated your codesandbox, you can take a look at it here.
I have a form component that has a state containing an array of items.
I am having a hard time trying to update the state of the form when one of the item inputs gets updated.
At first I was creating a state on the items themselves and updating the values using the following code:
class ItemRow extends Component{
constructor(props){
super(props)
this.state = this.props.item;
}
updateItem(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
.....
render(){
return (
<FormControl
type="text"
name="name"
value={this.state.name}
onChange={this.updateItem}
/>
<FormControl
type="text"
name="price"
value={this.state.price}
onChange={this.updateItem}
/>
.....
)
}
}
This worked fine for updating the value of the of the inputs, however the state was local to the item and not reflected or accessible by the form
I am trying to figure out how to keep the state in the form and have the item update the state of the form
I think this is the right approach but I can't figure out how to get it to work.
At this point I have something similar the following:
class Form extends Component{
this.state = {
items: [
{ name: 'soup', price: 7, quantity: 1 }
{ name: 'salad', price: 5, quantity: 2 }
]
}
updateItem(e) {
// Not sure how to handle updating
}
removeItem(item) {
let items = this.state.items;
items.splice(items.indexOf(item), 1);
this.setState({items: items})
}
render(){
return(
<ItemTable items={this.state.items} updateItem={this.updateItem} removeItem={this.removeItem} />
)
}
}
ItemTable:
class ItemTable extends Component {
removeItem(item){
this.props.removeItem(item)
}
render(){
let items = [];
this.props.items.forEach((item) => {
items.push(<ItemRow item={item} key={item.id} removeItem={this.removeItem.bind(this,item)} updateItem={this.props.updateItem}/>);
});
return(
{items}
)
}
}
ItemRow:
class ItemRow extends Component {
removeItem(item){
this.props.removeItem(item)
}
render() {
return (
<FormControl
type="text"
name="name"
value={this.props.item.name}
onChange={this.updateItem}
/>
<FormControl
type="text"
name="quantity"
value={this.props.item.quantity}
onChange={this.updateItem}
/>
<FormControl
type="text"
name="price"
value={this.props.item.price}
onChange={this.updateItem}
/>
<Button bsStyle="warning" onClick={this.removeItem}><Glyphicon glyph="trash"/></Button>
)
}
}
You're very close to the solution.
If you need to have a state shared between components, you should have it in the most parent component that should be aware of the state (in your case the Form component).
You pass down as props the method "updateItem" from the Form to the ItemTable and then ItemRow (like you're doing)
At this stage, inside the ItemRow you can use the method by calling 'this.props.updateItem' and you can run the function defined in Form, passing some parameters, if you need to.
I have a form in react with many input components. I do not like that I have to write a new onChange handler method for every input component that I build. So I want to know how can I stop repeated code.
<Input
label={"Blog Name"}
hint={"e.g. 'The Blog'"}
type={"text"}
value={this.state.name}
onChange={this.handleInputChange.bind(this, "name")}
/>
<Input
label={"Blog Description"}
hint={"e.g. 'The Blog Description'"}
type={"text"}
value={this.state.desc}
onChange={this.handleInputChange.bind(this, "desc")}
/>
So instead of writing a new function I am reusing the same function and passing an extra value. Is this the right way to do it? How do other experienced people solve this problem.
If you want your parent component to maintain the state with the value of each input field present in 'Input' child components, then you can achieve this with a single change handler in the following way:
handleChange(id, value) {
this.setState({
[id]: value
});
}
where the id and value are obtained from the Input component.
Here is a demo: http://codepen.io/PiotrBerebecki/pen/rrJXjK and the full code:
class App extends React.Component {
constructor() {
super();
this.state = {
input1: null,
input2: null
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(id, value) {
this.setState({
[id]: value
});
}
render() {
return (
<div>
<Input id="input1"
changeHandler={this.handleChange} />
<Input id="input2"
changeHandler={this.handleChange} />
<p>See input1 in parent: {this.state.input1}</p>
<p>See input2 in parent: {this.state.input2}</p>
</div>
);
}
}
class Input extends React.Component {
constructor() {
super();
this.state = {
userInput: null
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
const enteredText = event.target.valuel
this.setState({
userInput: enteredText
}, this.props.changeHandler(this.props.id, enteredText));
}
render() {
return (
<input type="text"
placeholder="input1 here..."
value={this.state.userInput}
onChange={this.handleChange} />
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
You can try event delegation, just like the traditional ways.
That is, just bind a function to the parent form element, and listens to all the events bubbling up from the children input elments.