skip to Main Content

I am new to React and trying to figure out how to update state when a selection changes on an dropdown selection.

Here is my code:

page.js

import { useState } from 'react';
import SelectionRow from './selection-row';


export default function Home() {

  const [positions, setPositions] = useState([
    {id: 1, role : "Executive", selected_val: 2},
    {id: 2, role : "Director", selected_val: 1},
    {id: 3, role : "Manager", selected_val: 0},
    {id: 4, role : "Staff", selected_val: 0}]);

  function selectionChanged(e) {
    
  }

  return (
    <main>
      <form>
        {positions.map((position) => (
        <SelectionRow key={position.id} id={position.id} label={position.role} selected_val={position.selected_val} onSelectionChanged={selectionChanged}/>
        ))}
      </form>
    </main>
  )
}

selection-row.js

const options = [
    { id: 0, name: "David"}, 
    { id: 1, name: "Andrew"}, 
    { id: 2, name: "Steven"}, 
    { id: 3, name: "Simon"}, 
  ];

export default function SelectionRow({id, label, selected_val, onSelectionChanged}) {

    return (
        <div>
        <label>{label}</label>
        <select onChange={onSelectionChanged} value={selected_val} id={id} name={id}>
        {options.map((value) => (
          <option value={value.id} key={value.id}>
            {value.name}
          </option> ))}
        </select>
        </div>
    );
}

The sample application has an array of "company positions" that I want to populate with names chosen for a dropdown box for each. When the name is the dropdown box is changed, I want the selected_val in the positions array to be updated with id of the person selected.

Beyond passing a callback to the component, i’m unsure what I need to add in the selectionChanged function to update the positions array.

2

Answers


  1. You can pass the setPositions function itself to SelectionRow.

    Then on the select change handler, use the previous value from the state to findIndex the changes index of the original array, then update the selected_val.

    Demo:

    const { useState } = React;
    
    function Home() {
    
      const [positions, setPositions] = useState([
        {id: 1, role : "Executive", selected_val: 2},
        {id: 2, role : "Director", selected_val: 1},
        {id: 3, role : "Manager", selected_val: 0},
        {id: 4, role : "Staff", selected_val: 0}]);
    
      return (
        <main>
          <form>
            {positions.map((position) => (
            <SelectionRow key={position.id} id={position.id} label={position.role} selected_val={position.selected_val} setPositions={setPositions}/>
            ))}
          </form>
        </main>
      )
    }
    
    
    function SelectionRow({id, label, selected_val, setPositions}) {
        const options = [
        { id: 0, name: "David"}, 
        { id: 1, name: "Andrew"}, 
        { id: 2, name: "Steven"}, 
        { id: 3, name: "Simon"}, 
      ];
    
        const onSelect = (e) => {
    
            console.log('Changed', id, 'to', e.target.value )
    
            setPositions(prev => {
                const dup = [...prev];
                console.log(2, dup)
                const changedId = dup.findIndex(d => d.id === id);
                if (changedId >= 0) {
                    dup[changedId] = { ...dup[changedId], selected_val: +e.target.value };
                }
                return dup;
            })
        }
    
        return (
            <div>
            <label>{label}</label>
            <select onChange={onSelect} value={selected_val} id={id} name={id}>
            {options.map((value) => (
              <option value={value.id} key={value.id}>
                {value.name}
              </option> ))}
            </select>
            </div>
        );
    }
    
    ReactDOM.render(<Home />, document.getElementById("react"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
    <div id="react"></div>

    If you need more control in the Home component, or want to re-use this logic in another component, you could also define a updateX function:

    const updateSelectedVal = (id, selected_val) => {
          setPositions(prev => {
              const dup = [...prev];
              const changedId = dup.findIndex(e => id === id);
              if (changedId >= 0) {
                  dup[changedId] = { ...dup[changedId], selected_val };
              }
              return dup;
          })
      }
    

    Then pass that to the SelectionRow with an argument for the new selected_val.

    Then you just need to call that function in the row:

    const onSelect = (e) => {
        updatePosition(+e.target.value);
    }
    

    Demo:

    const { useState } = React;
    
    function Home() {
    
      const [positions, setPositions] = useState([
        {id: 1, role : "Executive", selected_val: 2},
        {id: 2, role : "Director", selected_val: 1},
        {id: 3, role : "Manager", selected_val: 0},
        {id: 4, role : "Staff", selected_val: 0}]);
    
      console.log(positions)
      
      const updateSelectedVal = (id, selected_val) => {
          setPositions(prev => {
              const dup = [...prev];
              const changedId = dup.findIndex(d => d.id === id);
              if (changedId >= 0) {
                  dup[changedId] = { ...dup[changedId], selected_val };
              }
              return dup;
          })
      }
    
      return (
        <main>
          <form>
            {positions.map((position) => (
            <SelectionRow key={position.id} id={position.id} label={position.role} selected_val={position.selected_val} updatePosition={(n) => updateSelectedVal(position.id, n)}/>
            ))}
          </form>
        </main>
      )
    }
    
    
    function SelectionRow({id, label, selected_val, updatePosition}) {
        const options = [
        { id: 0, name: "David"}, 
        { id: 1, name: "Andrew"}, 
        { id: 2, name: "Steven"}, 
        { id: 3, name: "Simon"}, 
      ];
    
        const onSelect = (e) => {
            updatePosition(+e.target.value);
        }
    
        return (
            <div>
            <label>{label}</label>
            <select onChange={onSelect} value={selected_val} id={id} name={id}>
            {options.map((value) => (
              <option value={value.id} key={value.id}>
                {value.name}
              </option> ))}
            </select>
            </div>
        );
    }
    
    ReactDOM.render(<Home />, document.getElementById("react"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
    <div id="react"></div>
    Login or Signup to reply.
  2.  import { useState } from 'react';
            import SelectionRow from './selection-row';
            
            
            export default function Home() {
            
              const [positions, setPositions] = useState([
                {id: 1, role : "Executive", selected_val: 2},
                {id: 2, role : "Director", selected_val: 1},
                {id: 3, role : "Manager", selected_val: 0},
                {id: 4, role : "Staff", selected_val: 0}]);
            
              function selectionChanged(e,id) {
        let prev=[...positions]
        let filteredValue=prev.map(v=>{
          if(v.id==id){
            return newdata={...v,selected_val:e.target.value}
          }else{
            return v
          }
        })
        setPositions(filteredValue)
        
                
              }
            
              return (
                <main>
                  <form>
                    {positions.map((position,id) => (
                    <SelectionRow key={position.id} id={position.id} label={position.role} selected_val={position.selected_val} onSelectionChanged={(e)=>selectionChanged(e,id)}/>
                    ))}
                  </form>
                </main>
              )
            }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search