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}/>
Related
I'm running into a problem when passing down down a method from a parent component to a child component. The parent, FilterableProductTable has a state called filterText. FilterableProductTable renders a child component SearchBar, and passes down a function called handleChange as a prop. SearchBar calls this function onChange, so that I can transfer the user input from SearchBar to the filterText state in FilterableProductTable.
The problem I'm running into is filterText gets updated one increment too late. I logged filterText to the console and I logged the value of the user input, both in my handleChange function:
handleChange(event) {
this.setState({ filterText: event.target.value });
console.log(event.target.value + "value");
console.log(this.state.filterText + "state");
}
and the output I get in the console is:
//user input=a.
a value
state
//user input=ab
ab value
a state
//user input =a, because b was deleted(backspace key)
a value
ab state
-----As you can see the state is one increment behind the event.target.value. I'm not sure how to fix this. Below are my two functions. If someone could help me see what I'm doing wrong that would be great.
class SearchBar extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<form>
<input
type="text"
name="name"
placeholder="Search.."
onChange={this.props.handleChange}
></input>
<br />
<input type="checkbox" /> Only show items in stock
</form>
);
}
}
class FilterableProductTable extends React.Component {
constructor(props) {
super(props);
this.state = {
filterText: "",
inStockOnly: false,
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({ filterText: event.target.value });
console.log(event.target.value + " value");
console.log(this.state.filterText + " state");
}
render() {
console.log(this.state.filterText + " render");
return (
<div>
<SearchBar handleChange={this.handleChange} />
<ProductTable
products={this.props.products}
filterText={this.state.filterText}
inStockOnly={this.state.inStockOnly}
/>
</div>
);
}
}
That's because setting state in React is an asynchronous operation and won't be affected immediately. you need to use the setState callback for your check like this:
handleChange(event){
this.setState({filterText:event.target.value}, () => {
console.log(event.target.value+ ' value');
console.log(this.state.filterText+ ' state');
});
}
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.
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.