1
votes

I have a navigationContainer in my App function that contains two functions: Home and Cart. When I click on an item in Home, the its count is updated in the Cart list for the first time, but after the first time, the data updates but the flatlist doesn't update until clicked on!!

my App function:

export let ListDataContext = React.createContext(basketData);
const Tab = createBottomTabNavigator();
export default function App() {
    const [context, setContext] = useState(basketData);
    return (

        <ListDataContext.Provider value={[context, setContext]}>
            <NavigationContainer>

                <Tab.Navigator screenOptions={({route}) => ({
                    tabBarIcon: ({focused, color, size, isVisible}) => {
                        let iconName;

                        if (route.name === 'Home') {

                            isVisible = 'none';
                            iconName = focused
                                ? 'home'
                                : 'home';
                        } else if (route.name === 'Cart') {

                            iconName = focused ? 'shopping-cart' : 'shopping-cart';
                        }
                        // You can return any component that you like here!
                        return <IconWithBadge name={iconName} color={'#ff9900'}
                                              badgeCount={23} size={21}
                                              visibility={isVisible}/>;
                    },
                })}
                               tabBarOptions={{
                                   activeTintColor: 'tomato',
                                   inactiveTintColor: 'gray',
                               }}
                >

                    <Tab.Screen name="Home" component={HomeStackScreen}/>
                    <Tab.Screen name="Cart" component={CartStackScreen}/>

                </Tab.Navigator>

            </NavigationContainer>
</ListDataContext.Provider>


    );
}

my Home func:

function ListItem({id, name, count, handleClicks}) {
    return useMemo(() => {
        return (
            <TouchableNativeFeedback onPress={() =>
                handleClicks(id)}>
                <Container style={{
                    width: '100%', height: 180,
                    backgroundColor: '#FEFFFF', alignSelf: 'center'
                }}>
                    <Content>


                        <View style={{
                            width: '95%', height: 170,
                            flexDirection: 'row', borderRadius: 10, elevation: 3,
                            padding: 10, marginBottom: 5
                        }}>

                            <View style={{
                                width: 100, height: '100%',
                                justifyContent: 'center', marginLeft: 10
                            }}>

                                <Image style={{width: '100%', height: 100, borderRadius: 50}}
                                       source={require('../images/imgprofile.jpg')}
                                />
                            </View>
                            <View style={{flex: 1, flexDirection: 'column'}}>
                                <Text style={{marginLeft: 20, marginTop: 30, fontSize: 18}}>
                                    {name}
                                </Text>
                                <Text style={{marginLeft: 20, fontSize: 28}}
                                      onPress={() => console.log('d')}>
                                    +
                                </Text>
                                <Text style={{marginLeft: 20, marginTop: 5, fontSize: 18}}>
                                    {count}
                                </Text>
                                <Text style={{marginLeft: 20, fontSize: 28}}>
                                    -
                                </Text>
                            </View>
                        </View>

                    </Content>
                </Container>
            </TouchableNativeFeedback>

        );
    },  [id, name, count, handleClicks]);

}

function HomeScreen({navigation}) {
    const [context, setContext] = useContext(ListDataContext);
    const [listItemsRefresh, setListItemsRefresh] = useState(false);
    const handleClicks = (id)  => {
        for (let i = 0; i < context.length; i++){
            if (context[i]['id'] === id){
                context[i]['count'] = context[i]['count'] + 1;
                setContext(context);
            }
        }
        setListItemsRefresh(!listItemsRefresh);
        setContext(context);
        console.log(context);
    };

    return (

        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>

            <SafeAreaView style={{ marginBottom: 10,
                width: '90%', height: '90%',
                alignContent: 'center'}}>
                <FlatList
                    data={context}
                    renderItem={({item}) => <ListItem
                        id={item.id}
                        name={item.name}
                        count={item.count}
                        handleClicks={handleClicks}
                    />}
                    keyExtractor={item => item.id}
                    extraData={listItemsRefresh}
                />
            </SafeAreaView>

        </View>

    );

}


export default HomeScreen;

and my Cart func:

function ListItem({id, name, count, handleClicks}) {

    return useMemo(() => {
        return (
            <TouchableNativeFeedback onPress={() =>
                handleClicks(id)}>
                <Container style={{
                    width: '100%', height: 180,
                    backgroundColor: '#FEFFFF', alignSelf: 'center'
                }}>

                    <View style={{
                        width: '95%', height: 170,
                        flexDirection: 'row', borderRadius: 10, elevation: 3,
                        padding: 10, marginBottom: 5
                    }}>

                        <View style={{
                            width: 100, height: '100%',
                            justifyContent: 'center', marginLeft: 10
                        }}>

                            <Image style={{width: '100%', height: 100, borderRadius: 50}}
                                   source={require('../images/imgprofile.jpg')}
                            />
                        </View>
                        <View style={{flex: 1, flexDirection: 'column'}}>
                            <Text style={{marginLeft: 20, marginTop: 30, fontSize: 18}}>
                                {name}
                            </Text>
                            <Text style={{marginLeft: 20, fontSize: 28}}
                                  onPress={() => console.log('d')}>
                                +
                            </Text>
                            <Text style={{marginLeft: 20, marginTop: 5, fontSize: 18}}>
                                {count}
                            </Text>
                            <Text style={{marginLeft: 20, fontSize: 28}}>
                                -
                            </Text>
                        </View>
                    </View>


                </Container>
            </TouchableNativeFeedback>

        );
    }, [id, name, count, handleClicks]);

}

function DetailScreen() {
    const [context, setContext] = useContext(ListDataContext);
    const [listItemsRefresh, setListItemsRefresh] = useState(true)
    const handleClicks = (id)  => {
        for (let i = 0; i < context.length; i++){
            if (context[i]['id'] === id){
                context[i]['count'] = context[i]['count'] - 1;
                setContext(context);
            }
        }
        setListItemsRefresh(!listItemsRefresh);
        setContext(context);
        //navigation.navigate('Home');
        console.log(context);
    };
    return (
        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>

            <SafeAreaView style={{ marginBottom: 10,
                width: '90%',
                alignContent: 'center'}}>
                <FlatList
                    data={context}
                    renderItem={({item}) => <ListItem
                        id={item.id}
                        name={item.name}
                        count={item.count}
                        handleClicks={handleClicks}
                    />}
                    keyExtractor={item => item.id}
                    extraData={listItemsRefresh}
                />
            </SafeAreaView>

        </View>
    );
}

export default DetailScreen;

All I want is to update flatlist after Count is changed. Tnx

3

3 Answers

2
votes

After 2 Days of struggling I finally got the answer!! There is a prop in "@react-navigation/native" package called useIsFocused which determines whether the screen is focused (is showing) or not. So in order to re-render the FlatList all you have to do is calling useIsFocused() inside your screen function. Now my HomeScreen looks like this:

...
import { useIsFocused} from '@react-navigation/native'; 
...

function HomeScreen() {
    const [context, setContext] = useContext(ListDataContext);
    const [listItemsRefresh, setListItemsRefresh] = useState(false);
    const handleClicks = (id)  => {
        for (let i = 0; i < context[0].data.length; i++){
            if (context[0].data[i]['id'] === id){
                context[0].data[i]['count'] = context[0].data[i]['count'] + 1;
                context[0].badgeCount = context[0].badgeCount + 1;
            }
        }
        setListItemsRefresh(!listItemsRefresh);
        console.log(context[0].listRefresh)
        console.log(context);
    };

    //Add this line to tell the function that it's in focuse
    useIsFocused();

    return (
        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
            <SafeAreaView style={{ marginBottom: 10,
                width: '90%', height: '90%',
                alignContent: 'center'}}>
                <FlatList
                    data={context[0].data}
                    renderItem={({item}) => <ListItem
                        id={item.id}
                        name={item.name}
                        count={item.count}
                        handleClicks={handleClicks}
                    />}
                    keyExtractor={item => item.id}
                    extraData={listItemsRefresh}
                />
            </SafeAreaView>
        </View>
    );
}
export default HomeScreen;

Now add useIsFocused() to all of your screens

1
votes

I don't think this is the right way to update state :

If you want to update state that depends on the past state, you should do like this:

    setListItemsRefresh(listItemsRefresh => !listItemRefresh)
-1
votes

You are not supposed to mutate state, you can use spread: {...state,prop:'newValue'} and array methods map and filter or use immer