2

I'm still a novice at the React framework. I'm working on a project where I would like to allow the user to create select fields. I am currently using react-select to render the select fields or dropdowns. I need the values that they selected from the select input fields. The problem is the user needs to be able create multiple input fields. Each input field must have a unique key. I want to be able to retrieve the values as they as they choose them.

What follows is what I have so far:

 class OptionsComponent extends React.Component{
  constructor(props){
   super(props);
   this.state = {
    fields: [],
    parentArray: [],
    optionsArray: [],
    selectedOption:'',

   };

this.handleSelect = this.handleSelect.bind(this); 
}

 handleSelect = (event, index) => {
  console.log(event);
  console.log(optionsArray);
  console.log(selectedOption);
  const optionsArray = [...this.state.optionsArray];
  let selectedOption = event.label;
  optionsArray[index]= event.target.value;

  this.setState({
    optionsArray,
    selectedOption: event.label,
  })}

 render(){

return(

  <div className="">
    <div className="optionsArea">

      { this.state.parentArray.map((el, index) =>
        <div key={index} className="selectDropdown ">
            { this.state.optionsArray.map((el, index) => {
              return <Select
              key={index}
              name="optionsSelect"
              className="userOptionSelectDropdown"
              placeholder="Select Variable"
              value={this.state.selectedOption}
              onChange={(event) => this.handleSelect(event)}
              onSelect={(event) => this.handleSelect(event)}
              options={this.state.fields.map((field, index) => {
              return {value: index, label: field.name;
            })} />
          })}
        </div>
      )
    }
    </div>

  </div>

    );
   }
 }

I apologize in advance if this is a duplicate. I checked the similar questions and did a google search and I couldn't find anything. Right now when I use this code in my component, it doesn't render anything. I'm not really sure why. Thanks for your help.

2 Answers 2

3

You can set the name of the component and get it during your handleSelect and get the value directly (works if you use custom controls from bootstrap 4 for example). Remember to use a different name for the index in the options.

handleSelect = (event) => {
    let name = event.target.name;
    let value = event.target.value; 

    // perform action

    ...

}

and then in your render generate a unique name in the map, something like name={"optionsSelect_" + el + "_" + index} or whatever you like:

...

{ this.state.parentArray.map((el, index) =>
    <div key={index} className="selectDropdown ">
        { this.state.optionsArray.map((el, index) => {
          return <Select
          key={index}
          name={"optionsSelect_" + el + "_" + index}
          className="userOptionSelectDropdown"
          placeholder="Select Variable"
          value={this.state.selectedOption}
          onChange={(event) => this.handleSelect(event)}
          onSelect={(event) => this.handleSelect(event)}
          options={this.state.fields.map((field, index2) => {
          return {value: index2, label: field.name;
        })} />
      })}
    </div>
  )
}

...

If setting the name doesn't work, you can try references as well:

...

{ this.state.parentArray.map((el, index) =>
    <div key={index} className="selectDropdown ">
        { this.state.optionsArray.map((el, index) => {
          return <Select
          key={index}
          ref={a => (this["optionsSelect_" + el + "_" + index] = a)}
          name={"optionsSelect_" + el + "_" + index}
          className="userOptionSelectDropdown"
          placeholder="Select Variable"
          value={this.state.selectedOption}
          onChange={(event) => this.handleSelect(event)}
          onSelect={(event) => this.handleSelect(event)}
          options={this.state.fields.map((field, index2) => {
          return {value: index2, label: field.name;
        })} />
      })}
    </div>
  )
}
...

and then get it in your handle (you get the complete component):

handleSelect = (event) => {
    let name = event.target.name;
    let element = this[name]; 

    // perform action

    ...

}

There are a lot of ways of getting the selected value using pure javascript, have a look here.

This is very important!

if you need to modify the state based on a previous state, don't do this:

    ...
    const optionsArray = [...this.state.optionsArray];
    let selectedOption = event.label;
    optionsArray[index]= event.target.value;

    this.setState({
        optionsArray,
        selectedOption: event.label,
    })
    ...

The setState also receives a function, and will give you the previous state for you to handle it, make any changes you need, and ENSURE that they will be set correctly.

    ...    
    this.setState(prevState => {
        // get current state
        let newOptionsArray = prevState.optionsArray;
        let selectedOption = event.label; // or whatever
        newOptionsArray[index]= event.target.value; // or whatever

        return({
            optionsArray: newOptionsArray,
            selectedOption: event.label,
        });
    });
    ...

Have a read in the official docs to understand how setState works.

Sign up to request clarification or add additional context in comments.

2 Comments

Are refs considered to be bad practice? I thought I read that refs are supposed to be considered a last resort. Or are they okay to use in React?
If you can avoid them don't it's better to use alternative approaches, but if there is no alternative use them. I always use the handle approach for this case, and get the name and value using the handle, updating the state with these.
1

Not sure I understood exactly what you're asking/trying to do, but I believe this will do it:

  1. Intializes a Select field with options
  2. Creates a new Select field with options from user input
  3. Each Field stores the selected option (if one was selected)
  4. Clicking "Submit Form" shows all the selected options

Working example: https://codesandbox.io/s/81jlqp31p9

import each from "lodash/each";
import map from "lodash/map";
import isEmpty from "lodash/isEmpty";
import React, { Component, Fragment } from "react";
import Select from "react-select";

export default class DynamicSelect extends Component {
  state = {
    Fields: [
      {
        name: "Field0",
        options: [
          { value: "chocolate", label: "Chocolate" },
          { value: "strawberry", label: "Strawberry" },
          { value: "vanilla", label: "Vanilla" }
        ]
      }
    ],
    selectedOptions: {}
  };

  clearSelections = () => this.setState({ selectedOptions: [] });

  handleChange = (name, value) => {
    this.setState(prevState => ({
      selectedOptions: [
        ...this.state.selectedOptions,
        {
          name: value
        }
      ]
    }));
  };

  handleAddField = () => {
    this.setState(prevState => {
      const { newFields } = this.state;
      if (!newFields) return;
      const arr = newFields.split(",");
      const options = map(arr, val => ({ value: val, label: val }));

      return {
        newFields: "",
        Fields: [
          ...this.state.Fields,
          {
            name: `Field${this.state.Fields.length}`,
            options
          }
        ]
      };
    });
  };

  handleFieldChange = e => {
    this.setState({ newFields: e.target.value });
  };

  showSelectedOptions = selectedOptions => {
    return map(selectedOptions, ({ name }) => name + "");
  };

  handleSubmit = e => {
    e.preventDefault();

    const { selectedOptions } = this.state;

    alert(`Selection option(s): ${this.showSelectedOptions(selectedOptions)}`);
  };

  render() {
    const { Fields, newFields, selectedOptions } = this.state;

    return (
      <form onSubmit={this.handleSubmit} style={{ margin: 20 }}>
        <h3 style={{ textAlign: "center" }}>Dynamic Select Fields</h3>
        {!isEmpty(Fields) &&
          map(Fields, ({ name, options }, key) => (
            <div key={key} style={{ marginBottom: 20 }}>
              <div>{name}:</div>
              <Select
                name={name}
                value={selectedOptions[name]}
                onChange={({ value }) => this.handleChange(name, value)}
                options={options}
                style={{ marginBottom: 20 }}
              />
            </div>
          ))}
        <div style={{ marginTop: 20 }}>
          <input
            style={{ marginBottom: 5, width: "100%" }}
            type="text"
            className="uk-input"
            onChange={this.handleFieldChange}
            placeholder="Create a Select Field (option, option, option, ..etc)"
            value={newFields}
          />
          <br />
          <button
            onClick={this.handleAddField}
            className="uk-button uk-button-default"
            type="button"
            disabled={newFields ? false : true}
          >
            Add Field
          </button>
        </div>
        <div style={{ marginTop: 40 }}>
          <button
            className="uk-button uk-button-primary"
            type="submit"
            disabled={!isEmpty(selectedOptions) ? false : true}
          >
            Submit Form
          </button>
        </div>
      </form>
    );
  }
}

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.