TypeError: Cannot read property ‘map’ of undefined while creating a dropdown select in react

Tags: , ,



I’m trying to create a dropdown select list that is dependent on it’s parent list i.e Category and then a subcategory for a clothing catalogue. The plan is to then store that data in firestore but whenever i use map, I keep running into this error that tells me “Cannot read property ‘map’ of undefined”. I’ve tried putting the map block in an if statement but that does’nt work either.

class Catalogue extends Component{
  
  constructor(){
    super();
    this.state={
      prodname:'',
      prodprice:'',
      prodcat:'',
      prodsize:'',
      proddetails:'',
      selectedView:'Men'

    };

  }
  updateInput = e => {
    this.setState({
      [e.target.name]: e.target.value
    });
  }
  
  addProd = e => {
    e.preventDefault();
    const db = firebase.firestore();
  
    const userRef = db.collection("Users").doc("User1").set({
        prodname: this.state.prodname,
        prodprice: this.state.prodprice,
        prodcat: this.state.prodcat,
        prodsize: this.state.prodsize,
        proddetails: this.state.proddetails,
    });  
    this.setState({
        prodname:'',
        prodprice:'',
        prodcat:'',
        prodsize:'',
        proddetails:'',
        selectedView:'Men'
    });
  };

  render(){

    //==================Subcategories Code====================//
    const { selectedView } = this.state
    const VIEWS = [
      {
        name: 'Men', 
        minor: ['Shirts', 'Pants']
      }, {
        name: 'Women', 
        minor: ['Shirt', 'Skirt']
      }
    ]
    const getMajorMethod = () => {
      const view = VIEWS.filter(({name}) => name === selectedView)
      return (
        <div>
          <select>
            {view.minor.map(m => <option>{m}</option>)}
          </select>
        </div>
      )
    }

    return(
      <div id="container">
            <Form onSubmit={this.addProd}>
      <FormGroup row>
        <Label for="prod_name" sm={2}>Product Name</Label>
        <Col sm={10}>
          <Input type="text" name="prodname" id="prod_name" onChange={this.updateInput} value={this.state.prodname}/>
        </Col>
      </FormGroup>
      <FormGroup row>
        <Label for="prod_price" sm={2}>Price Rs:</Label>
        <Col sm={10}>
          <Input type="number" name="prodprice" id="prod_price" onChange={this.updateInput} value={this.state.prodprice} />
        </Col>
      </FormGroup>
      <FormGroup row>
        <Label for="prod_cat" sm={2}>Category</Label>
        <Col sm={10}>
          <Input type="select" name="prodcat" id="prod_cat" onChange={this.updateInput} value={this.state.prodcat} >
           <option disabled="disabled" value="">Select Category</option>
           <option value="Men">Men</option>
           <option value="Women">Women</option>
          </Input>
        </Col>
      </FormGroup>

      <FormGroup row>
        <Label for="prod_subcat" sm={2}>Sub Category</Label>
        <Col sm={10}>
         <select onChange={(e) => this.setState({selectedView: e.target.value})}>
          {VIEWS.map(({name}) => <option value={name}>{name}</option>)}
         </select>
        {getMajorMethod()}
        </Col>
      </FormGroup>

      <FormGroup row>
        <Label for="prod_size" sm={2}>Size</Label>
        <Col sm={10}>
          <Input type="select" name="prodsize" id="prod_size" onChange={this.updateInput} value={this.state.prodsize}>
           <option disabled="disabled" value="">Select Size</option>
           <option value="S">S</option>
           <option value="M">M</option>
           <option value="L">L</option>
           <option value="XL">XL</option>
          </Input>
        </Col>
      </FormGroup>
      <FormGroup row>
        <Label for="prod_details" sm={2}>Product Details</Label>
        <Col sm={10}>
          <Input type="textarea" name="proddetails" id="prod_details" onChange={this.updateInput} value={this.state.proddetails} />
        </Col>
      </FormGroup>
      <FormGroup check row>
        <Col sm={{ size: 10, offset: 2 }}>
          <Button>Submit</Button>
        </Col>
      </FormGroup>
    </Form>

    </div>
    );
  }
}

export default Catalogue;

Answer

The array filter function returns an array, so view.minor is not valid, since view is an array.

You can use array.protoype.find instead to return the view object you want to access into and map the minor array of.

The find() method returns the value of the first element in the provided array that satisfies the provided testing function.

It can return undefined if no element in the array is found, so you will want to do a null check and conditionally render the select and options only if view is a defined object.

const getMajorMethod = () => {
  const view = VIEWS.find(({name}) => name === selectedView)
  return view ? (
    <div>
      <select>
        {view.minor.map(m => <option>{m}</option>)}
      </select>
    </div>
  ) : null
}


Source: stackoverflow