Dropdown menu Items fields specify with Redux

This is my first time using Redux and I haven’t properly learnt the map feature in JS so if I wanted to use the fields that are stored in my store to populate a dropdown menu, how would I go about it?

I am aware that the “value={10}” specifies the value that gets submitted by the form to populate the table. As the foreign key values relate to a number in the table I want to submit them too, I would need to have these values as well as the strings for each “menuItem”.

diveLogForm.component.js

const DiveLogForm = (props) => {

        // select user object from redux
        const user = useSelector(state => state.user);

        // get the object with all the fields
        const fields = useSelector(state => state.fields);

        // can destructure individual fields
        const { schoolName, current, region, diveType, visibility, diveSpot } = fields;

        .........

        // all onChange functions do the exact same thing, so you only need one
        // pass to a component like onChange={handleChange('typeID')}
        const handleChange = (property) => (e) => {
            setDive({
                // override the changed property and keep the rest
                ...dive,
                [property]: e.target.value,
            });
        }

        // get access to dispatch
        const dispatch = useDispatch();

        // useEffect with an empty dependency array is the same as componentDidMount
        useEffect(() => {
            // dispatch the action to load fields for each field type
            // once loaded, the changes will be reflected in the fields variable from the useSelector
        Object.keys(fields).forEach(name => dispatch(requireFieldData(name)));
        }, []); // <-- empty array

       ..........

        return (

            <form onSubmit={handleSubmitDive}>

                {/*<MuiThemeProvider theme={}>*/}
                <>
                    <AppBar title="Enter your dive details"></AppBar>
                    <Grid container spacing={6}
                          direction="row"
                          justify="center"
                          alignItems="center">
                            {/*<Grid item xs={1}>*/}
                            {/*    /!*<Paper className={classes.paper}>xs=3</Paper>*!/*/}
                            {/*</Grid>*/}
                            {/*<Grid item xs={10}/>*/}
                        <Grid item xs={4}>
                        <FormControl className={classes.formControl}>
                            <InputLabel id="Enter-the-dive-type">Dive Type</InputLabel>
                            <Select
                                labelId="Enter-the-dive-type"
                                id="Enter-the-dive-typer"
                                value={dive.typeID}
                                onChange={handleChange}>
                                <MenuItem value="">
                                    <em>None</em>
                                </MenuItem>
                                <MenuItem value={10}>Ten</MenuItem>
                                <MenuItem value={20}>Twenty</MenuItem>
                                <MenuItem value={30}>Thirty</MenuItem>
                            </Select>
                        </FormControl>
                        </Grid>
                        <Grid item xs={4}>
                            <FormControl className={classes.formControl}>
                                <InputLabel id="Enter-the-region">Dive School</InputLabel>
                                <Select
                                    labelId="Enter-the-dive-school"
                                    id="Enter-the-dive-school"
                                    value={dive.schoolNameID}
                                    onChange={handleChange}>
                                    <MenuItem value="">
                                        <em>None</em>
                                    </MenuItem>
                                    <MenuItem value={10}>Ten</MenuItem>
                                    <MenuItem value={20}>Twenty</MenuItem>
                                    <MenuItem value={30}>Thirty</MenuItem>
                                </Select>
                            </FormControl>
                        </Grid>
                        <Grid item xs={4}>
                        <FormControl className={classes.formControl}>
                            <InputLabel id="Enter-the-current-level">Current Level</InputLabel>
                            <Select
                                labelId="Enter-the-current-level"
                                id="Enter-the-current-level"
                                value={dive.currentID}
                                onChange={handleChange}>
                                <MenuItem value="">
                                    <em>None</em>
                                </MenuItem>
                                <MenuItem value={10}>Ten</MenuItem>
                                <MenuItem value={20}>Twenty</MenuItem>
                                <MenuItem value={30}>Thirty</MenuItem>
                            </Select>
                        </FormControl>
                        </Grid>
                        ...
                            <Grid item xs={12} md={12}>
                            <Button variant="primary" type="submit">
                                Submit</Button>
                            </Grid>
                            <br />
                        {/*<Grid item xs={1}>*/}
                        {/*    /!*<Paper className={classes.paper}>xs=3</Paper>*!/*/}
                        {/*</Grid>*/}
                    </Grid>

                </>
                {/*</MuiThemeProvider>*/}
            </form>
        )
    }

Hello @james_g2001 ,

I don’t understand your question quite well but I’ll try to explain as much as I can.

The map function basically iterates an array replacing every item with the return value of the callback function.
Here is an example

// A simple array of numbers
const array = [1, 2, 3];

// Use  the map method to add one to every item in the array
const mappedArray = array.map((number, index) => number + 1); 
// Results = [2, 3, 4]

To better understand the map method, let’s create our own implementation.

Array.prototype.myMap = function(callback) {
    const newArray = [];
    
    for(let i = 0; i < this.length; i++){
        newArray.push(callback(this[i], i))
    }

    return newArray;
}

In the code above,

  • We add a new method myMap to Array.prototype.
  • This method accepts a callback function as parameter.
  • In the function body, we declare a new array, newArray. The map method returns a new array and doesn’t alter the original.
  • We iterate the original array and push the result of callback(this[i], i)) into newArray. this[i] is the current element in the iteration and i is the index.
  • Finally, we return the new array, newArray.

I hope this helps clarify your doubts using the map method. Let me know if you have any other questions.

In React, the map method can be used to dynamically render components.
Here is an example

const menuItems = [
    {
        value: 10,
        label: 'Ten'
    },
    {
        value: 20,
        label: 'Twenty'
    },
    {
        value: 30,
        label: 'Thirty'
    }
]

export default function Demo(){
    return  (
        <Select
           labelId="Enter-the-dive-type"
           id="Enter-the-dive-typer"
           value={dive.typeID}
           onChange={handleChange} >
                {menuItems.map((item, index) => <MenuItem value={item.value} key={index}>{item.label}</MenuItem> )}    
        </Select>
}

I hope this helps

In the context of my code I want to populate each of the FormControl dropdowns with the de-structured individual fields data (schoolName, current, region etc). It would be preferable if I could create a method so that each of my dropdown fields had a option for each value in the array.

From this piece of code,

 // get the object with all the fields
        const fields = useSelector(state => state.fields);

        // can destructure individual fields
        const { schoolName, current, region, diveType, visibility, diveSpot } = fields;

What data type is schoolName, current, and all the other destructured items? If I understand you correctly, you want an individual dropdown for schoolName, current, the other items.

Yes that’s right. They are all string values… :slightly_smiling_face:

I have the below method for each of the dropdowns but it isn’t working.

<FormControl className={classes.formControl}>
                            <InputLabel id="Enter-the-dive-type">Dive Type</InputLabel>
                            <Select
                                labelId="Enter-the-dive-type"
                                id="Enter-the-dive-type"
                                value={dive.typeID}
                                onChange={handleChange}>
                                {/*<MenuItem value="">*/}
                                {/*    <em>None</em>*/}
                                {/*</MenuItem>*/}
                                {this.props.fields.diveType.length > 0 &&
                                this.props.fields.diveType.map(({diveType}) => {
                                    return (
                                        <MenuItem value={10}>{diveType}</MenuItem>
                                    )})}
                            </Select>
                        </FormControl>

Looking at this,

{this.props.fields.diveType.length > 0 &&
   this.props.fields.diveType.map(({diveType}) => {
     return (
          <MenuItem value={10}>{diveType}</MenuItem>
)})}

You are trying to iterate a string value. This isn’t possible with the map method. You need to store your menuItems( items which you wanna map through) as an array. This way, you can return the <MenuItem /> component on each iteration.

If there is say 10 diveType’s will it not iterate out 10 dropdown options?

I know I need to add another bit to the loop to increment the “value={10 etc}”.

Let’s say you want a dropdown with schoolNames, diveTypes…, you can create a structure like this:

const fields = {
    schoolNames: ["school1", "school2", "school3"],
    diveTypes: ["type1", "type2", "type3"]
}

Now, it’s possible to do this

{fields.diveType.length > 0 &&
   fields.diveType.map(({diveType}) => {
     return (
          <MenuItem value={divType}>{diveType}</MenuItem>
)})}

If you wanna have a different value for the label and value, you can do this

const fields = {
    diveTypes: [{label: 'type1Label', value: 'type1'}]
}

{fields.diveType.length > 0 &&
   fields.diveType.map(({diveType}) => {
     return (
          <MenuItem value={diveType.value}>{diveType.label}</MenuItem>
)})}