-2

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.

likith sai
  • 527
  • 1
  • 6
  • 21
  • Does this answer your question? [useState set method not reflecting change immediately](https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately) – Rafael Tavares Jan 19 '22 at 15:30

2 Answers2

1

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]);
Yaman KATBY
  • 1,814
  • 10
  • 24
0

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.

Ian Vasco
  • 1,280
  • 13
  • 17
  • OK. Can i use setItemData(itemData) inside the useEffect()? – likith sai Jan 19 '22 at 15:33
  • Well, it depends on what you are trying to do. If you dependency array in your `useEffect` includes `itemData` it won't make any sense since it will create a loop. What you can do is to show the Toast inside the `useEffect` to show the correct data. – Ian Vasco Jan 19 '22 at 15:43