Home > OS >  How to individually function Select Tag in .map() method in React?
How to individually function Select Tag in .map() method in React?

Time:12-02

    const [age, setAge] = React.useState('Assigned');

    const handleChange = (event) => {
        setAge(event.target.value);
        console.log(age)
    };


    return (
        <>
        {
            actionItemArray.map(item => (
                <tr className={styles.styledTable}>
                    <td className={styles.styledTd}>{item.TITLE}</td>
                    <td className={styles.styledTd}>{item.P_PK}</td>
                    <td className={styles.styledTd}>
                    <Select
                    value={age}
                    onChange={handleChange}
                    >
                    <MenuItem value='Assigned'>Assigned</MenuItem>
                    <MenuItem value='Processing'>Processing</MenuItem>
                    <MenuItem value='Done'>Done</MenuItem>
                    </Select>
                    </td>
                    <td className={styles.styledTd}>{getUnixTime(item.START_DATE)}</td>
                    <td className={styles.styledTd}>{getUnixTime(item.DUE_DATE)}</td>
                    <td className={styles.styledTd}>{item.owner.FIRST_NAME   item.owner.LAST_NAME}</td>
                    <td className={styles.styledTd}><a className={styles.styledATag}>상세보기</a></td>
                    <td className={styles.styledTd}><a className={styles.styledATag}>수정</a></td>
                    <td className={styles.styledTd}><a className={styles.styledATag} onClick={openDeleteModal}>삭제</a></td>
                </tr>
            ))
        }
        </>
    )
}

Hey guys. This is my code. I'm using a Material-UI Select Form tag but can't figure out how to make this form work individually while I'm using .map() method.

After I render with this code,

enter image description here

This looks like working fine but the Selects Forms are working at the same time. Need some guidance.

CodePudding user response:

You just have one age state variable and you are referencing all your Select elements to that variable. Of course whenever it changes, all of your elements are getting affected by it. What you need to do is maintain separate variables for every single Select element in your list.

So instead of having one static value for age, let's initialise it as an object so that that we can hold value for every list item.

const [age, setAge] = React.useState({});

The idea here is to hold age in this variable against each actionItemArray element. so your state variable should look something like this:

age = {
    zz: "Processing",
    aa: "Done",
    cc: "Assigned"
}

Of course if you have access to some unique ID, please use that as a key inside this state variable. For now I will be using item.TITLE as the key.

This will change the way in which you were setting the state value. Earlier you were directly assigning a string to a single variable, now you will have to update your handleChange so that it can target a specific value to save inside your object:

const handleChange = (event, title) => {
    setAge({
        ...age,
        title: event.target.value
    };
    console.log(age)
};

The next thing that naturally changes is the way you were accessing the state value in your Select element. Instead of directly accessing age, you will now have to access the specific title inside age

<Select
    value={age[item.TITLE]}
    onChange={(event) => handleChange(event, item.TITLE)}
>

This should be enough to get your code up and running. However something else that I suspect you want is to have a default value of "Assigning" when nothing is selected in the Select tag. As of now when I initialised the age variable, I set it as an empty object {}. This would mean that no default value is provided to any Select element, and hence undefined will show up as the default value for each element in list. To fix this, you can use an useEffect hook to initialise all the values inside this variable as "Assigning"

useEffect(() => {
    setAge(actionItemArray.map(item => age[item.title] ? age[item.title] : "Assigning"))
}, [actionItemArray])

Effectively, your final code should look something like this:

const [age, setAge] = React.useState({});

const handleChange = (event, title) => {
    setAge({
        ...age,
        title: event.target.value
    };
    console.log(age)
};

useEffect(() => {
    setAge(actionItemArray.map(item => age[item.title] ? age[item.title] : "Assigning"))
}, [actionItemArray])

return (
    <>
    {
        actionItemArray.map(item => (
            <tr className={styles.styledTable}>
                <td className={styles.styledTd}>{item.TITLE}</td>
                <td className={styles.styledTd}>{item.P_PK}</td>
                <td className={styles.styledTd}>
                <Select
                    value={age[item.TITLE]}
                    onChange={(event) => handleChange(event, item.TITLE)}
                >
                <MenuItem value='Assigned'>Assigned</MenuItem>
                <MenuItem value='Processing'>Processing</MenuItem>
                <MenuItem value='Done'>Done</MenuItem>
                </Select>
                </td>
                <td className={styles.styledTd}>{getUnixTime(item.START_DATE)}</td>
                <td className={styles.styledTd}>{getUnixTime(item.DUE_DATE)}</td>
                <td className={styles.styledTd}>{item.owner.FIRST_NAME   item.owner.LAST_NAME}</td>
                <td className={styles.styledTd}><a className={styles.styledATag}>상세보기</a></td>
                <td className={styles.styledTd}><a className={styles.styledATag}>수정</a></td>
                <td className={styles.styledTd}><a className={styles.styledATag} onClick={openDeleteModal}>삭제</a></td>
            </tr>
        ))
    }
    </>
)

CodePudding user response:

You can pass the index of the item from the map function like this:

actionItemArray.map((item,i) => (
            <tr className={styles.styledTable}>
                <td className={styles.styledTd}>{item.TITLE}</td>
                <td className={styles.styledTd}>{item.P_PK}</td>
                <td className={styles.styledTd}>
                <Select
                value={age[i]} // to get the current age value
                onChange={(e) => handleChange(e,i)}
                >
                <MenuItem value='Assigned'>Assigned</MenuItem>
                <MenuItem value='Processing'>Processing</MenuItem>
                <MenuItem value='Done'>Done</MenuItem>
                </Select>
                </td>
                <td className={styles.styledTd}>{getUnixTime(item.START_DATE)}</td>
                <td className={styles.styledTd}>{getUnixTime(item.DUE_DATE)}</td>
                <td className={styles.styledTd}>{item.owner.FIRST_NAME   item.owner.LAST_NAME}</td>
                <td className={styles.styledTd}><a className={styles.styledATag}>상세보기</a></td>
                <td className={styles.styledTd}><a className={styles.styledATag}>수정</a></td>
                <td className={styles.styledTd}><a className={styles.styledATag} onClick={openDeleteModal}>삭제</a></td>
            </tr>

In the handleChange function you use that index to change the value of that particular item only and you need to set the initial value of age to an empty array like this:

 const [age,setAge] = React.useState([]);
 const handleChange = (event,index) => {
    setAge([...age.map((data,i) => i === index ? event.target.value:data )]);
    
};
  • Related