html – I am using react hooks for a little music project I'm working on. How can I make state change in only the div affected?-ThrowExceptions

Exception or error:

I am mapping out 4 divs. They print out like I want, each div showing their corresponding number in an h2 and the default chord ‘C’.

I have a dropdown with options A-G. I want the h1 to change the chord to the new chord in only the div it was selected in.

The chord does change, but in all of the divs. How do I make it so that state changes for that particular div only? Is this possible? I know I could write a separate state for each div, it’s only four but I want it cleaner than that if possible. Any ideas? I’m sure most of you will be able to spot the problem right away. Thanks in advance for help.

    function Grid() {
      const [chord, setChord] = useState("C");

     const number = [1, 2, 3, 4]

      return (
        number.map(number =>
      <div style={left}>

        <div style={styles} className="col-">
          <h1 style={keyStyle}>{chord}</h1>
          <h2 key={number}>{number.toString()}.</h2>

          <label for="key">KEY </label>

          <select
            style={dropdownStyles}

            onChange={e => setChord(e.target.value)}

          >
            <option value="C">C</option>
            <option value="dm">dm</option>
            <option value="em">em</option>
            <option value="F">F</option>
            <option value="G">G</option>
            <option value="am">am</option>
          </select>
        </div>
      </div>


    )

  );
}
How to solve:

You need to initialize your state with an Array, instead of a single String value, as follows:


function Grid() {
      // Initialize an Array state with 4 entries for each div: 
      const [chord, setChord] = React.useState(["C","C","C","C"]);
     // Rename list to 'numbers'. Naming matters.
     const numbers = [1, 2, 3, 4];
      return (
        numbers.map((number,idx) =>
      <div style={left}>
        <div style={styles} className="col-">
          {/* Use the current div's number, minus 1 to display the appropriate state value */}
          <h1 style={keyStyle}>{ chord[idx] }</h1>
          <h2 key={number}>{number.toString()}.</h2>
          <label for="key">KEY </label>
          {/* Update the state using the current div's number: */}
          <select
            style={dropdownStyles}
            onChange={e => {
                            // Get a copy of the current state:
                            const _chord = chord.slice();
                            // Change the entry for the specified div:
                            _chord[idx] = e.target.value;
                            // Update the state:
                            setChord(  _chord );
                        }}
          >
            <option value="C">C</option>
            <option value="dm">dm</option>
            <option value="em">em</option>
            <option value="F">F</option>
            <option value="G">G</option>
            <option value="am">am</option>
          </select>
        </div>
      </div>
    )
  );
}

You can also use the spread operator to get a copy of the chords array inside the event handlers:

const _chord = [ ...chord ];

Note: You should also consider renaming the array number to numbers (list of elements require plural) or you will be looking for troubles, since you are also using the number inside the map callback. Make sure the two names correspond to different values.

Leave a Reply

Your email address will not be published. Required fields are marked *