React Native: Variable state not updated on first click

235 Views Asked by At

I am new in react-native and i am working on an app.

The below code is a simple react-native app which has a custom component with custom events.

But the problem is the variable state is not updated on the first click on the component. But when i click on the second item, The state of the variable is updated.

Please find the code and screenshot below.

App.js

import React, {useState} from 'react';
import { Text, SafeAreaView, ToastAndroid } from 'react-native';
import Dropdown from './components/dropdown';

const app = () => {
    const [ itemData, setItemData ] = useState('');

    return (
        <SafeAreaView style={{ margin: 50 }}>
            <Dropdown 
              onPressItems={(item) => {
                  ToastAndroid.show('item: ' + item, ToastAndroid.LONG)
                  setItemData(item)
                  ToastAndroid.show('setItem: ' + itemData, ToastAndroid.LONG)
              }}/>
        </SafeAreaView>
    );
}

export default app;

Dropdown.js

import React, { useState } from 'react';
import { TouchableOpacity, Text } from 'react-native';

const Dropdown = (props) => {
    return (
        <TouchableOpacity onPress={() => { props.onPressItems('this is sample data') }}>
            <Text>Sample Text</Text>
        </TouchableOpacity>
    );
} 

export default Dropdown;

Screenshot enter image description here

Code: https://snack.expo.dev/@likithsai/custom-component

Please help me on this issue. Thanks.

2

There are 2 best solutions below

8
Yaman KATBY On BEST ANSWER

useState() hook changes the state asynchronously. so you can't make sure that the state will be changed immediately after calling setItemData() function.

Try useEffect to run a side effect whenever the state changes.

useEffect(() => {
  ToastAndroid.show("setItem: " + itemData, ToastAndroid.LONG);
}, [itemData]);

However, this code will show the toast on the component mount. to prevent it try something like this:

const isInitialMount = useRef(true);

useEffect(() => {
  if (isInitialMount.current) {
    isInitialMount.current = false;
  } else {
    ToastAndroid.show("setItem: " + itemData, ToastAndroid.LONG);
  }
}, [itemData]);
2
Ian Vasco On

Your code is working just as expected. One of the hooks that are useful to watch for state updates is useEffect. You can add this to your code and see it's working properly:

const app = () => {
    const [ itemData, setItemData ] = useState('');

    React.useEffect(() => {
      console.log('updated itemData:', itemData)
    }, [itemData])

    return (
        <SafeAreaView style={{ margin: 50 }}>
            <Dropdown 
              onPressItems={(item) => {
                  ToastAndroid.show('item: ' + item, ToastAndroid.LONG)
                  setItemData(item)
                  ToastAndroid.show('setItem: ' + itemData, ToastAndroid.LONG)
              }}/>
        </SafeAreaView>
    );
}

You need to take into consideration that useState updates are asynchronous, which means the change won't be reflected immediately.