Reputation: 103
I'm quite newly to React Js and I'm really confusing about below scenario. I have an array called availableShops
in my parent component I just update it in child component using setState. I want to keep setState method in my child component also.
Parent:
import React, { Component } from 'react';
import Child from './shop-with-price';
export default class Parent extends Component {
constructor(props) {
super(props);
this.state = {
availableShops:[]
}
}
render{
return (
<div>
<Child
shops = {this.state.shops}
onChange = {this.handleInputChange}
avalilableShops = {this.state.avalilableShops}
/>
</div>
);
}
}
Child Component
import React, { Component } from 'react';
export default class Child extends Component {
constructor(props) {
super(props);
this.state = {
avalilableShops: this.props.avalilableShops,
}
}
handleInputChange(e) {
const target = e.target;
const name = target.name;
const value = target.value;
this.setState({
[name]: value
});
}
createShops(e){
e.preventDefault();
let shopName = this.state.shop_name;
let phonePrice = this.state.phone_price;
const phoneInfo = {'shop_name':shopName, 'phone_price':phonePrice};
this.setState((preState) => ({
avalilableShops: [...preState.avalilableShops, phoneInfo]
}), ()=>{ console.log(this.state.avalilableShops)});
}
render() {
const rows = this.state.avalilableShops.map((record,index) => {
return (
<tr key={index}>
<td>{record.shop_name}</td>
<td>{record.phone_price}</td>
<td>
<button
type="button"
className="btn btn-primary"
// onChange={this.createShops}
>
Delete
</button>
</td>
</tr>
);
});
return (
<div>
<label>Add shops that phone already available...</label>
<div className="row">
<div className="col-md-6">
<div className="form-group">
<select
name="shop_name"
className="form-control select2"
onChange={this.handleInputChange}
>
{this.state.shops.map((shops, index) => (
<option key={index} value={shops.name}>
{shops.name}
</option>
))}
;
</select>
</div>
</div>
<div className="col-md-4">
<div className="form-group">
<input
type="number"
name="phone_price"
placeholder="Price"
className="form-control"
value={this.state.phone_price}
onChange={this.handleInputChange}
/>
</div>
</div>
<div className="col-md-2">
<button
type="button"
className="btn btn-primary"
onClick={this.createShops.bind(this)}
>
Add
</button>
</div>
</div>
<br />
<table className="table table-striped">
<thead>
<tr>
<th scope="col">Shop name</th>
<th scope="col">Price</th>
<th scope="col">Remove</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
</div>
);
}
}
I want to all these changes happen in child component because & within createShop method I update my availableShops
using spread operator. Rendering components fine. How ever availableShops
array doesn't update now but I want to use it in parent component also. Please help me to figure out this problem.
Upvotes: 0
Views: 3084
Reputation:
I have updated in your code and make it working for you.
Here I have created demo for you
https://codesandbox.io/s/smoosh-night-33yn4
Parent Component
constructor(props) {
super(props);
this.state = {
availableShops: []
};
this.createShops = this.createShops.bind(this);
}
createShops(shop_name, phone_price) {
this.setState(preState => ({
availableShops: [...preState.availableShops, { shop_name, phone_price }]
}));
}
render() {
return (
<div>
<Child
createShops={this.createShops}
availableShops={this.state.availableShops}
/>
</div>
);
}
Child Component
import React, { Component } from "react";
export default class Child extends Component {
constructor(props) {
super(props);
this.state = {
shop_name: "Shop 1",
phone_price: ""
};
this.shops = [{ name: "Shop 1" }, { name: "Shop 2" }, { name: "Shop 3" }];
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(e) {
const { name, value } = e.target;
this.setState({ [name]: value });
}
render() {
const rows =
this.props.availableShops &&
this.props.availableShops.map((record, index) => {
return (
<tr key={index}>
<td>{record.shop_name}</td>
<td>{record.phone_price}</td>
<td>
<button
type="button"
className="btn btn-primary"
// onChange={this.createShops}
>
Delete
</button>
</td>
</tr>
);
});
return (
<div>
<label>Add shops that phone already available...</label>
<div className="row">
<div className="col-md-6">
<div className="form-group">
<select
name="shop_name"
className="form-control select2"
onChange={this.handleInputChange}
>
{this.shops &&
this.shops.map((shops, index) => (
<option key={index} value={shops.name}>
{shops.name}
</option>
))}
;
</select>
</div>
</div>
<div className="col-md-4">
<div className="form-group">
<input
type="number"
name="phone_price"
placeholder="Price"
className="form-control"
value={this.state.phone_price}
onChange={this.handleInputChange}
/>
</div>
</div>
<div className="col-md-2">
<button
type="button"
className="btn btn-primary"
onClick={() => {
debugger;
const { shop_name, phone_price } = this.state;
this.props.createShops(shop_name, phone_price);
}}
>
Add
</button>
</div>
</div>
<br />
<table className="table table-striped">
<thead>
<tr>
<th scope="col">Shop name</th>
<th scope="col">Price</th>
<th scope="col">Remove</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
</div>
);
}
}
Output
Hope this will work for you!
Upvotes: 1
Reputation: 20755
Don't store props
into state
in child component,
avalilableShops: this.props.avalilableShops,
Instead you can directly use props
to iterate and show your data in child component,
const rows = this.props.avalilableShops.map((record,index) => { //access data using this.props
...
}
Do the same for shops,
{this.props.shops.map((shops, index) => ( //access shops using this.props
<option key={index} value={shops.name}>
{shops.name}
</option>
))}
As you need data to be accessible in parent component you should have a function in parent component which will change the state in parent component. Pass that funtion to child component,
Create a function in parent component,
createShops = (phoneInfo) => {
this.setState({
avalilableShops: [...this.state.avalilableShops, phoneInfo]
}, ()=>{ console.log(this.state.avalilableShops)});
}
pass this function to child component,
<Child
shops = {this.state.shops}
onChange = {this.handleInputChange}
avalilableShops = {this.state.avalilableShops}
createShops = {this.createShops}
/>
Now in child component you need to call the function in parent component to update the state,
createShops(e){
e.preventDefault();
let shopName = this.state.shop_name;
let phonePrice = this.state.phone_price;
const phoneInfo = {'shop_name':shopName, 'phone_price':phonePrice};
this.props.createShops(phoneInfo); //This will update the state in parent component.
}
Upvotes: 2
Reputation: 1817
I would avoid maintaining state of avialableShops in both the parent and child. Rather, I would maintain state of the availableShops only in your parent component, which can be updated by passing a function to the child component. The child component can access the available shops by way of the parents state through props, and should rerender when the parent's state/its props are updated. Here a simplified solution:
export default class Parent extends Component {
constructor(props) {
super(props);
this.state = { availableShops:[] }
}
handleUpdateShops = newShop => {
{ availableShops: oldShops } = this.state;
this.setState({ availableShops: [...oldShops, newShop] });
}
render{
return (
<div>
<Child
{...this.props}
{...this.state}
handleUpdateShops={this.handleUpdateShops}
/>
</div>
);
}
}
export default class Child extends Component {
constructor(props) {
super(props);
}
handleInputChange(e) {
...
}
createShops(e) {
e.preventDefault();
let shopName = this.state.shop_name;
let phonePrice = this.state.phone_price;
const phoneInfo = {'shop_name':shopName, 'phone_price':phonePrice};
this.props.handleUpdateShops(phoneInfo);
}
render() {
// this.props.availableShops available to map here
}
}
Upvotes: 0