onChange function on created select component

Viewed 49

I created a component called SelectPortfolio, that is a select button that fetch the options from an api. the file name is:

SelectPortfolio.js

import React from 'react';
import { Label, } from 'reactstrap'
import { apiHost } from '../../config/apiHost.js';

class SelectPortfolio extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
        // asset: this.props.asset,
        portfolio_options:[]
    };
  }

  componentDidMount() {
    this.fetchData();
  }
  
  fetchData() {
    fetch(apiHost + '/api/portfolio/options')
    .then(res => res.json())
    .then(
      (result) => {
        this.setState({
          isLoaded: true,
          portfolio_options: result
        });
      },
      (error) => {
        this.setState({
          isLoaded: true,
          error
        });
      }
    )
}

render() {
  const { error, isLoaded, portfolio_options  } = this.state;
  if (error) {
    return <div>Error: {error.message}</div>;
  } else if (!isLoaded) {
    return <div>Loading...</div>;
  } else {
    return (
      <>
      <Label for="portfolio_id">Portfolio</Label>
      <select value={this.props.asset.portfolio_id} onChange={this.setPortfolio_id} className="form-control">
        <option value="" disabled selected>Select your option</option>
        {portfolio_options.map((option) => (
          <option value={option.value} key={option.value}>{option.label}</option>
        ))}
      </select>
    </>
    );
  }
}
}

export default SelectPortfolio;

And this component is used in another file, where is the Form:

FiiForm.jsx

import React, { Component } from 'react'
import { Redirect } from 'react-router'
import { Container, Row, Col, Alert, Form} from 'reactstrap'
import SelectPortfolio from '../../components/selects/SelectPortfolio'

const Api = require('./Api.js')


class FiiForm extends Component {
  constructor(props) {
    super(props)

    this.state = {
      fii: {
        id: this.getFiiId(props),
        category_id: '',
        portfolio_id: '',
        amount: '',
        cost: '',
      },
      redirect: null,
      errors: []
    }

    this.setCategory_id = this.setCategory_id.bind(this)
    this.setPortfolio_id = this.setPortfolio_id.bind(this)
    this.setAmount = this.setAmount.bind(this)
    this.setCost = this.setCost.bind(this)

    this.handleSubmit = this.handleSubmit.bind(this)
  }


  getFiiId(props) {
    try {
      return props.match.params.id
    } catch (error) {
      return null
    }
  }

  setCategory_id(event) {
    let newVal = event.target.value || ''
    this.setFieldState('category_id', newVal)
  }

  setPortfolio_id(event) {
    let newVal = event.target.value || ''
    this.setFieldState('portfolio_id', newVal)
  }

  setAmount(event) {
    let newVal = event.target.value || ''
    this.setFieldState('amount', newVal)
  }

  setCost(event) {
    let newVal = event.target.value || ''
    this.setFieldState('cost', newVal)
  }

  setFieldState(field, newVal) {
    this.setState((prevState) => {
      let newState = prevState
      newState.fii[field] = newVal
      return newState
    })
  }

  handleSubmit(event) {
    event.preventDefault()

    let fii = {
      category_id: this.state.fii.category_id,
      portfolio_id: this.state.fii.portfolio_id,
      amount: this.state.fii.amount,
      cost: this.state.fii.cost,
    }

    Api.saveFii(fii, this.state.fii.id)
      .then(response => {
        const [error, errors] = response
        if (error) {
          this.setState({
            errors: errors
          })
        } else {
          this.setState({
            redirect: '/fiis'
          })
        }
      })
  }

  componentDidMount() {
    this.fetchData();
    if (this.state.fii.id) {
      Api.getFii(this.state.fii.id)
        .then(response => {
          const [error, data] = response
          if (error) {
            this.setState({
              errors: data
            })
          } else {
            this.setState({
              fii: data,
              errors: []
            })
          }
        })
    }
  }

  render() {
    const { redirect, fii, errors, portfolio_options } = this.state

    if (redirect) {
      return (
        <Redirect to={redirect} />
      )
    } else {

      return (
        <Container>
          <Row>
            <Col>
              <h3 className="mt-4 mb-4">Edit</h3>

              {errors.length > 0 &&
                <div>
                  {errors.map((error, index) =>
                    <Alert color="danger" key={index}>
                      {error}
                    </Alert>
                  )}
                </div>
              }
              
              <Form onSubmit={this.handleSubmit}>
                <Row>
                  <Col md={ 4 }>
                    <SelectPortfolio portfolio_options={portfolio_options} asset={fii}/>
                  </Col>
                  <Col md={ 3 } >
                    <Label for="amount">Amount</Label>
                    <Input type="text" name="amount" id="amount" value={fii.amount} placeholder="Enter amount" onChange={this.setAmount} />
                  </Col>
                  <Col md={ 3 }>
                    <Label for="cost">Cost</Label>
                    <Input type="text" name="cost" id="cost" value={fii.cost} placeholder="Enter cost" onChange={this.setCost} />
                    </Col>
                </Row>
                <button className="btn btn-success mt-4">Submit</button>
              </Form>
            </Col>
          </Row>
        </Container>
      )
    }
  }
}

export default FiiForm

But, the function onChange for the select is not working, this function works ok for the other fields.

i tried around different solutions, but could not figure out where is my mistake.

Thanks in advance.

1 Answers

The onChange event is referencing a function that doesn't exist:

onChange={this.setPortfolio_id}

That component has no function called setPortfolio_id. It looks like your intent is to pass that function to the component as a prop. Start by passing it to the component:

<SelectPortfolio
  portfolio_options={portfolio_options}
  asset={fii}
  onChange={this.setPortfolio_id}
/>

Then in the component you can use that prop. It's been a while since I've used class-based components and I don't recall the convention for this, but I imagine you'd just store the reference somewhere to be used. For example:

constructor(props) {
  super(props);
  this.state = {
    // asset: this.props.asset,
    portfolio_options:[]
  };
  this.handleChange = props.onChange;
}

This should store the function reference in handleChange at the class level, allowing you to use it:

<select
  value={this.props.asset.portfolio_id}
  onChange={this.handleChange}
  className="form-control"
>
Related