score:6
update (refactored code)
import { usenavigation, commonactions } from "@react-navigation/native";
export default function useresetprofilestacknavigator() {
const navigation = usenavigation();
return () => {
const bottomtabnavigator = navigation
.getstate()
?.routes?.find(({ name }) => name === "bottomtabnavigator");
const profiletab = bottomtabnavigator?.state?.routes?.find(
({ name }) => name === "profilestacks"
);
const { key: target, routes } = profiletab?.state ?? {};
if (!target || routes?.length <= 1) return;
routes.length = 1; // poptotop()
navigation.dispatch({
...commonactions.reset({ routes }),
target,
});
};
}
and here is how to use it:
export default function usesendposttoprofile() {
const navigation = usenavigation();
const issending = useref(false);
const resetprofilestacknavigator = useresetprofilestacknavigator();
return (post) => {
if (issending.current) return;
issending.current = true;
// make sure there is only one route open in the profile stack
resetprofilestacknavigator();
navigation.navigate("bottomtabnavigator", {
screen: "profilestacks",
params: {
screen: "profile",
params: {
post,
},
},
});
};
}
previous solution
after a few hours studying the problem i have found a solution. it's not the best but it works for my use case and it surely works for other people's.
what i have tried to achieve is to reset the routes of the "profile" screen that is in a stack navigator that in turn is in another tab of the tab navigator in which my current stack screen is. it sounds somewhat confusing, but it is basically something similar to what happens on instagram when you upload a photo.
if in instagram you navigate to other users profiles from the home screen and then upload a photo to your account, you will see how you go from the "publish your photo" screen to the root of the stack navigator that is in the home tab, the feed.
in my use case, i am doing something similar, i can navigate to other users profiles from my own profile, and the photos are uploaded in this screen, with a progress bar.
from the beginning i had in mind to use navigation.poptotop (), but i have not been able to obtain the result i wanted, because as i have commented previously in the question, the parameters (that contained the post) were lost. so i have no choice but to simulate this behavior from my "publish photo" screen.
the steps i have followed are as follows:
- as my "publish photo" screen shares the navigation with my "profile" screen, through the tab navigator (which is obvious, since if it were not like that i could not do the navigation.navigate()), i have followed the navigation path from this to the stack navigator of the profile tab and then i have tried to take both its key and its routes.
- in case i have found the current key and paths, that means the stack navigator is mounted (in my case, the tab does a lazy initialization of all my pages, that's why i speak of "trying to take"). so it will be necessary to apply steps 3 and 4.
- simulate the navigation.poptotop() reducing the size of the routes to 1 (note that the root of the stack navigator is the item in the first position of the "routes" array)
- dispatch the reset operation over the profile's stack navigator using the navigation api.
- the final step, navigate to the stack screen normally passing the photo as param.
here is the code:
const resetprofilestacknavigator = () => {
const currentnavigationstate = navigation.dangerouslygetstate();
// find the bottom navigator
for (let i = 0; i < currentnavigationstate?.routes?.length; i++) {
if (currentnavigationstate.routes[i].name === "bottomtabnavigator") {
// get its state
const bottomnavigationstate = currentnavigationstate.routes[i].state;
// find the profile tab
for (let j = 0; j < bottomnavigationstate?.routes?.length; j++) {
if (bottomnavigationstate.routes[j].name === "profile") {
// get its state
const profiletabstate = bottomnavigationstate.routes[j].state;
// get the key of the profile tab's stack navigator
var targetkey = profiletabstate?.key;
var targetcurrentroutes = profiletabstate?.routes;
break;
}
}
break;
}
}
// reset the profile tab's stack navigator if it exists and has more than one stacked screen
if (targetkey && targetcurrentroutes?.length > 1) {
// set a new size for its current routes array, which is faster than array.splice to mutate
targetcurrentroutes.length = 1; // this simulates the navigation.poptotop()
navigation.dispatch({
...commonactions.reset({
routes: targetcurrentroutes, // it is necessary to copy the existing root route, with the same key, to avoid the component unmounting
}),
target: targetkey,
});
}
}
/*
maybe, the stack navigator of the profile tab exists and has changed from its initial state...
in this situation, we will have to find the key of this stack navigator, which is also
nested in the same tab navigator in which this stack screen is.
*/
resetprofilestacknavigator();
// finally, navigate to the profile stack screen and pass the post as param
navigation.navigate("profile", {
screen: "profile",
params: {
post,
},
});
pd: i know there are some applicable refactorings, but i prefer to display the code this way so that the steps i discussed above are clearly visible.
if anyone who has read this manages to generalize this segment of code into a generic function using es6, please leave it as an answer, as it can be very useful for me and for other users.
score:1
this code is enough and it works
<tab.screen name='searchnavigator' component={searchnavigator}
listeners={props => tabpresslistener({ ...props })}/>
and this
const tabpresslistener = props => {
const { navigation } = props
return {
blur: e => {
const target = e.target
const state = navigation.dangerouslygetstate()
const route = state.routes.find(r => r.key === target)
// if we are leaving a tab that has its own stack navigation, then clear it
if (route.state?.type === "stack" && route.state.routes?.length > 1) {
navigation.dispatch(stackactions.poptotop())
}
}
}
}
score:1
as of react navigation 6.x you can easily achieve this with commonactions.reset
. i wrote some example code based on the original question assuming only one stack, haven't tested for nested stacks but the solution might be something similar.
import {commonactions} from '@react-navigation/native';
navigation.dispatch((state) => {
const params = state.routes[state.routes.length - 1].params;
return commonactions.reset({
index: 1,
routes: [{name: 'home', params}]
});
});
score:4
i was struggling with similar issue. my case was that i want to have the same stack navigator and the tabs would be just different starting points, something like 2 home screens. it is the behavior that is seen in spotify for android, for example - we have home, search and library and all of them have common screens such as album screen and song screen. and when the user clicks on one of the tabs, the stack is cleared (like poptotop() should do).
my solution was to have bottom tab navigator with stack navigators that have the same screens - discovernavigator and searchnavigator:
const searchnavigator = () => (
<stack.navigator headermode='screen'>
<stack.screen name='search' component={searchscreen} />
<stack.screen name='searchresults' component={searchresultsscreen} />
<stack.screen name='item' component={itemscreen} />
</stack.navigator>
)
const discovernavigator = () => (
<stack.navigator headermode='screen'>
<stack.screen name='discover' component={discoverscreen} />
<stack.screen name='search' component={searchscreen} />
<stack.screen name='searchresults' component={searchresultsscreen} />
<stack.screen name='item' component={itemscreen} />
</stack.navigator>
)
and the trick is to add a listener on blur for the tabs, like that:
<navigationcontainer>
<tab.navigator>
<tab.screen name='discovernavigator' component={discovernavigator}
listeners={props => tabpresslistener({ ...props })}
/>
<tab.screen name='searchnavigator' component={searchnavigator}
listeners={props => tabpresslistener({ ...props })}
/>
</tab.navigator>
</navigationcontainer>
that the handler for the blur event will check if the current tab has its own stack navigation and if it should clear it:
const tabpresslistener = props => {
const { navigation } = props
return {
blur: e => {
const target = e.target
const state = navigation.dangerouslygetstate()
const route = state.routes.find(r => r.key === target)
// if we are leaving a tab that has its own stack navigation, then clear it
if (route.state?.type === "stack" && route.state.routes?.length > 1) {
navigation.dispatch(stackactions.poptotop())
}
}
}
}
here is a demo: https://snack.expo.io/@monikamateeva/bottom-tab-navigation-with-poptotop
and this is all of the code:
import { createbottomtabnavigator } from '@react-navigation/bottom-tabs'
import { navigationcontainer, commonactions, stackactions } from '@react-navigation/native'
import { createstacknavigator } from '@react-navigation/stack'
import react from 'react'
import { button, stylesheet, view } from 'react-native'
import { enablescreens } from 'react-native-screens';
enablescreens();
const stack = createstacknavigator()
const tab = createbottomtabnavigator()
const itemscreen = ({ navigation, route }) => {
react.uselayouteffect(() => {
navigation.setoptions({
title: `item ${route.params?.id}`,
})
}, [navigation, route])
return (
<view style={styles.container}>
<button title='item 2' onpress={() => navigation.push('item', { id: 2 })} />
</view>
)
}
const searchresultsscreen = ({ navigation, route }) => (
<view style={styles.container}>
<button title={`item ${route.params?.id}`} onpress={() => navigation.push('item', { id: route.params?.id })} />
</view>
)
const discoverscreen = ({ navigation }) => (
<view style={styles.container}>
<button title='search results 20' onpress={() => navigation.navigate('search', { screen: 'searchresults', params: { id: 20 } })} />
<button title='item 20' onpress={() => navigation.navigate('search', { screen: 'item', params: { id: 20 } })} />
</view>
)
const searchscreen = ({ navigation }) => (
<view style={styles.container}>
<button title='search results 10' onpress={() => navigation.push('searchresults', { id: 20 })} />
<button title='item 10' onpress={() => navigation.push('item', { id: 10 })} />
</view>
)
const searchnavigator = () => (
<stack.navigator headermode='screen'>
<stack.screen name='search' component={searchscreen} />
<stack.screen name='searchresults' component={searchresultsscreen} />
<stack.screen name='item' component={itemscreen} />
</stack.navigator>
)
const discovernavigator = () => (
<stack.navigator headermode='screen'>
<stack.screen name='discover' component={discoverscreen} />
<stack.screen name='search' component={searchscreen} />
<stack.screen name='searchresults' component={searchresultsscreen} />
<stack.screen name='item' component={itemscreen} />
</stack.navigator>
)
const tabpresslistener = props => {
const { navigation } = props
return {
blur: e => {
const target = e.target
const state = navigation.dangerouslygetstate()
const route = state.routes.find(r => r.key === target)
// if we are leaving a tab that has its own stack navigation, then clear it
if (route.state?.type === "stack" && route.state.routes?.length > 1) {
navigation.dispatch(stackactions.poptotop())
}
},
// log the state for debug only
state: e => {
const state = navigation.dangerouslygetstate()
console.log(`state`, state)
}
}
}
const appnavigator = () => {
return (
<navigationcontainer name="bottomtabnavigator">
<tab.navigator>
<tab.screen
name='discovernavigator'
component={discovernavigator}
listeners={props => tabpresslistener({ ...props })}
/>
<tab.screen
name='searchnavigator'
component={searchnavigator}
listeners={props => tabpresslistener({ ...props })}
/>
</tab.navigator>
</navigationcontainer>
)
}
export default appnavigator
const styles = stylesheet.create({
container: {
flex: 1,
},
})
Source: stackoverflow.com
Related Query
- React Navigation 5 - Reset a stack (similar to popToTop()) from another stack in a different tab before navigating to it
- React navigation 5 hide tab bar from stack navigator
- Remove last route from react navigation stack
- React Navigation Reset Stack Navigator which is sub navigation
- Which method is called when the screen is displayed from the Navigation Stack in React Native
- How can I render HTML from another file in a React component?
- how to navigate from one page to another in react js?
- Making an HTML string from a React component in background, how to use the string by dangerouslySetInnerHTML in another React component
- Re-render component when navigating the stack with React Navigation
- How to get refs from another component in React JS
- React Navigation TabNavigator: Reset previous tab on tab change
- Where to put all the screens which are common in multiple stack navigators? - React Navigation v5
- How to update the state of a sibling component from another sibling or imported component in REACT
- React Navigation params doesn't reset
- React - Triggering a component method from another component, both belonging in the same render()
- React Navigation: How to update navigation title for parent, when value comes from redux and gets updated in child?
- Call function from another React component
- 'Maximum call stack size exceeded' from dotenv-expand on CircleCI using React
- React Navigation 5 - How to navigate from headerRight?
- React - toggle display of one component from the onClick of another component
- Reset navigation history to Login screen using react navigation
- iOS (React native): Unnecessary space from the top of the header rendered using react navigation
- React Native Stack Navigation With Class Component
- React - How do you call a function from inside another function
- React animation for moving an element from one parent to another
- react how to call state variable from another class?
- Animate movement of React component from one to another
- Reset a React Native Formik form from outside the form
- How to from one component set state another component in react native?
- In React why does calling a function from another function in our code not work and display?
More Query from same tag
- How can I provide a clickable link in a tooltip?
- Authentication using Passport.js on Node.js with React.js frontend
- AADSTS700016 Adal problem with wrong tenant directory
- React : Accessing props data through NavLink from parent component
- Extending Components in rebass.js?
- How to put the icon inside the React Material Ui text field?
- Array items sorting in javascript
- Setting state conditionally based on prevState in useEffect cause exponential re-render
- Getting props from parent component state to render data
- React js changing state does not update component
- How to load favicon in django server from react build folder? manifest.json not found error
- Why is the Redux store being updated with a duplicate nested key?
- How do I invalidate the old images when request for a new one is initiated in React?
- React use img onerror
- TypeError: Cannot read properties of undefined (reading 'map'). Error being thrown when I am trying to map over an array of cryptocurrencies
- React container component's method test with jest
- Why react rerender another variable?
- Make div's below each other
- Prevent React from importing CSS files that are not being used yet
- How can I make responsive images from Ghost (Content API) to Next.js?
- Stripe with Node throws Error 400: "Cannot read property 'sessions' of undefined"
- Extending component class exported wrapped in a HOC
- I am rendring brightcove player from componentDidMount() and componentDidUpdate() , DOM is getting updated but video is not initialized . React+Redux
- Using localStorage in React
- How to check if a page exist in a custom server using Next.js
- How to get field from multiple-layer document in Firestore
- React-Firebase routing and authentication approach
- this.state is undefined when used in function React JS component
- How to convert 2 object into single one
- Trigger react select onchange