0

I have a reactJS application where I render a drop down select with the following code:

renderPrimary = (passedid,value) => {  

    if (value=="Primary") {
        return (      
            <div className="col-9 text-left text_14">
                <select id={passedid}>
                    <option value="Primary" selected>Yes</option>
                    <option value="Secondary">No</option>
                </select>
            </div>
        )
    } else {
        return (      
            <div className="col-9 text-left text_14">
                <select id={passedid}>
                    <option value="Primary">Yes</option>
                    <option value="Secondary" selected>No</option>
                </select>
            </div>
        )
    }
}

This will render a drop down with two values, either Primary or Secondary based on the passed parameter 'value'. In my first test case, value = "Primary" so the code with render the with a drop down with "Yes" and "No" and the "Yes" is selected.

When the user clicks on a button, I validate other elements in the DOM. I then try to pick up the value from the so I know if the user selected Yes or No. I do this with the following code:

            if (formIsValid) {
                var id = "benePorS"+(l+1);
                var element = document.getElementById(id);
                var id2 = "benePct"+(l+1);
                var element2 = document.getElementById(id2);

                var PorS = element.selectedIndex;
                console.log("PorS: ", PorS);
                if (PorS == 0) {
                    hasPrimary = true;   
                    primaryPct = primaryPct + element2.value;                     
                } else {
                    hasSecondary = true;
                    secondaryPct = secondaryPct + element2.value;  
                }
            }

When I execute the code, I see the following in the console.log:

PorS:  0

but then I see this:

Uncaught TypeError: Cannot read property 'selectedIndex' of null
at Beneficiaryupdate.SaveBeneficiaryInformation

So it seems that I can access the selected value (when I select No, I see PorS: 1 in the console.log) but the error prevents the code from continuing.

This is from the developers tool elements tab showing the rendered html:

enter image description here

Why would I be getting this error message?

4
  • "benePct"+(l+1); Where is this in your DOM? What is the value of l? Commented Oct 3, 2018 at 2:28
  • l is controlling a for loop. for (var l=0;l<numBene'l++) where numBene = 1. Commented Oct 3, 2018 at 3:29
  • @jmargolisvt I have also included an image of the html that gets rendered showing the id of the select item as "benePorS1". Commented Oct 3, 2018 at 3:35
  • 1
    The error is self explanatory. You're calling element.selectedIndex but element is null. That means an element with that ID on the page was not found. Either your selector is wrong, the code is executing before the element is on the page, or there is no element with that ID. You don't provide enough code to answer those questions. Commented Oct 3, 2018 at 3:36

1 Answer 1

1

Since you are using react, there should be better ways to handle your <select>

First:

You don't need to use the selected attribute in react's select tag. As stated in the docs, you could simply pass the selected value in the root select tag, like so:

renderPrimary = (value) => {
    return (      
                <div className="col-9 text-left text_14">
                    <select value={value} >
                        <option value="Primary">Yes</option>
                        <option value="Secondary">No</option>
                    </select>
                </div>
            );
}

Second:

Since you're setting a value property, you should add a onChange property to handle the user interaction:

constructor(props) {
    super(props);
    this.state = {
        selectedValue: "Primary"
    };
}

handleValueSelected = (event) => {
    this.setState({selectedValue: event.target.value});
}

renderPrimary = () => {
    return (      
                <div className="col-9 text-left text_14">
                    <select value={this.state.selectedValue} 
                            onChange={this.handleValueSelected}>
                        <option value="Primary">Yes</option>
                        <option value="Secondary">No</option>
                    </select>
                </div>
            );
}

That way, wherever you need the selected value in your component, you can just access this.state.selectedValue.

Third:

If you really need to get the DOM of this select, or any other elements, you can use react's ref, like:

constructor(props) {
    super(props);
    this.state = {
        selectedValue: "Primary"
    };
    this.selectRef = React.createRef();
}
handleValueSelected = (event) => {
    this.selectedValue = event.target.value;
}

renderPrimary = () => {
    return (      
                <div className="col-9 text-left text_14">
                    <select ref={this.selectRef} 
                            value={this.state.selectedValue} 
                            onChange={this.handleValueSelected}>
                        <option value="Primary">Yes</option>
                        <option value="Secondary">No</option>
                    </select>
                </div>
            );
}

And with this, this.selectRef should have the DOM once the component is mounted, but you shouldn't really need to access the DOM much with react.

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

4 Comments

Emmanuel: Thank you for your solution. I understand your "First" and "Second" and would like to implement the "Second". My question now would be how to expand on your code to allow for dynamic renderPrimary. In other words, my app will call renderPrimary based on the number of primary selections the user requires. That number can range from 1 to 10. That is the reason my original code generated the select element id based on passedid. passedid will contain benePorS1, benePorS2, benePorS3,...benePorS10.
You could start the selectedValue variable from the state as an array, since you have could pass the number to renderPrimary you could use it as the index to update the array. So the properties would be value={this.state.selectedValue[index]} onChange={() => this.handleValueSelected(index)}. Then, in the handleValueSelectedfunction you would update the index as const updatedValues = ...this.state.selectedValues; updatedValues[index] = event.target.value; setState({selectedValues: updatedValues});
I now have <select value={value} onChange={this.handleValueSelected(index)}> <option value="Primary">Yes</option> <option value="Secondary">No</option> </select> as my select command. I have handleValueSelected = (event,index) => { console.log("handleValueSelected, index: ", index); localData.benePorS[index] = event.target.value; this.setState({ data:localData }); } to handle the change but it gives me an error. I dont think I'm passing index to handleValueSelected correctly.
If you put the parenthesis on the function you're setting as callback like this onChange={this.handleValueSelected(index)} you end up calling it and passing the return value to onChange instead of assigning it. You can write it as an arrow function onChange={(event) => this.handleValueSelected(event, index)} so you're assigning the function call instead of the result.

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.