2

I have two textboxes which has a default value of 25. I have 2 buttons; increment by 1 and decrement by 1. I do not want to manual key in the values of the textboxes, as such, I wanted to create a dropdown select to control the values of the textbox. This is how it looks like:

enter image description here

Is it possible to bind the textboxes with the select? Additional Question: Is it possible to have one [value,setValue] to control both inputs?

This is my code:

const { useState } = React;

function MyApp(){
  const [value1, setValue1]= useState(25)
  const [value2, setValue2]= useState(25)
  function increment(){
     if(selected_id == 1){ //not sure how to implement
        setValue1(value1+1)
     }
     else{
        setValue2(value2+1)
     }
  }    
  function decrement(){
     if(selected_id == 1){ //not sure how to implement
        setValue1(value1-1)
     }
     else{
        setValue2(value2-1)
     }
  }    
  return(
    <div id="react">
       <div>
         Textbox1: <input id="1" defaultValue="value1"/>
         Textbox2: <input id="2" defaultValue="value2"/>
       </div>
       <div>
          <select defaultValue="Textbox1"/>
          <button onClick={increment}> Increment </button>
          <button onClick={decrement}> Decrement </button>
       </div>
    </div>
  )
}

ReactDOM.render(
    <MyApp />,
    document.body
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
jason1234
  • 201
  • 5
  • 14
  • What are these `Input`, `Select`, and `Button` components? (Note that they're capitalized, so they aren't the `input`, `select`, and `button` DOM elements.) – T.J. Crowder Oct 31 '20 at 09:56
  • @T.J.Crowder it's a library by antd. You were right about the second comment, it was a mistake on my end – jason1234 Oct 31 '20 at 09:58
  • @T.J.Crowder How can I add libraries? I usually do npm install but how do I link it here? – jason1234 Oct 31 '20 at 10:14
  • You click the **Add an external library** button, or just write a `script` tag, and point to a copy of the library on https://cdnjs.com/ or similar. But the only library you need for a [mcve] here is React, which [the page I linked to above](http://meta.stackoverflow.com/questions/338537/) talks about adding (it also talks about adding libs generally). (For an MRE, you would just use `input`, `select`, and `button` rather than wrapper components.) – T.J. Crowder Oct 31 '20 at 10:23
  • @T.J.Crowder for some reason I am having some script error. are you able to help me out? – jason1234 Oct 31 '20 at 10:38
  • I've fixed it for you. As I said above, no need for the antd library, and you'd skipped some steps described in the page I linked above. It's worth reading and following instruction *carefully*. :-) There was also an error in the JSX (`id=1` instead of `id="1"` and the same with the other one). – T.J. Crowder Oct 31 '20 at 10:44
  • The `select` doesn't have anything in it, though. What do you want in it? How does its value relate to the two inputs? – T.J. Crowder Oct 31 '20 at 10:45
  • @T.J.Crowder thank you. the select is actually to toggle between the two textboxes. I didn't put it in because I don't know how to link them inside the select options. – jason1234 Oct 31 '20 at 10:46
  • Ah, so you want the select to control which text box the Increment and Decrement buttons update? In that case, the answer to your second question is "no" rather than "yes" since by definition in that case, the text boxes will have different values sometimes. – T.J. Crowder Oct 31 '20 at 10:49
  • @T.J.Crowder yes correct, it is to control which textbox to increment or decrement – jason1234 Oct 31 '20 at 10:52

1 Answers1

1

If I understand correctly, you want the select to choose which text box the Increment and Decrement buttons control. If so, then yes it's possible to do that, but no you can't have one value for both text boxes since by definition, they'll contain different values sometime.

To have the select control the buttons, you'd add a piece of state that tells the buttons what to update:

const { useState } = React;

function MyApp() {
    const [value1, setValue1] = useState(25);
    const [value2, setValue2] = useState(25);
    // The state telling us which text box to update
    const [chosenTextBox, setChosenTextBox] = useState(1);
  
    function increment(){
        // Update the relevant box
        if (chosenTextBox === 1) {
            setValue1(v => v + 1);
        } else {
            setValue2(v => v + 1);
        }
    }    
  
    function decrement(){
        if (chosenTextBox === 1) {
            setValue1(v => v - 1);
        } else {
            setValue2(v => v - 1);
        }
    }    
  
    return(
        <div id="react">
             <div>
                 Textbox1: <input id="1" value={value1} onChange={e => setValue1(+e.target.value)} />
                 Textbox2: <input id="2" value={value2} onChange={e => setValue2(+e.target.value)} />
             </div>
             <div>
                  <select value={chosenTextBox} onChange={e => setChosenTextBox(+e.target.value)}>
                      <option value="1">Textbox 1</option>
                      <option value="2">Textbox 2</option>
                  </select>
                  <button onClick={increment}> Increment </button>
                  <button onClick={decrement}> Decrement </button>
             </div>
        </div>
    )
}

ReactDOM.render(
    <MyApp />,
    document.body
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>

A couple of notes on that:

  • You need to tell the text boxes what their values are. I've used value={value1} and such above.
  • Although with the code above you'd be okay using setValue1(value1 + 1) because of the specific way the state changes in that component, I recommend defaulting to using the callback style anyway. In more complex components that get their state changed in other ways, it can matter.
  • We could use arrays if there were going to be more than just a couple of text boxes

Just for the fun of it, that array approach:

const { useState } = React;

function MyApp() {
    // The values -- the length of the array dictates how many text
    // boxes there are
    const [values, setValues] = useState([25, 25, 25]);
    // The state telling us which text box to update
    const [chosenTextBox, setChosenTextBox] = useState(0);

    function modifyValue(index, delta) {
        if (index >= 0 && index < values.length) {
            setValues(values => Object.assign(
                [...values],
                {[index]: values[index] + delta}
            ));
        }
    }    
  
    function setValue(index, value) {
        if (index >= 0 && index < values.length) {
            setValues(values => Object.assign(
                [...values],
                {[index]: value}
            ));
        }
    }    
  
    return (
        <div id="react">
            <div>
                {values.map((value, index) => (
                    <div>
                        Textbox{index + 1}:
                        <input id={index + 1} value={value} onChange={e => setValue(index, +e.target.value)} />
                    </div>
                ))}
            </div>
            <div>
                 <select value={chosenTextBox} onChange={e => setChosenTextBox(+e.target.value)}>
                     {values.map((value, index) => <option value={index}>Textbox{index + 1}</option>)}
                 </select>
                 <button onClick={() => modifyValue(chosenTextBox, 1)}> Increment </button>
                 <button onClick={() => modifyValue(chosenTextBox, -1)}> Decrement </button>
            </div>
        </div>
    )
}

ReactDOM.render(
    <MyApp />,
    document.body
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • this flow of the answer (non-array approach) is correct, however, I have an issue. The state of `setChosenTextBox` is not updated immediately. How can I fix it? – jason1234 Nov 01 '20 at 04:09
  • @jason1234 - What do you mean by not "immediately"? Perhaps you're running into [this common misunderstanding](https://stackoverflow.com/questions/41446560/react-setstate-not-updating-state)? – T.J. Crowder Nov 01 '20 at 08:53
  • yes, it is asynchronous. If I were to switch from textbox 1 to textbox 2,my `chosenTextBox` value will be 1, whereas my console.log in ` – jason1234 Nov 01 '20 at 09:01
  • @jason1234 - I'm afraid I still don't understand what you mean. When I use the live examples above, there's no problem with a delay choosing the text box. Setting state is asynchronous, but that doesn't cause any problem above. What is the actual problem you're seeing? – T.J. Crowder Nov 01 '20 at 09:03
  • the value of chosenTextBox does not change when I change the select from Textbox1 to Textbox2. Thus, when I increment/decrement, it is always the previous `chosenTextBox` value that got incremented/decremented – jason1234 Nov 01 '20 at 09:06
  • @jason1234 - Are you talking about the examples above or your implementation in your project? It changes above for me. If it doesn't in your implementation, try `onInput` rather than `onChange`, maybe the antd control behaves differently...? – T.J. Crowder Nov 01 '20 at 09:10
  • @jason1234 - Ah, good. This led me to realize that I'd left the `onChange`/`onInput` handlers off the text boxes in the answer; I've fixed that with the usual `onChange`, but you may need `onInput` if antd does the same thing with text boxes as with the select. – T.J. Crowder Nov 01 '20 at 09:34