0

I am trying to use the map function but it wont rerender my select box with the updated selected value. The filter is on a material ui dialog that pops up when you view a file. The values will update when i close the modal and reopen it but wont update if i dont close the window. Any help would be greatly appriciated.

import React, { useEffect, useState } from 'react';
import Grid from '@material-ui/core/Grid';
import Paper from '@material-ui/core/Paper';
import InputLabel from '@material-ui/core/InputLabel';
import FormControl from '@material-ui/core/FormControl';
import Select from '@material-ui/core/Select';
import { makeStyles } from '@material-ui/core/styles';
import MenuItem from '@material-ui/core/MenuItem';

import filter from '../../../constants/filter-options';
 
export default function KeywordFilter(props) {
    
    const [selectedFilter, setFilter] = useState(props.selectedFilter);
    
    const [filterOptions, setFilterOptions] = useState(filter);

    const useStyles = makeStyles(theme => ({
        root: {
            flexGrow: 1
        },
        paper: {
            padding: theme.spacing(2),
            textAlign: 'center',
            color: theme.palette.text.primary,
        },
        modal: {
            height: '80vh',
            width: '40vw',
            maxWidth: '40vw'
        }
    }));

    const classes = useStyles();

    const handleChange = (event, keyword) => {
        var temp =  selectedFilter;
        
        temp[keyword] = event.target.value;
        console.log("TEMP: ", temp)
        console.log("keywordList: ", keywordList)

        props.onFilterChange(temp);
        setFilter(temp)
        setFilterOptions(filter)
    };

    const keywordList = Object.keys(filterOptions)

    
    return (


        <div key={keywordList}>
            <h4 style={{textAlign:'center'}}>Filters: </h4>
            <Grid container spacing={3}>
                {keywordList.map((keyword) => {
                    return (
                        <Grid item xs={6}>
                            {console.log("selectedFilter: ", selectedFilter)}
                            <Paper className={classes.paper}>
                            {keyword}:&nbsp;<FormControl className={classes.formControl}>
                                                <Select
                                                    key={keyword}
                                                    labelId="demo-simple-select-label"
                                                    id="demo-simple-select"
                                                    value={selectedFilter[keyword] ? selectedFilter[keyword] : "None"}
                                                    onChange={(e) => handleChange(e, keyword)}
                                                >
                                                    {filterOptions[keyword].map(element => <MenuItem value={element}>{element}</MenuItem>)}
                                                </Select>
                                            </FormControl>
                            </Paper>
                        </Grid>
                    )}
                )}
            </Grid>
        </div>
    ); 
}

The filter file looks like the following:

const filter = 
{
    Schedule : ["None", "Monthly", "Quarterly", "Semi-Annually",  "Annually"],    
    Chemical : ["None", "Copper", "Phosphorus"],
    Color : ["None", "Black", "Blue"]
}

export default filter;
Horrerblade
  • 103
  • 3
  • 14
  • Make sure that `selectedFilter[keyword]` is actually changing. – DedaDev Jul 31 '20 at 01:29
  • `temp[keyword] = event.target.value;` is mutating the current state (and the props by extension) and it's [an anti-pattern](https://stackoverflow.com/q/37755997/1218980). In fact, it's the root of the problem you're facing since the object, which is just a reference, never changes, so any `setFilter` is then ignored by React. – Emile Bergeron Jul 31 '20 at 01:32
  • 1
    Does this answer your question? [React Hooks useState() with Object](https://stackoverflow.com/questions/54150783/react-hooks-usestate-with-object) – Emile Bergeron Jul 31 '20 at 01:34
  • Yes, thank you for all the help. It turns out I was modifying the state directly and that was creating a bunch of issues. The following changes fixed the issue – Horrerblade Jul 31 '20 at 04:32
  • const handleChange = (event, keyword) => {` //deep copy selected filters to avoid directly changing state const target = JSON.parse(JSON.stringify(props.selectedFilter)) //set new value to add const source = { [keyword]: event.target.value}; //merge the 2 objects (this will update target aswell) const results = Object.assign(target, source) //update state props.onFilterChange(results) }; – Horrerblade Jul 31 '20 at 04:32

1 Answers1

0

It turns out I was modifying the state directly and that was creating a bunch of issues. The following changes to the handleChange function and removing unnecessary code fixed the issue:

import React, { useEffect, useState } from 'react';
import Grid from '@material-ui/core/Grid';
import Paper from '@material-ui/core/Paper';
import FormControl from '@material-ui/core/FormControl';
import Select from '@material-ui/core/Select';
import { makeStyles } from '@material-ui/core/styles';
import MenuItem from '@material-ui/core/MenuItem';

import filter from '../../../constants/filter-options';
 
export default function KeywordFilter(props) {

    const useStyles = makeStyles(theme => ({
        root: {
            flexGrow: 1
        },
        paper: {
            padding: theme.spacing(2),
            textAlign: 'center',
            color: theme.palette.text.primary,
        },
        modal: {
            height: '80vh',
            width: '40vw',
            maxWidth: '40vw'
        }
    }));

    const classes = useStyles();

    const handleChange = (event, keyword) => {
        
        //deep copy selected filters to avoid directly changing state
        const target = JSON.parse(JSON.stringify(props.selectedFilter)) 
        //set new value to add
        const source = { [keyword]: event.target.value};
        //merge the 2 objects (this will update target aswell)
        const results = Object.assign(target, source)
        //update state
        props.onFilterChange(results)
    };

    return (
        <>
            <h4 style={{textAlign:'center'}}>Filters: </h4>
            <Grid container spacing={3}>
                {Object.keys(filter).map((keyword) => {
                    return (
                        <Grid item xs={6}>
                            <Paper className={classes.paper}>
                            {keyword}:&nbsp;<FormControl className={classes.formControl}>
                                                <Select
                                                    key={keyword}
                                                    labelId="demo-simple-select-label"
                                                    id="demo-simple-select"
                                                    value={props.selectedFilter[keyword] ? props.selectedFilter[keyword] : "None"}
                                                    onChange={(e) => handleChange(e, keyword)}
                                                >
                                                    {filter[keyword].map(element => <MenuItem value={element}>{element}</MenuItem>)}
                                                </Select>
                                            </FormControl>
                            </Paper>
                        </Grid>
                    )}
                )}
            </Grid>
        </>
    ); 
}
Horrerblade
  • 103
  • 3
  • 14