Sending params with navigation.goBack in react navigation - react-native

hellow , how to send params navigation go back ?
const Onsubmit = (data, details = null) => {
console.log(details.formatted_address);
route.params.onPlaceChosen(
route.params.id,
details.formatted_address,
details.geometry
);
navigation.goBack();
};
here I want to pass the value from details.formatted_address to page B.
How to ?

If you are navigating from Screen A to Screen B, and when you want to go back to Screen A with running a callback in again Screen A, below is what you need to do:
In your Screen A (Source)
...
const onPlaceChosen = (params) => {
// here is your callback function
}
...
navigation.navigate('ScreenB', { onPlaceChosen })
...
In your Screen B (Destination)
..
const Onsubmit = (data, details = null) => {
navigation.state.params.onPlaceChosen(
route.params.id,
details.formatted_address,
details.geometry
);
navigation.goBack();
};
...

I did something like this based on https://reactnavigation.org/docs/5.x/hello-react-navigation/#passing-additional-props
const [params, setParams] = useState({});
<Stack.Navigator>
<Stack.Screen name="One">
{props => <FirstScreen {...props} paramsFromTwo={params} />}
</Stack.Screen>
<Stack.Screen name="Two">
{props => <SecondScreen {...props} onGoBack={(paramsFromSecond} => setParams(paramsFromSecond)} />}
</Stack.Screen>
</Stack.Navigator>

I did this way:
onPress={() => {
// Pass and merge params back to home screen
navigation.navigate({
name: 'Home',
params: { post: postText },
merge: true,
});
}}
It was extracted from:
https://reactnavigation.org/docs/params#passing-params-to-a-previous-screen

In my case, using navigation and route passed by props, the solution was:
route.params.onFilter({
route.params.id,
details.formatted_address,
details.geometry
});
navigation.goBack();

Related

I want to prevent the application from going to the login screen, how do I do that?

const [cred, setcred] = React.useState(false);
const [state, setState] = React.useState(false)
React.useEffect(() => {
isLogg();
}, []);
const isLogg = async () => {
const credentials = await Keychain.getGenericPassword();
if (credentials) {
setcred(true);
//SplashScreen.hide();
} else {
// await Keychain.resetGenericPassword();
setcred(false);
SplashScreen.hide();
}
};
const Stack = () => {
return (
<StackNav.Navigator>
{!cred ? (
<StackNav.Screen
name={SCREEN.AuthNavigation}
component={AuthNavigation}
options={{ headerShown: false }}
/>
) : (
<StackNav.Screen
name={SCREEN.PageNavigation}
component={PageNavigation}
options={{ headerShown: false }}
/>
)}
</StackNav.Navigator>
);
};
Here I am checking with keychain.
If the keychain is coming, it should go to the dashboard without showing the login screen, otherwise it should redirect to the login screen, but it doesn't work.
The application freezes.
how do i solve this

sending parameter between 2 screens

React-native noob here,
I have 2 screens. Users fill in their name on a screen called InputName and then when they click a button they go to HomeScreen and should see their name on that screen. I am using React Navigation to navigate between screens. The code on InputName Screen looks as follows:
const InputName = ({ navigation }) => {
const [username, setUsername] = useState('');
const handleName = async () => {
if (!username.trim()) {
alert('Please fill in a name')
} else {
navigation.navigate("CommonScreens", {
screen: "Home",
state: {
username: username,
},
});
console.log(username)
AsyncStorage.setItem("hasSeenWelcome", "true");
}
}
Once the user presses a button, handleName is executed.
On the InputName Screen users fill in their name in a TextInput that has the following code:
<TextInput
style={style}
placeholder="Fill in your name here"
onChangeText={text => setUsername(text)}
/>
The screen where I'm trying to retrieve this username is HomeScreen. The code I'm using to retrieve it is as follows:
const HomeScreen = ({ navigation, route }) => {
let username = route.params?.username;
{console.log(username)}
As you can see I have console.log on both InputName screen and HomeScreen. In InputName I get the value that I filled in and in HomeScreen it comes back as undefined.
EDIT: Navigation structure
function WelcomeStackScreen() {
return (
<WelcomeStack.Navigator
initialRouteName="Welcome"
screenOptions={{ headerShown: false }}
>
<WelcomeStack.Screen name="Welcome" component={WelcomeScreen} />
<WelcomeStack.Screen
name="ChooseDepartment"
component={ChooseDepartment}
/>
<WelcomeStack.Screen
name="InputName"
component={InputName}
/>
</WelcomeStack.Navigator>
);
}
function CommonScreensStackScreen() {
return (
<CommonScreensStack.Navigator screenOptions={{ headerShown: false }}>
<CommonScreensStack.Screen name="HomeTab" component={HomeTabScreen} />
<CommonScreensStack.Screen name="QuizScreen" component={DilemmasScreen} />
<CommonScreensStack.Screen name="UitlegScreen" component={UitlegScreen} />
<CommonScreensStack.Screen
name="PrivacyPolicy"
component={PrivacyPolicy}
/>
<CommonScreensStack.Screen
name="AlgemeneVoorwaarden"
component={AlgemeneVoorwaarden}
/>
<CommonScreensStack.Screen
name="ChooseDepartment"
component={ChooseDepartment}
/>
<CommonScreensStack.Screen
name="Toelichting"
component={ToelichtingScreen}
/>
<CommonScreensStack.Screen name="Results" component={ResultScreen} />
</CommonScreensStack.Navigator>
);
}
<HomeTab.Navigator
initialRouteName="Home"
>
<HomeTab.Screen name="Results" component={ResultsScreen} />
<HomeTab.Screen name="Home" component={HomeScreen} />
<HomeTab.Screen name="Settings" component={SettingsScreen} />
</HomeTab.Navigator>
Any help/pointers would be greatly appreciated!
Try to change your navigation.navigate() as below:
navigation.navigate("Home", {
username: username,
});
Use params field instead of state to pass parameters.
navigation.navigate('CommonScreens', {
screen: 'Home',
params: { username: username },
});
See Passing params to nested navigators.
Hi thanks for all your answers.
I have no idea why none of them or my own solution didn't work but I managed to get around it by using AsyncStorage.
AsyncStorage.setItem("username", username);
I'm then able to retreive the username in other components/screens like this:
const [username, setUsername] = useState("");
useEffect(() => {
const getUsername = async () => {
const username = await AsyncStorage.getItem("username");
setUsername(username);
};
getUsername();
  }, []);

React-Native navigation doesn't recognize params

I have a typescript react-native application. I have used navigation with some sucess but in this case, no matter what I do, the id, filename, and file are all undefined.
Here is the code with the issue. I know according to react-native navigation doing what I'm doing with the file isn't necessary great coding practice, but this is just displaying a file, so it's not a huge deal. (I am storing the filename and id in a sqlite database). I added the useState hoping that the file gets passed or change that it can change the state.
export type Props = {
navigation: PropTypes.func.isRequired;
id:PropTypes.int.isRequired;
filename:Protypes.string.isRequired;
file:{
name: PropTypes.string.isRequired;
uri: PropTypes.path.isRequired;
type: PropTypes.mime.isRequired};
};
const FileViewScreen: React.FC<Props> = ({navigation,id,filename,file}) => {
console.log("File View Screen?")
console.log("currentFile");
console.log(id)
console.log(currentFile)
console.log(filename)
console.log(file)
const [currentFile,setCurrentFile] = useState(file);
Here is where the user gets routed to the FileScreen. Here I was testing to see if any id is passed, I'm aware that the id needs changed to the id and not 1 but this was testing.
const HomeScreen: React.FC<Props> = ({navigation}) => {
const [loading, setLoading] = useState(false);
const [file, setFile] = useState({});
const [files, setFiles] = useState([]);
const downloadFile = async () => {
try {
...
const newEntry = {
name: 'ImageFileName' + Math.random().toString(),
uri: result.path,
type: result.mime,
};
const res = await addFile(result.path);
console.log(res)
navigation.navigate('FileView', { id:1,filename:res,file:newEntry });
} catch (error) {
console.log('downloadFile error', error);
}
};
return (
<View style={styles}>
<Text>Welcome Home</Text>
{loading && <ActivityIndicator size={'large'} color="#000" />}
{!loading && (
<>
<Button
title="Start Recording"
onPress={downloadFile}
/>
Here is the addFile function. I don't think this matters but I've been wrong before. Here
export const addFile = file_path => {
db.transaction(txn => {
console.log("db transaction")
console.log(file_path)
const response = txn.executeSql(
'INSERT INTO files(file_path,uploaded) VALUES (' +
file_path +
',' +
false +
')',
(sqlTxn, res) => {
console.log("adding")
console.log(`${file_path} video added successfully`);
return file_path;
},
error => {
console.log('error on adding file ' + error.message);
return 0;
},
);
});
console.log(resopnse)
};
In my app.js (i do have a working register and, login, home screen. Right now this is the only time I have an issue.
<NavigationContainer>
<Stack.Navigator initialRouteName={initalRoute}>
<Stack.Screen name="Login">
{props => (
<LoginScreen {...props} setToken={setUserToken} setUser={setUser} />
)}
</Stack.Screen>
<Stack.Screen name="Home">
{props => (
<HomeScreen {...props}/>
)}
</Stack.Screen>
<Stack.Screen name="Register" component={RegisterScreen} />
<Stack.Screen name="FileView">
{props =>(
<FileViewScreen {...props} />
)}
</Stack.Screen>
</NavigationContainer>
Things that I've tried.
I tried to change the RecordingView in app.js to make sure it's specifically passing props
I've changed props to be only an id, only a filename, or only the newentry.
I've tried to set the state as the file in case it gets passed later.
Things that I haven't tried
I haven't put this in a button. That's the main thing I haven't been able to find if navigation.navigate only works on a push event. I don't see any documentation stating that.
If your FileViewScreen is a child component of some parent view then id,filename,file will be available from component props object. If instead you navigate to FileViewScreen from another screen then id,filename,file will be part of route prop.
To account for both use cases you could so something like this
const FileViewScreen: React.FC<Props> = (props) {
// try extracting props from root prop object
let { id,filename,file } = props;
// if navigation route params are available,
// then extract props from route.params instead
// you could also check if id, filename, file etc are null
// before extracting from route.params
const { route } = props;
if (route && route.params) {
({ id,filename,file } = route.params);
}
...
}

Flatlist inside tab navigator is scrolling to top on state change in react native

Here you can see the gif
Here is my whole Navigator functional component. I'm trying to implement two tabs using Tab Navigator. One to display the cryptos and the other to display the forex data.
The problem is, when I try to load more data on reaching the flatlist's end, the flatlist is scrolling to the top since I'm making a state change [page+1].
const Navigator = () => {
const Tab = createMaterialTopTabNavigator();
const renderItems = ({ item }) => (
<Text>{item.name}<Text>
);
const fetchMarketData = async () => {
console.log("Fetching");
const marketData = await getCryptoMarketData({ page });
if (marketData != "Network Error") {
const ids = data.map((item) => item.id);
let newData = marketData.filter((item) => !ids.includes(item.id));
setData([...data, ...newData]);
setFetching(false);
} else {
setFetching(false);
Alert.alert(marketData, "Sorry for the inconvenience");
}
};
useEffect(() => {
setFetching(true);
const data = async () => {
await fetchMarketData();
};
}, [page]);
const handleLoadMore = async () => {
setFetching(true);
setPage((page) => page + 1);
};
const ScreenA = () => (
<FlatList
data={data}
style={{ backgroundColor: "white" }}
keyExtractor={(item) => item.id}
renderItem={renderItems}
scrollEventThrottle={16}
onEndReached={handleLoadMore}
onEndReachedThreshold={0}
/>
);
return (
<Tab.Navigator
screenOptions={({ route }) => screenOptions(route)}
keyboardDismissMode="auto"
>
<Tab.Screen name="Crypto" component={ScreenA} />
<Tab.Screen name="Forex" component={ScreenC} />
</Tab.Navigator>
);
};
export default Navigator;
OnEndReached is firing the handleLoadMore function and after the state change on data, the Flatlist is scrolling to the top.
1st reason
you have typo in "fetchMarketData", how exactly u get "newData" because i cant see it anywhere, maybe it should be "marketData" if not then u adding SAME old data PLUS undefined[...data, ...undefined]
2nd reason
reason why is that u call setPage(page + 1) and then "fetchMarketData" this is bad why ? because setState is async and it can be changed instant or after 5 secound, so u dont know when its changed and this is why we have hooks, you can use "useEffect" to handle this
change your "handleLoadMore" for example like this
const handleLoadMore = () => {
setPage(page + 1);
};
add useEffect hook that runs when "page" state changes
React.useEffect(() => {
(async() => {
setFetching(true)
const marketData = await getCryptoMarketData({ page });
if (marketData != "Network Error") {
setData([...data, ...marketData]);
} else {
Alert.alert(marketData, "Sorry for the inconvenience");
}
setFetching(false)
})()
}, [page])

Block/Disable tabs in TabNavigator - react-navigation

I have a TabNavigator as shown in the picture.
Header Image
I am using TabNavigator for creating these tabs as below.
const Tab_Navigator = TabNavigator({
First:{
screen: First,
},
Second:{
screen: Second,
},
Third:{
screen: Third,
},
Now I want to block/disable "Second" and "Third" tabs. It should be visible but one shouldn't able to navigate to them.
I tried blocking these tabs as shown here but I guess I am missing something. My try:
Tab_Navigator.router.getStateForAction = (action, state) => {
if( action.type === NavigationActions.navigate({ routeName: "Second"}) ||
action.type === NavigationActions.navigate({ routeName: "Third"}))
{
return null;
}
return Byte.router.getStateForAction(action, state);
};
In this case, the action.type = "Navigation/NAVIGATE" and action.routeName is the name of your tab. It is just a little different from the ReactNavigation Routers example. The following should work:
const defaultGetStateForAction = Tab_Navigator.router.getStateForAction;
Tab_Navigator.router.getStateForAction = (action, state) => {
if ((action.type === NavigationActions.NAVIGATE) &&
(action.routeName === "Second" || action.routeName === "Third") {
return null;
}
return defaultGetStateForAction(action, state);
};
EDIT: Here is an image of the the Chrome Debugger stopped at a breakpoint in a very similar piece of code(tab names are different), but it shows the values of the "action" object being passed into this function.
For Version 5.x+ there's a new way to do it.
<Tabs.Screen
name="Chat"
component={Chat}
listeners={{
tabPress: e => {
// Prevent default action
e.preventDefault();
},
}}
/>
Here's the reference link to the docs: https://reactnavigation.org/docs/navigation-events/
You have to use tabBarOnPress propert under defaultNavigationOptions, and check the route name to which you dont want to navigate return them null else return defaultHandler. Please check the following code
const Tab_Navigator = createBottomTabNavigator({
First:{
screen: First,
},
Second:{
screen: Second,
},
Third:{
screen: Third,
}
}, defaultNavigationOptions: ({ navigation }) => ({
tabBarOnPress: ({ navigation, defaultHandler }) => {
if (
navigation.state.routeName === "Second" ||
navigation.state.routeName === "Third"
) {
return null;
}
defaultHandler();
},})
This is my solution to disable and completely take control over the tabBarButton in React Navigation 6.
// Defining the disabled tabBarButton component
//
const DisabledTabBarButton = ({ style, ...props }: BottomTabBarButtonProps) => (
<Pressable disabled style={[{ opacity: 0.2 }, style]} {...props} />
)
const Tab = createBottomTabNavigator()
const Router = () => (
<Tab.Navigator>
<Tab.Screen name="Screen 1" />
<Tab.Screen name="Screen 2"
options={{
// Applying the disabled button
tabBarButton: DisabledTabBarButton,
}}
/>
</Tab.Navigator>
export default Router