Binoj123
Binoj123

Reputation: 103

Updating props from child component in React JS

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

Answers (3)

user11910739
user11910739

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

enter image description here

Hope this will work for you!

Upvotes: 1

ravibagul91
ravibagul91

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

Greg Brodzik
Greg Brodzik

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

Related Questions