0

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.

6
  • Why do you send availableShops from the parent component to the child component if it's empty? Commented Sep 14, 2019 at 10:12
  • unless how I used avalilableShops in my parent component? Commented Sep 14, 2019 at 10:15
  • You have to apply single source of truth in your components, don't change variable values in more than one component Commented Sep 14, 2019 at 10:20
  • Ok, assume I define it in a child as Empty its working fine. But I want to use it in form in parent component, How do I do it? Commented Sep 14, 2019 at 10:24
  • Hi Binoj, check my solution and let me know if that helps. Commented Sep 14, 2019 at 10:38

3 Answers 3

2

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.
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for reply..I have stated availableShops as avalableShops in my child component..That's why all these happens
1

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!

1 Comment

Thank you for reply, I have already tried correct ways..Unfortunately I missed passed variable name availableShops as avalableShops in my code
0

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
    }
  }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.