E
E
Ekaterina43212019-12-13 23:41:04
React
Ekaterina4321, 2019-12-13 23:41:04

Why isn't Context.Consumer updated on changes to Context.Provider?

I have a web application that walks to the Movie DB API. The SideBar displays movie search filters, by clicking the search button with the selected parameters (genre, year, etc.) it sends a request to the API. This is how the application looks like (SideBar on the left, Home, in which the Movie is located, on the right): I
5df3f26c14187832945652.png
use React Context to transfer data.
App.js code (Context.Provider in it):

class App extends Component {
constructor(props) {
    super(props);
    this.state = {
        year: '',
        genre: '',
        country: '',
        allGenres: {},
        genreId: '',
        filter: this.filter
    };
}

filter = (year, country, genre, genreId) => {
    this.setState(state => ({
        ...state,
        filter: this.filter
    }));
};

render() {
    return (
        <div className="App">
            <header className="App-header">
                <MovieContext.Provider value={this.state}>
                    <Home/>
                </MovieContext.Provider>
            </header>
        </div>
    );
    }
}

export default (App);

Home.js code:
function Home(props) {

return (
    <div className={classes.root}>
        <SideBar/>
        <main>
            <Movie/>
        </main>
    </div>
);
}

export default withStyles({withTheme: true})(Home);

SideBar.js code (in it is Context.Consumer, in which the Search button is wrapped, by clicking on which I send a request):
function SideBar(props) {
const [open, setOpen] = useState(true);

const [state, setState] = React.useState({
    year: '',
    genre: '',
    country: '',
    allGenres: {},
    genreId: ''
});

const handleChange = name => event => {
    setState({
        ...state,
        [name]: event.target.value,
    });
};

const handleChangeGenre = name => event => {
    setState({
        ...state,
        [name]: event.target.value,
        genreId: event.target.selectedOptions[0].getAttribute('id')
    });
};
var apiKey = "blablabla";

useEffect(() => {

    fetch(`url`, {
        "method": "GET",
        "headers": {
            "Accept": "application/json"
        }
    })
        .then(response =>
            response.ok ? response : Promise.reject(response)
        )
        .then(response =>
            response.json()
        )
        .then(json => {
            setState(state => ({
                ...state,
                allGenres: json
            }));
        })
        .catch(err => {
            console.log(err);
        });
}, []);

const genreRender = () => {
    var genres = state.allGenres.genres;
    if (genres) {
        return genres.map((genre, id) => {
            return (
                <option key={id} id={genre.id} value={genre.name}>{genre.name}</option>
            )
        });
    } else return null;
};

const searchMovie = (filter) => {

    var year = state.year;
    var genre = state.genre;
    var genreId = state.genreId;
    var country = state.country;

    filter(year, genre, genreId, country);

    fetch(`url`, {
        "method": "GET",
        "headers": {
            "Accept": "application/json"
        }
    })
        .then(response =>
            response.ok ? response : Promise.reject(response)
        )
        .then(response =>
            response.json()
        )
        .then(json => {
            console.log(json);
        })
        .catch(err => {
            console.log(err);
        });
};

return (
    <div className={classes.root}>

        <AppBar position="fixed">
            <Toolbar>
                <div className={classes.sectionDesktop}>
                    <IconButton>
                        <FavoriteIcon/>
                    </IconButton>
                </div>
            </Toolbar>

        </AppBar>

        <Drawer
            open={open}
        >
            <div className={classes.toolbar}>
                <Typography className={classes.title} variant="h6" noWrap>
                    Filters
                </Typography>
            </div>
            <Divider/>

            <FormControl variant="outlined" className={classes.formControl}>
                <InputLabel ref={inputLabel} htmlFor="outlined-age-native-simple">
                    Genre
                </InputLabel>
                <Select
                    native
                    value={state.genre}
                    onChange={handleChangeGenre('genre')}
                    labelWidth={labelWidth}
                    inputProps={{
                        name: 'genre',
                        id: 'outlined-age-native-simple',
                    }}
                >
                    <option value=""/>
                    {genreRender()}
                </Select>
            </FormControl>
            <MovieContext.Consumer>
                {({genre, year, genreId, country, filter}) => (
                    <FormControl variant="outlined" className={classes.formControl}>
                        <InputLabel ref={inputLabel} htmlFor="outlined-age-native-simple">Year</InputLabel>

                        <Select
                            value={state.year}
                            onChange={handleChange('year')}
                            labelWidth={labelWidth}
                            inputProps={{
                                name: 'age',
                                id: 'outlined-age-native-simple',
                            }}
                        >
                            <option value=""/>
                            <option value={2017}>2017</option>
                            <option value={2018}>2018</option>
                            <option value={2019}>2019</option>
                        </Select>

                    </FormControl>
                )}
            </MovieContext.Consumer>
            <FormControl variant="outlined" className={classes.formControl}>
                <InputLabel ref={inputLabel} htmlFor="outlined-age-native-simple">Country</InputLabel>
                <Select
                    value={state.country}
                    onChange={handleChange('country')}
                    labelWidth={labelWidth}
                    inputProps={{
                        name: 'age',
                        id: 'outlined-age-native-simple',
                    }}
                >
                    <option value=""/>
                    <option value="USA">USA</option>
                    <option value="UK">UK</option>
                    <option value="France">France</option>
                </Select>
            </FormControl>

            <MovieContext.Consumer>
                {({genre, year, genreId, country, filter}) => (
                    <div className={classes.divBtn}>

                        <Button className={classes.Btn} variant="contained" onClick={() => searchMovie(filter)}>
                            search
                        </Button>

                    </div>
                )}
            </MovieContext.Consumer>

            <Typography className={classes.cr}>Copyright © 2019 KatushkaEsina</Typography>
            <Typography className={classes.cr2}>All rights reserved</Typography>
        </Drawer>
    </div>
);
}

export default withStyles({withTheme: true})(SideBar);

The Movie.js code (it has a Context.Consumer that wraps the entire movie component - in which I'm trying to get the movie's title, but so far just the year from the filter):
function Movie() {

return <MovieContext.Consumer>
    {({genre, year, genreId, country, filter}) => (
    <div className={classes.root}>
        <Paper className={classes.paper}>
            <Grid container spacing={2}>
                <Grid item>
                    <ButtonBase className={classes.image}>
                        <img className={classes.img} alt="moviePoster" src={logo}  style={{width: 120, height: 120}}/>
                    </ButtonBase>
                </Grid>
                <Grid item xs={12} sm container>
                    <Grid item xs container direction="column" spacing={2}>
                        <Grid item xs>
                            <Typography gutterBottom variant="subtitle1">
                                {year || 'Movie name'}
                            </Typography>

                            <Typography variant="body2" gutterBottom>
                                Movie description
                            </Typography>
                            <Typography variant="body2" color="textSecondary">
                                Movie genre
                            </Typography>
                        </Grid>
                        <Grid item>
                            <Typography variant="body2" style={{ cursor: 'pointer' }}>Snow more info</Typography>
                        </Grid>
                    </Grid>
                </Grid>
            </Grid>
        </Paper>
    </div>
    )}
</MovieContext.Consumer>
}

export default withStyles({withTheme: true})(Movie);

The context itself:
export const MovieContext = React.createContext({
  genre: '',
  country: '',
  year: '',
  genreId: '',
  filter: (genre, country, year, genreId) => {
      this.genre = genre || '';
      this.country = country || '';
      this.year = year;
      this.genreId = genreId || '';
  },
});

The problem is that when I select the filters I need, click on the button and send a request, in the movie component (Movie.js), the text remains in Typography, and not the year that I select in the filter ({year || 'Movie name '}). Tell me, please, what am I doing wrong?
PS if there are not enough details or vice versa too much, I'm sorry, my first question :)

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question