grahammendick / navigation

Scene-Based Navigation for React and React Native
https://grahammendick.github.io/navigation/
Apache License 2.0
594 stars 41 forks source link

Select active tab programmatically #739

Closed salisuabubakar closed 1 year ago

salisuabubakar commented 1 year ago

Hi, is there a way to select active tab programmatically. Thanks

grahammendick commented 1 year ago

Hi, yea, you can control the active tab (you've asked me this before).

const [tab, setTab] = useState(0);
<TabBar tab={tab} onChangeTab={setTab}>

Then you can call setTab(1), for example, to change tabs programmatically.

salisuabubakar commented 1 year ago

Hi Graham, Sorry for the repeatition of my questions. I meant i have two component one is have muiltple sceneds for tabs and the other has multiple scnenes of tabs.

Example:

LoggedOutTab.js

return (
    // <TopContext.Provider value={{ switchToservicesTabScreen: () => setTab(1) }}>
    <>
      <NavigationBar
        hidden={true}
      />
      <TabBar
        primary={true}
        tab={tab}
        onChangeTab={setTab}
      >
        <TabBarItem
        >
          <NavigationHandler stateNavigator={stateNavigatorHomeScreen}>
            <NavigationStack>
              <Scene
                backgroundColor={"white"}
                stateKey="homeScreen"
              >
                <HomeScreen
                  switchToservicesTabScreen={() => setTab(1)}
                />
              </Scene>
            </NavigationStack>
          </NavigationHandler>
        </TabBarItem>

        <TabBarItem
        >
          <NavigationHandler stateNavigator={stateNavigatorServicesScreen}>
            <NavigationStack>
              <Scene
                stateKey="serviceScreen"
              >
                <ServiceScreen />
              </Scene>
            </NavigationStack>
          </NavigationHandler>
        </TabBarItem>

        <TabBarItem
        >
          <NavigationHandler stateNavigator={stateNavigatorAboutScreen}>
            <NavigationStack>
              <Scene stateKey="aboutScreen"
              >
                <AboutScreen />
              </Scene>
            </NavigationStack>
          </NavigationHandler>
        </TabBarItem>

        <TabBarItem
        >
          <NavigationHandler stateNavigator={stateNavigatorProfileScreen}>
            <NavigationStack
            >
              <Scene
                stateKey="profileScreen"
              >
                <ProfileScreen />
              </Scene>
            </NavigationStack>
          </NavigationHandler>
        </TabBarItem>
      </TabBar>
    </>
  )

LoggedInTab.js

return (
    // <TopContext.Provider value={{ switchToservicesTabScreen: () => setTab(1) }}>
    <>
      <NavigationBar
        hidden={true}
      />
      <TabBar
        primary={true}
        tab={tab}
        onChangeTab={setTab}
      >
        <TabBarItem
        >
          <NavigationHandler stateNavigator={stateNavigatorHomeScreen}>
            <NavigationStack>
              <Scene
                backgroundColor={"white"}
                stateKey="homeScreen"
              >
                <HomeScreen
                  switchToservicesTabScreen={() => setTab(1)}
                />
              </Scene>
            </NavigationStack>
          </NavigationHandler>
        </TabBarItem>

        <TabBarItem
        >
          <NavigationHandler stateNavigator={stateNavigatorServicesScreen}>
            <NavigationStack>
              <Scene
                stateKey="serviceScreen"
              >
                <ServiceScreen />
              </Scene>
            </NavigationStack>
          </NavigationHandler>
        </TabBarItem>

        <TabBarItem
        >
          <NavigationHandler stateNavigator={stateNavigatorAboutScreen}>
            <NavigationStack>
              <Scene stateKey="aboutScreen"
              >
                <AboutScreen />
              </Scene>
            </NavigationStack>
          </NavigationHandler>
        </TabBarItem>

        <TabBarItem
        >
          <NavigationHandler stateNavigator={stateNavigatorProfileScreen}>
            <NavigationStack
            >
              <Scene
                stateKey="anihScreen"
              >
                <AnihScreen />
              </Scene>
            </NavigationStack>
          </NavigationHandler>
        </TabBarItem>
      </TabBar>
    </>
  )

In my app.js exmaple:

      <NavigationHandler stateNavigator={stateNavigator}>
        <NavigationStack
          crumbStyle={from => from ? true : true}
          unmountStyle={from => from ? false : false}
        >
          <Scene
            stateKey="loggedInTab">
            <LoggedInTab />
          </Scene>
          <Scene
            stateKey="loggedOutTab">
            <LoggedOutTab />
          </Scene>
        </NavigationStack>
      </NavigationHandler>

In my app.js am doing using react-native-mmkv library to check if user has loggedin and if its true then navigate to the LoggedInTab.js ....

  useEffect(() => {
    const hasUserName = storage.contains('storageusernameData')
    hasUserName === true ? stateNavigator.navigate('loggedInTab') : stateNavigator.navigate('loggedOutTab');
  }, []);

My question is that the right thing to do or i can use state or anything to change the two tabbed components . Very sorry for the way i ask the questions earlier.

Thanks

grahammendick commented 1 year ago

Hey, no problem. The more information you give the better. The way you're doing it is fine. You can also do it like below. The Navigation router will automatically change from loggedOutTab to loggedInTab.

<NavigationStack>
  {hasUserName ? (
    <Scene stateKey="loggedInTab">
      <LoggedInTab />
    </Scene>
  ) : (
    <Scene stateKey="loggedOutTab">
      <LoggedOutTab />
    </Scene>
  )}
</NavigationStack>
salisuabubakar commented 1 year ago

Hi, Thanks for your response. When i do this:

    <NavigationStack
      crumbStyle={from => from ? true : true}
      unmountStyle={from => from ? false : false}
    >
      {hasUserName ? (
        <Scene stateKey="loggedInTab" j>
          <LoggedInTab />
        </Scene>
      ) : (
        <Scene stateKey="loggedOutTab">
          <LoggedOutTab />
        </Scene>
      )}
    </NavigationStack>

Am not able to move from loggedInTab to loggedOutTab and i dont see any error message. Can you check it please.

grahammendick commented 1 year ago

You checked that the value of hasUserName has changed?

salisuabubakar commented 1 year ago

Yes i do console.log(hasUsername) and console will log true if there is a value and if there is no value console will lof false.

grahammendick commented 1 year ago

I changed the App component of the twitter sample so that it does the logged in switch. I put it in a 5 second timer just to show it working. You can see in the vid below that after 5 seconds the logged out tabs are removed and the logged in tabs are shown - the tabs are the same in this example, but they reset which shows they've changed.

const stateNavigator = new StateNavigator([
  {key: 'tabs'},
  {key: 'tabsLoggedIn'},
  {key: 'home'},
  {key: 'notifications'},
  {key: 'tweet', trackCrumbTrail: true},
  {key: 'timeline', trackCrumbTrail: true}
]);

const App = () => {
  const [loggedIn, setLoggedIn] = useState(false);
  useEffect(() => {
    const timer = setTimeout(() => setLoggedIn(true), 5000);
    return () => clearTimeout(timer);
  }, [])
  return (
    <NavigationHandler stateNavigator={stateNavigator}>
      <NavigationStack
        crumbStyle={from => from ? 'scale_in' : 'scale_out'}
        unmountStyle={from => from ? 'slide_in' : 'slide_out'}
      >
        {!loggedIn ? (
          <Scene stateKey="tabs"><Tabs /></Scene>
        ): (
          <Scene stateKey="tabsLoggedIn"><Tabs /></Scene>
        )}
      </NavigationStack>
    </NavigationHandler>
  )
};

https://github.com/grahammendick/navigation/assets/1761227/c0449754-d226-430b-9a35-a37e50fd5729

salisuabubakar commented 1 year ago

Hi, Thanks for your reseponse. I appreciate. I still couldnt get it to work but doing this works: useEffect(() => { const hasUserName = storage.contains('storageusernameData') hasUserName === true ? stateNavigator.navigate('loggedInTab') : stateNavigator.navigate('loggedOutTab'); }, []);

But i have a challenge. When i use prop from MyANIHScreen.js and i want to navigate loggedOutTab it works but then the ProfileScreen of the LoggedOutTab which is the last tab is always blank but i see the bottomtabs below. When i close the app and refresh then the Profilescreen comes back to normal.

Let me explain further. The ProfileScreen.js is my login page and when the user login and its successfull then i check if its true then navigate to the loggedInTab.js but when after login is successfull, and i logout using the pro i stated above it navigate to the loggedOutTab which is perfect as i want but the ProfileScreen which is the loggin screen becomes blank untill i close and open the app before it comes to normal.

Hope you understood. Thanks <Scene stateKey="myANIHScreen"

<MyANIHScreen logoutButtonToLogoutTab={() => stateNavigator.navigate("loggedOutTab")} />

grahammendick commented 1 year ago

Let's start with the first problem for now. So I want to get my original snippet to work for you.

Where is the code that writes to storage? After you write storageusernameData into storage then you need to re-render your App component. If you don't re-render then you won't be able to change scene when you log in or out.

There are many different ways to write to storage that will trigger a re-render. Which one of these are you using?

salisuabubakar commented 1 year ago

Hi, Below is the code i write to storage: storage.set('storageusernameData', data.current_user.name);

Thanks

grahammendick commented 1 year ago

That won't trigger a rerender. You need to use one of these ways so that the component re-renders after writing

salisuabubakar commented 1 year ago

Can you give example of one specific that will rerender please. Its been about two weeks now and am stuck. Thanks

grahammendick commented 1 year ago

Use the hooks

salisuabubakar commented 1 year ago

Hi, am doing this to store now const [checkUsername, setCheckUsername] = useMMKVString("storageusernameData")

and storing the data from fetch and if its successfull i excecute this storage.set('storageusernameData', data.current_user.name); setCheckUsername(data.current_user.name)

Now my issues is how do i check if there is data and change it from LoggedinTab to LoggedOutTab in my App.js.

Is it possible to pass the state checkUsername to the App.js and check if there is value ?

Very sorry am asking too much. Am stacked for about two weeks now and i will appreciate it if you can give me some advise or examples.

Thanks

grahammendick commented 1 year ago
const [username, setUsername] = useMMKVString("storageusernameData")

You don't need to call storage.set anymore. You only need to call setUsername. Then render the logged in scene if userName is there

<NavigationStack>
  {username ? (
    <Scene stateKey="loggedInTab">
      <LoggedInTab />
    </Scene>
  ) : (
    <Scene stateKey="loggedOutTab">
      <LoggedOutTab />
    </Scene>
  )}
</NavigationStack>
salisuabubakar commented 1 year ago

Hi, Thanks very much for your time. its working well now. below is what i did

const [checkUsername, setCheckUsername] = useMMKVString("storageusernameData")

  <NavigationHandler stateNavigator={stateNavigator}>
    <NavigationStack
      crumbStyle={from => from ? true : true}
      unmountStyle={from => from ? true : true}
    >
      {
        checkUsername ?
          <Scene
            stateKey="loggedInTab">
            <LoggedInTab />
          </Scene>
          :
          <Scene
            stateKey="loggedOutTab">
            <LoggedOutTab />
          </Scene>
      }
    </NavigationStack>
  </NavigationHandler>
</>

I really appreciate. Now the issue of the blank screen i get after i logout. Please help.

Thanks and God Bless

grahammendick commented 1 year ago

I couldn't understand the blank screen. Could you explain again, please? Maybe include a video. If you want me to help you have to help me out too

salisuabubakar commented 1 year ago

Hi, Thanks soo much for your time. Let me explain the blank screen issue.

when i login and it’s successful I navigate to the LoggedInTab.is and one of the scenes is called profile which has the login form.

when I logout is navigates to back to LoggedOutTab which is the profile screen which is part of the screen in the LoggedOnTab.

the issue is that the profile screen becomes blank when I logout.

below is the video.

https://github.com/grahammendick/navigation/assets/6886556/76e1d682-97ca-4c93-b3fc-c9ad2961427d

Thanks

grahammendick commented 1 year ago

Thanks for the extra info but I can't debug that for you. I'd put in console logs and see what's rendering.

But why are you switching all the tabs in and out when the user logs in and out? The tabs are the same whether they're logged in or out so it makes sense to keep one set of tabs. Then when the user logs in and out just change the content of the scenes.

salisuabubakar commented 1 year ago

Yeah i thought about that. Will try that. Thanks soo much. Really appreciate.

salisuabubakar commented 1 year ago

I have used one tab now and changed the scene based on user logged in or logged out. Am very greatful. Its working perfect and i dont have any blank screen now. Thanks

grahammendick commented 1 year ago

Nice!