sending parameter between 2 screens - react-native

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();
  }, []);

Related

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);
}
...
}

Using 2 Drawer Navigator in one project

I have 2 DrawerNavigators, one of them(PaymentHistoryDrawer) is inside the BottomTabNavigator. I bind them to two buttons - the 1st for the HamburgerMenu , the 2nd for the FilterButton inside Payment History.
export const DrawerNavigator = () => (
<Drawer.Navigator drawerContent={(props) => <Sidebar {...props} />}>
<Drawer.Screen name={SCREEN_ROOT} component={BottomTabNavigator} />
</Drawer.Navigator>
);
export const DrawerPaymentHistoryNavigator = () => (
<PaymentHistoryDrawer.Navigator
drawerContent={PaymentHistorySidebar}
drawerPosition="right"
drawerStyle={{
zIndex: 0,
width: 300
}}
drawerType="front">
<PaymentHistoryDrawer.Screen
name={SCREEN_PAYMENT_HISTORY_DRAWER}
component={HistoryScreen}
options={{ title: 'My home' }}
/>
</PaymentHistoryDrawer.Navigator>
);
I initialize them differently and bind them differently as "Drawer" for Hamburger Menu, "PaymentHistoryDrawer" for Filters. But anyway, when I click, let's say, on the Hamburger Menu button in the Bottom tab, where at the same time there is FilterButton, that calls PaymentHistoryDrawer, the PaymentHistory comes out. Why are they related to each other? how to untie?
//this code is the beginning of the above written code
type DrawerParamList = {
[SCREEN_ROOT]: undefined;
};
type PaymentHistoryDrawerParamList = {
[SCREEN_PAYMENT_HISTORY_DRAWER]: undefined;
};
export type RootScreenNavigationProp = DrawerNavigationProp<
DrawerParamList,
'SCREEN_ROOT'
>;
export type PaymentHistoryScreenNavigationProp = DrawerNavigationProp<
PaymentHistoryDrawerParamList,
'SCREEN_PAYMENT_HISTORY_DRAWER'
>;
const Drawer = createDrawerNavigator<DrawerParamList>();
const PaymentHistoryDrawer =
createDrawerNavigator<PaymentHistoryDrawerParamList>();
Binding ToggleDrawer to the FilterButton
export const FilterButton = () => {
const { toggleDrawer } =
useNavigation<PaymentHistoryScreenNavigationProp>();
return (
<FilterContainer onPress={toggleDrawer}>
<Image source={FilterIcon} />
<FilterText>Фильтры</FilterText>
</FilterContainer>
);
};
Binding ToggleDrawer to the HamburgerMenuButton
const _Menu = () => {
const { toggleDrawer } = useNavigation<RootScreenNavigationProp>();
return <Icon onPress={toggleDrawer} />;
};
export const Menu = memo(_Menu);
Additional question : ToggleDrawer is UseNavigation tool. Can it take any additional arguments? I read 2 times documentation, but didn't find anything about arguments or props. Thanks!
The answer is still secret...
I hope u're don't using drawers like this

How to navigate in const function [react native]

I have 2 questions:
How do I add props to this function in order to navigate
const usersRef = firestore().collection('Users');
//const signIn = React.useContext(AuthContext);
const { signIn } = React.useContext(AuthContext);
const CreateUser = async (email, password) => {
try {
let response = await auth().createUserWithEmailAndPassword(email, password)
if (response) {
console.log( "?", response)
}
} catch (e) {
console.error(e.message)
}
usersRef.add({
// Name: this.state.Name,
Email: email
})
navigation.navigate("SignIn")
}
export function RegisterScreen({navigation}) {
const [email, setEmail] = React.useState('');
const [password, setPassword] = React.useState('');
const { register } = React.useContext(AuthContext);
const {container, txtInput} = styles;
return (
<View style={container}>
<Text>Reg</Text>
<TextInput
placeholder="email"
value={email}
onChangeText={setEmail}
style={txtInput}
/>
<TextInput
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
style={txtInput}
/>
<Button title="Register" onPress={() => {
// navigation.navigate('signIn')
//register(email,password)
CreateUser(email,password)
}} />
</View>
);
}
I want to make it logged in after the registration is complete, so far as my understanding I need to get it to SignIn function of my AuthContext
I have const signIn = React.useContext(AuthContext);
and I try to do signIn(email,password) after or before the navigation.
please try this, where did you put it CreateUser
const { signIn } = React.useContext(AuthContext);
const data = {
username: state.username,
password: state.password
}
signIn(data)
App.js
it will switch automatically
{userToken ?
<RootStack.Navigator>
<RootStack.screen />
</RootStack.Navigator> :
<RootStack.Navigator>
<RootStack.screen />
</RootStack.Navigator>}
You can change routes by doing this
const CreateUser = async (data,navigation) => {
try {
let response = await auth().createUserWithEmailAndPassword(data.email, data.password)
if (response) {
console.log("test")
}
} catch (e) {
console.error(e.message)
}
usersRef.add({
// Name: this.state.Name,
Email: email
})
navigation.navigate("SignIn")
}
So I fixed it by changing registration to class then I was able to use navigation and using constructor props.
The only problem is that the auth context doesnt seem to support class. so I navigate after registration to signIn that remained as function so there Im able to use the authContext SignIn
If someone want to help me and tell me if its possible to manage it on class instead of function it would be greatful.

Navigation Error when navigating to Home page from Firebase registration

I'm following this guide: https://www.freecodecamp.org/news/react-native-firebase-tutorial/ in attempt to learn how to use firebase, and even though I've followed the code very closely, I'm receiving a NAVIGATION error:
The action 'NAVIGATE' with payload {"name":"Home","params":{"user":{"id":"AWSKEmmUsua5koR1V3x5bapc3Eq2","email":"tk#gmail.com","fullName":"t"}}} was not handled by any navigator.
Do you have a screen named 'Home'?
I do however, have a screen named Home. App.js:
import Home from './src/Home';
import Login from './src/Login/Login';
import Registration from './src/Registration/Registration';
const Stack = createStackNavigator();
export default function App() {
const [loading, setLoading] = useState(true);
const [user, setUser] = useState(null);
return (
<NavigationContainer>
<Stack.Navigator>
{ user ? (
<Stack.Screen name="Home">
{props => <Home {...props} extraData={user} />}
</Stack.Screen>
) : (
<>
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Registration" component={Registration} />
</>
)}
</Stack.Navigator>
</NavigationContainer>
);
}
When I use the Registration form to register a new user and Navigate to the Home page is when I get the error. Registration.js:
import { firebase } from '../firebase/config';
export default function Registration({ navigation }) {
const [fullName, setFullName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const onFooterLinkPress = () => {
navigation.navigate('Login');
}
const onRegisterPress = () => {
if (password !== confirmPassword) {
alert("Passwords do not match!");
return
}
// This works. However, navigation does not for some reason
firebase.auth()
.createUserWithEmailAndPassword(email, password)
.then((response) => {
const uid = response.user.uid
const data = {
id: uid,
email,
fullName
}
const usersRef = firebase.firestore().collection("users");
usersRef.doc(uid).set(data).then(() => {
// This is where the navigation error lies. It has nothing to do with the component
// This error happened even when I created a new plain Home component
navigation.navigate("Home", { user: data})
})
.catch((error) => alert(error))
})
.catch((error) => alert(error))
}
return (
....Input Forms
<TouchableOpacity
style={styles.loginButton}
onPress={() => onRegisterPress()}
>
<Text style={styles.buttonTitle}>Create Account</Text>
</TouchableOpacity>
I have used React Navigation before and haven't run into this issue. I am not using nested navigators and cannot see where the issue lies. Thank you for reading.
Adding to Göksel Pırnal answers:
At first, suppose there is no user. So We are in Registration Screen. At that stage, our navigator doesn’t even know whether there is any “Home” Screen. At this stage, our navigator only knows 2 screens: “Login” and “Registration” screens.
You need to notify our app.js whether anyone registered in the Registration screen or not. After that our app.js should change the value of 'user' in [user,setUser].
In your, App.js put this lines of code:
const [initializing,setInitializing]=useState(true)
useEffect(()=>{
const subscriber=firebase.auth().onAuthStateChanged((user)=>{
setUser(user)
setInitializing(false)
})
return subscriber
},[])
if (initializing) return null //Here you may use an Activity indicator
Then after rerendering our navigator will see the value of “user” has changed and it should navigate to the Home screen.
And guess what! You do not need to navigate manually from Registration Screen as you already put a condition in App.js ( in return () ).
You have a problem where you check the user value in App.js. After the registration is done, you did not assign the state in the App.js page and it will always be null. The Home page will not be added to the stack because the user value is null. That's why you got the error.
Solution: You need to notify App.js after registration.

Sending params with navigation.goBack in react navigation

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();