How to add screen at beginnig with CommonsActions.reset() in react navigation - react-native

I want to change screens order in navigation flow reseting navigation state
I want to change screens order in navigation flow, something like
{
condition ? <> <Screen A/> <Screen B/> </> : <> <Screen B/> <Screen A/> </>
}
following [https://reactnavigation.org/docs/auth-flow/#removing-shared-screens-when-auth-state-changes%5C]
How is the same screen this dont change the order (only when get condition from storage when app starts), so i was trying something like this to add screen A at beginnig keeping the others
navigation.dispatch((state) => {
const stateCopy = { ...state };
const routes = stateCopy?.routes?.unshift({ name: 'screen-A' });
// screen a dont have the key as the others in state
return CommonActions.reset({
...state,
routes,
index: 0,
});
});
Basically this doesnt works and i didnt find an example in [https://reactnavigation.org/docs/navigation-actions/#reset%5C] . How can i do this? Thanks.

Related

React Native -Undefined param while coming back to previews screen

I have a root screen called selectlanguage once user select his language I'm passing that selected language as params to the Home screen, and then to "Home" children. but once I hit back button from that child screen to the Home that param turns to undefined that why when I want to open that child screen again it cause some issues.
Root screen selectedLanguage:
<View style={{ marginTop: 40 }}>
<Button
handleClick={() =>
navigation.navigate('Home', { lan: userLanData.value })
}
title={'NEXT'}
{...}
/>
Home screen:
const lan = route.params?.lan;
// and below i'm passing above param as props to Task screen
<Tasks
lanParam={lan}
{...}
/>
Task screen:
//Again passing it to another Child screen
<TouchableOpacity
onPress={() =>
navigation.navigate('byWords', {
id: item.id,
currentLevelName,
index,
lanParam, <-----
})
}>
{...}
</TouchableOpacity>
Task Child screen:
const lanParam = route?.params?.lanParam;
const colRef = collection(
db,
`${lanParam && lanParam}/${currLevelParam}/tasks/${id}/byWords/`
);
And from this point I had an idea to try send this Param back to home screen while coming back like this:
<Header
{...}
goBack={() =>
navigation.navigate('Home', { index, currLevelParam, lanParam })
}
/>
And now Home screen should look like this and I'm receiving that value back again:
Home screen:
const lan = route.params?.lan;
const lanParam = route.params?.lanParam;
// and below i'm passing above param as props to Task screen
<Tasks
lanParam={lan}
{...}
/>
But the problem is that, how could I use that value again since we have no access to the root screen again ?
it was easy to fix!!
I just modified lanParams to lan to keep same as Original param and update Home param again while coming back

React Native: How to handle protected routes with react navigation

What I want is to protect specific screens where the user can run specific actions and only at that point redirect him to a login screen, if the user is successfully authenticated, redirect him to the point where he left off the flow.
We can do this in React JS(with react-router), so I would like to know if its posible to implement a similar solution for react-native(with react-navigation):
React JS approach with React Router
function RequireAuth({ children }) {
const { authed } = useAuth();
const location = useLocation();
return authed === true ? (
children
) : (
<Navigate to="/login" replace state={{ path: location.pathname }} />
);
}
This is possible in react native and is documented in the authentication workflow section of the documentation.
In summary we conditionally need to render screens inside the navigator. Here is a minimal example using a Stack.Navigator which is similar to your posted code snippet. The workflow is identical for other navigators (nested or not). I will use the context api in my example, but there are other ways to achieve the same as well. The overall pattern is the same.
const AppContext = React.createContext()
function App() {
const [isSignedIn, setIsSignedIn] = React.useState(false)
const appContextValue = useMemo(
() => ({
isSignedIn,
setIsSignedIn,
}),
[isSignedIn]
)
return (
<AppContext.Provider value={appContextValue}>
<Stack.Navigator>
{!isSignedIn ? (
<Stack.Screen
name="Login"
component={LoginScreen}
/>
) : (
// whatever screens if user is logged in
<Stack.Screen name="Home" component={HomeScreen} />
)}
</Stack.Navigator>
</AppContext.Provider>
);
In the LoginScreen, you then need to change the authed state to true if the login was successful. The first screen in the conditional will then be initially rendered automatically (in this case HomeScreen).
function LoginScreen() {
const { setIsSignedIn } = useContext(AppContext)
// do whatever, then setIsSignedIn to true
}

React Native Screen refresh from nested component screen

I have a screen, PDP, that screen contains a component, TopNews. I want TopNews onclick to redraw PDP (it passes in an Article ID which the PDP uses to retrieve the article). The diagram below shows the flow
The code I have to support this inside TopNews is;
<TouchableOpacity onPress={() => navigation.navigate('Pdp', {articleid: item.id})}>
The challenge is the TouchableOpacity event triggers, but the page doesn't refresh the PDP. I don't want to refresh only the PDP as I may include the TopNews component in other screens outside of the PDP, its just in this case its inside of the screen it needs to call.
There are more then one way to do it.
You can create a state for the article id and allow the TopNews to set this state:
const PDP = () => {
const [ id, setId ] = React.useState(0);
return (
<div>
<ArticleComponent articleId={id} />
<TopNews idSetter={setId} />
</div>
);
}
const ArticleComponent = ({ articleId }) => {
return /* something that renders the article by the id */ ;
}
const TopNews = ({ idSetter }) => {
return (
<TouchableOpacity
onPress={() => {
idSetter(item.id);
navigation.navigate('Pdp')
}}>
);
}
Another more elegant way, is to use react contexts. You can create a context provider in the PDP component and change the context data in any PDP's children component.
I found the answer, instead of using navigation.navigate, use navigation.push instead;
<TouchableOpacity onPress={() => navigation.push('Pdp', {articleid: item.id})}>
This works perfectly :)

React Native Error: The action 'NAVIGATE' with payload {"name":"GameScreen"} was not handled by any navigator

I know that this issue has been brought up before but I have not found any answer that works for me.
I simply want to navigate from screen "Start" to screen "Game", using App.js as the router.
App.js:
const Stack = createStackNavigator();
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Start">
<Stack.Screen name="Start" component={StartingScreen} />
<Stack.Screen name="Game" component={GameScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
StartingScreen.js:
const StartingScreen = ({ navigation }) => {
//lots of code
return (
//more code
<Button title="Begin" onPress={() => {navigation.navigate('GameScreen')}}/>
)
This gives me the error in the title with ("Do you have a game named 'GameScreen'?") and nothing happens. I have tried following the React Navigation docs, but in their example they put everything in App.js, that does not work for me. Other things I have tried include exporting the navigation stack to StartingScreen.js, changing the arguments of navigation.navigate(), placing the navigator inside StartingScreen.js.
GameScreen is spelled exactly the same in all places.
Change your code like below
const StartingScreen = ({ navigation }) => {
//lots of code
return (
//more code
<Button title="Begin" onPress={() => {navigation.navigate('Game')}}/>
)
You are giving the component name but you should provide the name of the screen that you give in the stack which is 'Game'
As your screen name is given 'Game' in the stack navigator, you should navigate to 'Game' instead of 'GameScreen'.
onPress={() => {navigation.navigate('Game')}}

React Native/Shoutem: navigateBack() not working

My Problem is that I would like to navigateBack() from the BountyDetailsScreen to the LoyaltyScreen, but the navigateBack() function call does not trigger any action. When I log the function it says:
The only thing I notice is, that the navigationStack is empty. When I do the same with the navigateTo function it is working, but then I have a messed up navigation stack.
In my LoyaltyScreen.js I am displaying a ListView. It is a RN ListView (not imported from shoutem).
LoyaltyScreen.js
renderRow(bounty) {
return (
<ListBountiesView
key={bounty.id}
bounty={bounty}
onDetailPress={this.openDetailsScreen}
redeemBounty={this.redeemBounty}
/>
);
}
ListBountiesView.js
The ListBountiesView renders each ListView Row and opens a Detail Screen when clicked on the Row.
render() {
const { bounty } = this.props;
return (
<TouchableOpacity onPress={this.onDetailPress}>
{bounty.type == 0 ? this.renderInShopBounty() : this.renderContestBounty()}
<Divider styleName="line" />
</TouchableOpacity>
);
}
BountyDetailsScreen.js
In the BountyDetailsScreen I display detailed information and would like to navigateBack() to the Loyalty Screen when I press a button.
<Button styleName="full-width" onPress={() => this.onRedeemClick()}>
<Icon name="add-to-cart" />
<Text>Einlösen</Text>
</Button>
onRedeemClick() {
const { bounty, onRedeemPress } = this.props;
onRedeemPress(bounty);
navigateBack();
}
navigateBack is an action creator. You need to map it to props and read it from props in your redeemClick function. Just executing the imported action creator won't do anything since it's not connected to Redux.
Here's an example of you map it to props:
export default connect(mapStateToProps, { navigateBack })(SomeScreen));
Here's how you use it:
const { navigateBack } = this.props;
navigateBack();
I can see that airmiha's answer is what you're looking for, but I just wanted to add onto it.
You can also use hasHistory to set up your #shoutem/ui NavigationBar (if you're using it) with a simple back button that utilises navigateBack().
<NavigationBar
styleName="no-border"
hasHistory
title="The Orange Tabbies"
share={{
link: 'http://the-orange-tabbies.org',
text: 'I was underwhelmed by The Orange Tabbies, but then I looked at that
sweet, sweet back button on the Nav Bar.
#MakeNavBarsGreatAgain',
title: 'Nevermind the cats, check the Nav Bar!',
}}
/>
You can find more examples with the NavigationBar component here.