0

I'm trying to have a food filter page whereby the user presses buttons to indicate their preference so that on confirm, I would be able to have the user's preference stored as on object with keys "area", "cuisine" and "price". After trying various combinations, the logged object I get keeps only reflecting a change in the first update which is area. {"area": "South", "cuisine": Array [], "price": -Infinity } instead of {"area":"South", "cuisine": "Japanese Cuisine", "price": 3}. How do I deal with this async nature of setstate to get all the updates correct? (the handle press functions are passed to children component for area cuisine and price)

enter image description here

Filter.js

const [filters, setFilters] = React.useState({});

    React.useEffect(() => {
        setFilters({ area: "", cuisine: "", price: 0 });
    }, []);

    const handleAreaPress = (area) => {
        setFilters((prevState) => ({ ...prevState, area: area }));
    };

    const handleCuisinePress = (cuisine) => {
        setFilters((prevState) => ({ ...prevState, cuisine: cuisine }));
    };

    const handlePricePress = (price) => {
        let max = Math.max(...price);
        setFilters((prevState) => ({ ...prevState, price: max }));
    };

    const handleConfirmPress = () => {
        console.log(filters);  // expected log to be {"area":"South", "cuisine": "Japanese Cuisine", "price": 3}
    };

Area.js

const AreaSelection = ({ handleAreaPress }) => {
    const [selected, setSelected] = React.useState([]);
    const areas = ["North", "South", "East", "West", "Central"];

    const handlePress = (area) => {
        setSelected(area);
    };

    const buttons = () =>
        areas.map((items) => (
            <TouchableOpacity
                key={items}
                onPress={() => {
                    handlePress(items);
                    handleAreaPress(items);
                }}
                style={[
                    styles.button,
                    {
                        backgroundColor: selected.includes(items)
                            ? "silver"
                            : "white",
                    },
                ]}
            >

Cuisine.js

const CuisineSelection = ({ handleCuisinePress }) => {
    const [selected, setSelected] = React.useState([]);
    const cuisine = [
        "Asian",
        "Western",
        "Chinese",
        "Korean",
        "Indian",
        "Japanese",
        "Cafe",
        "Local",
    ];

    const handlePress = (cuisine) => {
        selected.includes(cuisine)
            ? setSelected(selected.filter((s) => s !== cuisine))
            : setSelected([...selected, cuisine]);
    };

    const buttons = () =>
        cuisine.map((items) => (
            <TouchableOpacity
                key={items}
                onPress={() => {
                    handlePress(items);
                    handleCuisinePress(selected);
                }}
                style={[
                    styles.button,
                    {
                        backgroundColor: selected.includes(items)
                            ? "silver"
                            : "white",
                    },
                ]}
            >
5
  • Do you have a single button that is calling all 3 handler functions? Commented Jun 18, 2020 at 8:36
  • @vunski the screen is divided into 3 sections so for example area section has North South East West buttons that onPress will call handleAreaPress, same goes for the rest of the sections. Commented Jun 18, 2020 at 8:40
  • 1
    Can't really wrap my head in this as I can't try it, obviously easiest solution would be splitting all 3 into their own useState. Commented Jun 18, 2020 at 8:48
  • How are you calling those filters? I think it's better if you add the rest of the component's code. Commented Jun 18, 2020 at 8:53
  • @Clarity alright I've updated the post Commented Jun 18, 2020 at 8:56

3 Answers 3

1

The area code is actually working correct. The issue is in this code where you call two functions setting state:

    handlePress(items);
    handleCuisinePress(selected);

handlePress sets the internal component state, and then handleCuisinePress reads that state and sets it on the filters. However, since setting state is async when handleCuisinePress is called, it uses the old values of state since the handlePress results aren't applied yet.

The best way to handle this wold be to get rid of internal component state and read it directly from the filters:

// Declare array outside of component to not create it on every render
const cuisine = [
        "Asian",
        "Western",
        "Chinese",
        "Korean",
        "Indian",
        "Japanese",
        "Cafe",
        "Local",
    ];

const CuisineSelection = ({ handleCuisinePress, cuisine }) => {
    const buttons = () =>
        cuisine.map((items) => (
            <TouchableOpacity
                key={items}
                onPress={() => {
                    handleCuisinePress(items);
                }}

            >
Sign up to request clarification or add additional context in comments.

Comments

1
 const handlePress = (cuisine) => {
    let newSelected = [];

    if (selected.includes(cuisine)) {
      newSelected = selected.filter((s) => s !== cuisine);
    } else {
      newSelected = selected.concat(cuisine);
    }

    setSelected(cuisine);
    handleCuisinePress(newSelected);
};

The selected state you pass to handleCuisinePress is not up to date, you shouldn't rely on it, just pass the new state you calculated in handlePress to handleCuisinePress

Comments

0

You can define an expected value range and conditionally fallback to a default value.

    const handlePricePress = (price) => {
        const max = price >= 0 ? Math.max(...price) : 0;
        setFilters((prevState) => ({ ...prevState, price: max }));
    };

3 Comments

Sorry this does not help with the problem I'm facing due to async nature of setstate
Can you substantiate what exactly you want to achieve?
Once pressing the buttons for each section, on confirmation press the log I expect to get should be {"area":"South", "cuisine": "Japanese Cuisine", "price": 3} , but I'm getting {"area": "South", "cuisine": Array [], "price": -Infinity } instead so I'm not really sure how to get the state to update correctly

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.