reactjsreact-nativereact-hooksuse-effectreact-native-tab-view

How to update the multiple state using useState?


I'm using the TabView from the React-Native and as the part of this i'm using the two states for two different objects as shown below.

const [routes, setRoutes] = React.useState([]);
const [sceneMap, setSceneMap] = React.useState({});

I'm calling a function inside the useEffect hook whenever the TabView gets the focus. Also inside this hook itself i'm changes/reassigning the both of the states with the new values as shown below

React.useEffect(() => {
        // Return the function to unsubscribe from the event so it gets removed on unmount
        return navigation.addListener('focus', async () => {
            const dynamicTabsResponse = await getTabsDynamically();
            let response;
            if (dynamicTabsResponse != null) {
                response = await getRoutes(dynamicTabsResponse);
                **setRoutes(response);**

                const scenes = await getScenes(dynamicTabsResponse)
                newSceneMap = await getSceneMap(response, scenes)

                **setSceneMap({...newSceneMap});**
            }
        });
    }, []);

This is my return function:

if (routes.length > 0 && Object.keys(newSceneMap).length > 0) {
        return (
            <TabView
                navigationState={{index, routes}}
                renderScene={SceneMap(sceneMap)}
                onIndexChange={setIndex}
                initialLayout={initialLayout}
            />
        );
    } else {
      console.log( Object.keys(newSceneMap))
        console.log("routes length", routes.length)
        return null
    }

The problem is as soon as the first state gets update the re-render of the UI is happening and its causing the error, since my UI needs the updated state of the second one as well. How can i inform React-Native not to re-render until both the states get update? Is there a way to do like this or i'm implementing in a wrong way? Please suggest

Just to explain more consider the output below:

When i restart the app the values of Routes and Array is as below:

1. routes length inside if 2

2. inside if Array [
  "NEWS",
  "CRIME",
]

When go other screen and check the check box and come to Tabscreen, the output is as below:

1. routes length inside if 3

2. inside if Array [
  "NEWS",
  "CRIME",
]

As i can see from the 2nd output, the state of the Routes has been updated to 3(Which is causing the re-render), but not the state of the Array. Due to which TabView is displaying/throwing the error

Error:

Check the render method of `SceneComponent`., 
    in SceneComponent (created by SceneView)
    in RCTView (created by SceneView)
    in SceneView (created by Pager)
    in RCTView (at createAnimatedComponent.js:233)
    in AnimatedComponent(Component) (created by PanGestureHandler)
    in PanGestureHandler (created by Pager)
    in Pager (created by TabView)
    in RCTView (created by TabView)
    in TabView (at NewsScreen.js:73)
    in NewsScreen (at SceneView.tsx:98)
    in StaticContainer
    in StaticContainer (at SceneView.tsx:89)
    in EnsureSingleNavigator (at SceneView.tsx:88)
    in SceneView (at useDescriptors.tsx:125)
    in RCTView (at BottomTabView.tsx:38)
    in SceneContent (at BottomTabView.tsx:122)
    in RCTView (at ResourceSavingScene.tsx:44)
    in RCTView (at ResourceSavingScene.tsx:27)
    in ResourceSavingScene (at BottomTabView.tsx:117)
    in RCTView (at screens.native.js:132)
    in ScreenContainer (at BottomTabView.tsx:101)
    in RCTView (at BottomTabView.tsx:100)
    in SafeAreaProviderCompat (at BottomTabView.tsx:99)
    in BottomTabView (at createBottomTabNavigator.tsx:41)
    in BottomTabNavigator (at BottomTabNavigator.js:22)
    in BottomTabNavigator (at SceneView.tsx:98)
    in StaticContainer
    in StaticContainer (at SceneView.tsx:89)
    in EnsureSingleNavigator (at SceneView.tsx:88)
    in SceneView (at useDescriptors.tsx:125)
    in RCTView (at CardContainer.tsx:190)
    in RCTView (at CardContainer.tsx:189)
    in RCTView (at Card.tsx:526)
    in RCTView (at createAnimatedComponent.js:151)
    in AnimatedComponent (at Card.tsx:508)
    in PanGestureHandler (at Card.tsx:501)
    in RCTView (at createAnimatedComponent.js:151)
    in AnimatedComponent (at Card.tsx:497)

Solution

  • const Hello = () => {
     let response = [{one:"two"}]
     let newSceneMap = {
        name:"sdfvsdf"
     }
     const [routesAndsceneMap, setIt] = React.useState({
     routes:[{one:"three"}],
     sceneMap:{
       name:"sdfvds"
     }
    });
    const changeit = () => {
            setIt({...routesAndsceneMap,routes:[...response],sceneMap:  {...newSceneMap}})
     }
    return (
    <div>
      <h1>Hello {routesAndsceneMap.routes[0].one} {routesAndsceneMap.sceneMap.name}!</h1>
      <button onClick={changeit}>Click</button>
    </div>
    )};
    

    You can check here how it works For working see this link https://stackblitz.com/edit/react-p7jrjv?file=Hello.js

    You have added two different useState instead try to combine it into one as i have done and change it together in just one set function on button click it changes two properties together