I'm very new to React Native. I've got the following page which loads dates and results from a database, all displaying ok:
import React, { useEffect, useState } from "react";
import {
Text,
FlatList,
StyleSheet,
TouchableOpacity,
Platform,
} from "react-native";
import AsyncStorage from "#react-native-community/async-storage";
import Colors from "../../../res/Colors/Colors";
import { s, scale } from "react-native-size-matters";
import Axios from "axios";
import moment from "moment";
import { SafeAreaView} from "react-navigation";
export default function HistoryScreen({ navigation }) {
const [userDetails, setUserDetails] = useState(null);
const [loading, setLoading] = useState(true);
const [Star, setStar] = useState(null);
const [people, setPeople] = useState(null);
useEffect(() => {
_getHistory();
}, []);
const expandMonth = (month,year) => {
console.log(month);
console.log(year);
}
const _getHistory = async () => {
let star = await AsyncStorage.getItem("star");
star = JSON.parse(star);
setStar(star);
var formData = new FormData();
formData.append("StarID", star.StarID);
Axios({
method: "post",
url: "**API URL**",
data: formData,
//headers: { "Content-Type": "multipart/form-data" },
})
.then(async (response) => {
var responseBody = response.data.detail;
setPeople(responseBody);
setLoading(false);
})
.catch((error) => {
console.log({ error });
});
};
if (loading)
return (
<SafeAreaView>
</SafeAreaView>
);
return (
<SafeAreaView
style={{
flex: 1,
backgroundColor: Colors.lightGrey,
paddingTop: Platform.OS === "android" ? 40 : 40,
justifyContent: "space-evenly",
}}
>
<Text style={styles.headerText}>History</Text>
<FlatList
//keyExtractor={(item) => item.id}
data={people}
renderItem={({ item }) => (
<TouchableOpacity onPress={() => {
expandMonth(item.Month,item.Year)
navigation.navigate("HistoryMonthScreen", {
Month: item.Month,
Year: item.Year
});
}}
style={styles.month}>
<Text style={styles.monthname}>{moment(item.Month, 'M').format('MMMM')} {item.Year}</Text>
<Text style={styles.monthcount}>{item.Count} Bookings</Text>
</TouchableOpacity>
)}
/>
</SafeAreaView>
);
}
However when clicking the TouchableOpacity object it doesn't navigate to the next page passing the year and month variables. I assume I need to call some form of navigation to it, however everything I have tried has resulted in error after error.
For React Navigation 5 you have to use the route prop that is passed in alongside navigation for whatever screen you're navigating to.
Put the below code into HistoryMonthScreen.js
const HistoryMonthScreen = ({route, navigation}) => {
console.log(route.params.Month, route.params.Year);
return <View />
}
Edit: For context:
I'm going off what you have here
navigation.navigate("HistoryMonthScreen", {
Month: item.Month,
Year: item.Year
});
So I'm assuming you have some React Navigator somewhere that looks like this:
const AppNavigator5 = () => {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="HistoryScreen"
screenOptions={{
headerShown: false,
animationEnabled: false,
}}>
<Stack.Screen
name="HistoryScreen"
component={HistoryScreen}
/>
<Stack.Screen
name="HistoryMonthScreen"
component={HistoryMonthScreen}
/>
<Stack.Screen name="Egg" component={Egg} />
</Stack.Navigator>
</NavigationContainer>
);
};
You put the code with route and navigation inside that HistoryMonthScreen component. You can pass different params into route.params every time, you don't have to change HistoryMonthScreen.
Related
So, I want to consume a context TaskContext in just two components. There is a way to do this, without wrapping my whole application with the TaskContext ? Furthemore, what would be the best way to handle multiples context ?
I've try wrapping a single component with the context by creating another function that returns a new component. For example:
const HomePageContext = (props) => {
return (
<TaskProvider>
<HomePage {...props}/>
</TaskProvider>
)
}
But with this approach, the data update in AddTask screen doesn't appear in HomePage.
(Disclaimer: I just want to create a homePage that contains a list of tasks. This tasks are created in another screen, AddTasks. When the task is created, the homepage should be update to display the new list, with all other tasks and the new one. Thus, i dont know what is the best approach to do this.)
My code is here. It's only for test and practice purpose.
import React, { createContext, useReducer, useContext} from 'react'
import { View, FlatList, Text, Button } from 'react-native'
import { createNativeStackNavigator } from '#react-navigation/native-stack'
import { createDrawerNavigator } from '#react-navigation/drawer'
import { NavigationContainer } from '#react-navigation/native'
const TaskContext = createContext({})
const initialState = [{ name: `Task name ${Math.floor(Math.random() * 100)}`, id: Math.floor(Math.random() * 100) }]
//contexto provider
const TaskProvider = ({ children }) => {
const reducer = (state, action) => {
switch(action.type) {
case 'addTask':
console.log([...state, action.payload])
return [...state, action.payload]
default:
return state
}
}
const [tasks, dispatch] = useReducer(reducer, initialState)
return (
<TaskContext.Provider value={ { tasks, dispatch } }>
{ children }
</TaskContext.Provider>
)
}
const HomePage = ({ navigation }) => {
const { tasks, dispatch } = useContext(TaskContext)
return (
<View>
<Text>Home</Text>
<FlatList
data={tasks}
keyExtractor={item => `${item.id}`}
renderItem={({ item }) => <Text>{item.id} - {item.name}</Text>}
/>
<Button
title='Add Task'
onPress={() => navigation.navigate('AddTask')}
/>
</View>
)
}
const Info = () => {
return (
<View>
<Text>Info</Text>
</View>
)
}
const AddTaskPage = () => {
const { dispatch } = useContext(TaskContext)
const newTask = {id: Math.floor(Math.random() * 100), name: `Task name ${Math.floor(Math.random() * 100)}`}
return (
<View>
<Text>addTaskPage</Text>
<Button
title='AddTask'
onPress={() => dispatch({
type: 'addTask',
payload: newTask
})}
/>
</View>
)
}
// createNavigators
const Stack = createNativeStackNavigator()
const Drawer = createDrawerNavigator()
const DrawerNavigation = () =>{
return (
<Drawer.Navigator>
<Drawer.Screen name='Home' component={HomePage}/>
<Drawer.Screen name='Info' component={Info}/>
</Drawer.Navigator>
)
}
// App component
export default App = () => {
return (
<TaskProvider>
<NavigationContainer>
<Stack.Navigator initialRouteName='Menu'>
<Stack.Screen name='Menu' options={{ headerShown: false }} component={DrawerNavigation} />
<Stack.Screen name='AddTask' component={AddTaskPage} />
</Stack.Navigator>
</NavigationContainer>
</TaskProvider>
)
}
i want to show a screen depending on a state.
So when I click on the bottom left tab, if there is a valid user, I want to be redirected to the UserProfile-screen, else redirect to the LoginScreen.
The react-native navigation confuses me, i just cant see whats wrong.
So in LoginRoutes.tsx I just try to change this behaviour by using true / false
Thanks in Advance.
What I have so far:
BottomTabBar.tsx:
export const BottomTabNavigator = () => {
const colorScheme = useColorScheme();
return (
<Tab.Navigator
screenOptions={{
tabBarShowLabel: true,
tabBarStyle: {
backgroundColor: "#292929",
borderTopWidth:0
},
tabBarInactiveTintColor: "#919191",
tabBarInactiveBackgroundColor: "#292929",
tabBarActiveTintColor: Colors[colorScheme].text,
headerShown: false,
}}
>
<Tab.Screen
name="LoginRoutes"
component={LoginRoutes}
options={{ title: "Login", headerShown: false}}
/>
<Tab.Screen
name="SettingsTabScreen"
component={SettingsTabScreen}
options={{ headerShown: false,title:"test" }}
/>
</Tab.Navigator>
);
};
LoginRoutes.tsx
import * as React from "react";
import { ActivityIndicator, View, Text } from "react-native";
import AsyncStorage from "#react-native-async-storage/async-storage";
import { createStackNavigator } from "#react-navigation/stack";
import NavigationContainer from "./UserProfileStack";
import LoginScreen from "../Screens/LoginScreen";
import UserProfile from "../components/UserProfile";
import Colors from "../constants/Colors";
import { UserProfileInfo } from "../constants/Types";
function LoginRoutes({ navigation }: { navigation: any }) {
const [loading, setLoading] = React.useState(true);
const [user, setUser] = React.useState(null);
const Stack = createStackNavigator();
React.useEffect(() => {
// check if the user is logged in or not
AsyncStorage.getItem("user")
.then((userString) => {
if (userString) {
console.log("-----------EXISTING------------");
console.log(JSON.parse(userString).id);
setUser(JSON.parse(userString));
} else {
console.log("not logged in, showing LoginPage");
}
setLoading(false);
})
.catch((err) => {
console.log(err);
setLoading(false);
});
}, []);
if (loading) {
return (
<View
style={{
height: "100%",
justifyContent: "center",
backgroundColor: Colors.dark.background,
}}
>
<ActivityIndicator
size="large"
style={{ backgroundColor: Colors.dark.background }}
/>
<Text
style={{
color: Colors.dark.text,
marginTop: 10,
alignSelf: "center",
}}
>
retrieving userdata...
</Text>
</View>
);
}
return (
<NavigationContainer>
<Stack.Navigator>
{true ? (
<Stack.Screen name="LoginScreen" component={LoginScreen} />
) : (
<Stack.Screen name="UserProfile" component={UserProfile} />
)}
</Stack.Navigator>
</NavigationContainer>
);
}
export default LoginRoutes;
The stackNavigator:
import { createStackNavigator } from "react-navigation-stack";
import { createAppContainer } from "react-navigation";
import LoginScreen from "../Screens/LoginScreen";
import UserProfile from "../components/UserProfile";
import { UserProfileInfo } from "../constants/Types";
import { StackNavigationProp } from '#react-navigation/stack';
export type UserProfileStackParams = {
LoginScreen: undefined,
UserProfile: { profileInfo: UserProfileInfo };
};
const screens = {
LoginScreen: {
screen: LoginScreen,
navigationOptions: {headerShown: false,gestureEnabled:false},
},
UserProfile: {
screen: UserProfile,
navigationOptions: {headerShown: false,gestureEnabled:false},
},
};
// home stack navigator screens
const UserProfileStack = createStackNavigator(screens);
export default createAppContainer(UserProfileStack);
UserProfile.tsx
type Props = {
navigation: StackNavigationProp<UserProfileStackParams, "UserProfile">
loggedInUser: {}
};
const DATA = [
{
// contains valid data
},
];
export const UserProfile: React.FC<Props> = ({ navigation}) => {
const [steamID, setSteamID] = React.useState({ id: null, watchLists: null });
const [profileInfo, setProfileInfo] = React.useState<UserProfileInfo>(null);
const [loading, setLoading] = React.useState(true);
React.useEffect(() => {
// check if the user is logged in or not
AsyncStorage.getItem("user")
.then((userString) => {
if (userString) {
console.log("logged in.");
setSteamID(JSON.parse(userString));
fetchUserProfile(steamID.id).then((response) => {
setProfileInfo(response);
});
} else {
console.log("not logged in, showing LoginPage");
}
setLoading(false);
})
.catch((err) => {
console.log(err);
setLoading(false);
});
}, []);
return (
<>
<Button
title="btn"
onPress={() => {
navigation.navigate("LoginScreen")
}}
></Button>
export default UserProfile;
Inside the BottomTabNavigator you can check if the user is logged in. You can get the user in the same way you're getting it in UserProfile.tsx file.
export const BottomTabNavigator = () => {
const [user, setUser] = React.useState(null);
React.useEffect(() => {
// check for user
}, []);
return (
<Tab.Navigator
screenOptions={{ ... }}
>
<Tab.Screen
name="LoginRoutes"
component={user ? UserScreen : LoginScreen}
/>
</Tab.Navigator>
);
};
Alternatively, you can look into getting the user through Context Provider so that you don't have to check the storage every time you want to see if the user is logged in.
Read more about that here:
How To Manage User State with React Context
React Context
How to convert Fetch to Axios and Class component to Functional component?
I want to learn to implement Infinite-Scrolling using functional component and axios in React native, but it is difficult to apply because the reference document is composed of class component and fetch.
import React from 'react';
import {
View,
Image,
Text,
FlatList, // here
} from 'react-native';
export default class App extends React.Component {
state = {
data: [],
page: 1 // here
}
_renderItem = ({item}) => (
<View style={{borderBottomWidth:1, marginTop: 20}}>
<Image source={{ uri: item.url }} style={{ height: 200}} />
<Text>{item.title}</Text>
<Text>{item.id}</Text>
</View>
);
_getData = () => {
const url = 'https://jsonplaceholder.typicode.com/photos?_limit=10&_page=' + this.state.page;
fetch(url)
.then(r => r.json())
.then(data => {
this.setState({
data: this.state.data.concat(data),
page: this.state.page + 1
})
});
}
componentDidMount() {
this._getData();
}
// here
_handleLoadMore = () => {
this._getData();
}
render() {
return (
<FlatList
data={this.state.data}
renderItem={this._renderItem}
keyExtractor={(item, index) => item.id}
onEndReached={this._handleLoadMore}
onEndReachedThreshold={1}
/>
);
}
}
When converting from a class to a function component, there are a few steps which are relevant here:
replace lifecycle events like componentDidMount with useEffect.
replace component state with one or many useState hooks.
convert class methods to plain functions.
remove all references to this.
delete render() and just return the JSX directly.
The methods _renderItem, _getData, and _handleLoadMore are basically unchanged. They just become const variables instead of class properties.
Here's the straight conversion from class to function component:
import React, { useEffect, useState } from 'react';
import {
View,
Image,
Text,
FlatList,
} from 'react-native';
export default function App() {
const [page, setPage] = useState(1);
const [data, setData] = useState([]);
const _renderItem = ({ item }) => (
<View style={{ borderBottomWidth: 1, marginTop: 20 }}>
<Image source={{ uri: item.url }} style={{ height: 200 }} />
<Text>{item.title}</Text>
<Text>{item.id}</Text>
</View>
);
const _getData = () => {
const url =
'https://jsonplaceholder.typicode.com/photos?_limit=10&_page=' + page;
fetch(url)
.then((r) => r.json())
.then((data) => {
setData(data.concat(data));
setPage(page + 1);
});
};
const _handleLoadMore = () => {
_getData();
};
// useEffect with an empty dependency array replaces componentDidMount()
useEffect(() => _getData(), []);
return (
<FlatList
data={data}
renderItem={_renderItem}
keyExtractor={(item, index) => item.id}
onEndReached={_handleLoadMore}
onEndReachedThreshold={1}
/>
);
}
Here it is with axios and with a few other improvements. I noticed that the end reached function was being called upon reaching the end of the initial zero-length list causing the first page to be fetched twice. So actually the componentDidMount is not needed. I changed from .then() to async/await, but that doesn't matter.
import React, { useEffect, useState } from 'react';
import { View, Image, Text, FlatList } from 'react-native';
import axios from 'axios';
export default function App() {
const [page, setPage] = useState(1);
const [data, setData] = useState([]);
const _renderItem = ({ item }) => (
<View style={{ borderBottomWidth: 1, marginTop: 20 }}>
<Image source={{ uri: item.url }} style={{ height: 200 }} />
<Text>{item.title}</Text>
<Text>{item.id}</Text>
</View>
);
const _getData = async () => {
const url =
'https://jsonplaceholder.typicode.com/photos?_limit=10&_page=' + page;
const res = await axios.get(url);
setData(data.concat(res.data));
setPage(page + 1);
};
const _handleLoadMore = () => {
_getData();
};
// useEffect with an empty dependency array replaces componentDidMount()
useEffect(() => {
// put async functions inside curly braces to that you aren't returing the Promise
_getData();
}, []);
return (
<FlatList
data={data}
renderItem={_renderItem}
keyExtractor={(item, index) => item.id}
onEndReached={_handleLoadMore}
onEndReachedThreshold={1}
/>
);
}
Expo Link -- It works on my device, but the infinite scroll doesn't seem to work in the web preview.
There are additional more "advanced" improvements that you can make:
set state with a callback of previous state to ensure that values are always correct. setPage(current => current + 1) setData(current => current.concat(res.data))
memoization with useCallback so that functions like _renderItem maintain a constant reference across re-renders.
exhaustive useEffect dependencies (requires memoization).
I have created a picker component to get data from API and display it(ProductNames) in the picker list, to select one of them. Then I able to get picker item value as a productID. After that, I use this child component in the parent component, which indicates a form to submit. Now I can see the picker in the parent component(form) and I can select one of them.
My main purpose is to open an input field through the picker. So now I need to pass the item.value ( as a state 'selectedValue') to parent component to submit. How can I pass this child state to parent state?
this is my child component:
import React, {useEffect, useState} from 'react';
import {View, StyleSheet} from 'react-native';
import {Picker} from '#react-native-picker/picker';
import AsyncStorage from '#react-native-community/async-storage';
const ProductPicker = () => {
const [selectedValue, setSelectedValue] = useState('');
const [productDetails, setproductDetails] = useState([]);
console.log('selected value', selectedValue);
useEffect(() => {
getProductList();
}, []);
const getProductList = async () => {
const token = await AsyncStorage.getItem('userToken');
fetch('http://10.0.2.2:3000/customer/get-all-products', {
method: 'post',
headers: {
'Content-Type': 'application/json',
'Authentication': `Bearer ${token}`,
},
})
.then((response) => response.json())
.then((json) => setproductDetails(json.data))
.catch((error) => console.error(error));
};
return (
<View style={styles.container}>
<Picker
selectedValue={selectedValue}
style={{height: 40, width: 150}}
onValueChange={(itemValue, itemIndex) => {
setSelectedValue(itemValue);
}}
>
{productDetails.map((item, index) => {
return (
<Picker.Item label={item.productName} value={item.productID} key={index}/>);
})}
</Picker>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 40,
alignItems: 'center',
},
});
export default ProductPicker;
and this is my parent component:
import * as React from 'react';
import {Button, View, Text, ScrollView, StyleSheet, Alert} from 'react-native';
import {Appbar} from 'react-native-paper';
import {TextInput, HelperText} from 'react-native-paper';
import {useEffect, useState} from 'react';
import AsyncStorage from '#react-native-community/async-storage';
import ProductPicker from './ProductPicker';
const ComplaintSubmission = ({navigation}) => {
const [productID , setproductID] = useState('');
const [description , setdescription] = useState('');
const [token, setToken] = useState('');
const saveToken = async () => {
const token = await AsyncStorage.getItem('userToken');
console.log('token from storage', token);
setToken(token);
}
useEffect(() => {
saveToken();
fetch("http://10.0.2.2:3000/customer/lodge-complaint", {
method: "post",
headers: {
'Content-Type': 'application/json',
'Authentication': `Bearer ${token}`
},
body: JSON.stringify({
description : description,
})
})
}, []);
const openAlert = () => {
Alert.alert(
"Complaint Successfully Submitted",
"We review it as soon as possible. Thank you for reaching for us!",
[{
text: "OK",
onPress : () => navigation.navigate("DashboardDrawer" ),
}]
);
}
return (
<ScrollView>
<Appbar.Header>
<Appbar.BackAction onPress={() => navigation.goBack()} />
<Appbar.Content title="Submit Complaint" />
<Appbar.Action icon="magnify" onPress={() => navigation.openDrawer()} />
</Appbar.Header>
<Text>Plese Fill the following</Text>
<View>
<ProductPicker />
<HelperText type="info">
Make sure select the correct Product
</HelperText>
</View>
<TextInput
style={styles.PIDstyle}
label="Description"
onChangeText = {(description) => setdescription(description)}
/>
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Text>This is submittion</Text>
<Button onPress={() => openAlert()} title="Submit Complaint" />
</View>
</ScrollView>
);
};
export default ComplaintSubmission;
const styles = StyleSheet.create({
PIDstyle: {
marginTop: 30,
marginLeft: 10,
marginRight: 10,
},
});
You don't want to pass the state from child to parent, you want to pass it from parent to child. You need to lift the state up to ComplaintSubmission and use props to control ProductPicker.
In ComplaintSubmission, call the ProductPicker with the current productId and a callback to update it.
<ProductPicker productID={productID} setproductID={setproductID} />
Now ProductPicker has these props. It should use them instead of the local state selectedValue which you can now delete.
const ProductPicker = ({productID, setproductID}) => {
<Picker
selectedValue={productID}
style={{height: 40, width: 150}}
onValueChange={(itemValue, itemIndex) => {
setproductID(itemValue);
}}
>
I'm developing an app where I have a screen called Add, other called Save, that I navigate between them using onPress={() => navigation.navigate('Save', { image })} inside a button, so I tried to do the same on another screen called profile that should navigate to the screen SaveProfileImage, but when I try this, I get the error TypeError: undefined is not an object (evaluating 'props.route.params') and I can't understand what can be so wrong in my codes:
Here is the Profile codes:
import React, { useState, useEffect } from 'react';
import { StyleSheet, View, Text, Image, FlatList, Button } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import firebase from 'firebase';
require('firebase/firestore');
import { connect } from 'react-redux';
function Profile({ navigation }, props) {
const [userPosts, setUserPosts] = useState([]);
const [user, setUser] = useState(null);
const [hasGalleryPermission, setHasGalleryPermission] = useState(null);
const [image, setImage] = useState(null);
useEffect(() => {
const { currentUser, posts } = props;
if (props.route.params.uid === firebase.auth().currentUser.uid) {
setUser(currentUser)
setUserPosts(posts)
}
else {
firebase.firestore()
.collection('users')
.doc(props.route.params.uid)
.get()
.then((snapshot) => {
if (snapshot.exists) {
setUser(snapshot.data());
}
else {
console.log('does not exist')
}
})
firebase.firestore()
.collection('posts')
.doc(props.route.params.uid)
.collection('userPosts')
.orderBy('creation', 'desc')
.get()
.then((snapshot) => {
let posts = snapshot.docs.map(doc => {
const data = doc.data();
const id = doc.id;
return { id, ...data }
})
setUserPosts(posts)
});
};
(async () => {
const galleryStatus = await ImagePicker.requestMediaLibraryPermissionsAsync();
setHasGalleryPermission(galleryStatus.status === 'granted');
})();
}, [props.route.params.uid, props.following]);
const pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [1, 1],
quality: 1,
});
if (!result.cancelled) {
setImage(result.uri);
};
};
if (hasGalleryPermission === false) {
return <View />;
};
if (hasGalleryPermission === false) {
return <Text>No access to gallery</Text>;
};
const onLogout = () => {
firebase.auth().signOut();
};
if (user === null) {
return <View />
}
return (
<View style={styles.container}>
{image && <Image source={{ uri: image }}
style={{ flex: 1 }} />}
<Image source={{ uri: props.route.params.profileImage }} />
<View style={styles.containerInfo}>
<Text>{user.name}</Text>
<Text>{user.state}</Text>
<Text>{user.city}</Text>
<Text>{user.email}</Text>
{props.route.params.uid !== firebase.auth().currentUser.uid}
<Button title='Pick Image From Gallery' onPress={() => pickImage()} />
<Button title='Save'
onPress={() => navigation.navigate('SaveProfileImage',
{ profileImage })} />
</View>
<View style={styles.container}>
<FlatList
numColumns={3}
horizontal={false}
data={userPosts}
renderItem={({ item }) => (
<View
style={styles.containerImage}>
<Image
style={styles.image}
source={{ uri: item.downloadURL }}
/>
</View>
)}
/>
</View>
<Button
title='Logout'
onPress={() => onLogout()}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
containerInfo: {
margin: 20
},
containerImage: {
flex: 1 / 3
},
image: {
flex: 1,
aspectRatio: 1 / 1
}
})
const mapStateToProps = (store) => ({
currentUser: store.userState.currentUser,
posts: store.userState.posts,
following: store.userState.following
})
export default connect(mapStateToProps, null)(Profile);
The app.js has a navigation container to control these screens:
<NavigationContainer >
<Stack.Navigator initialRouteName='Main'>
<Stack.Screen name='Main' component={MainScreen} />
<Stack.Screen name='Add' component={AddScreen}
navigation={this.props.navigation}/>
<Stack.Screen name='Save' component={SaveScreen}
navigation={this.props.navigation}/>
<Stack.Screen name='SaveProfileImage' component={SaveProfileImageScreen}
navigation={this.props.navigation}/>
<Stack.Screen name='Comment' component={CommentScreen}
navigation={this.props.navigation}/> </Stack.Navigator>
<NavigationContainer >
I can't understand why I can navigate from the AddScreen to the Save Screen, but I can't do the same between the profileScreen and the SaveProfileImage.
The save is this one:
import React, { useState } from 'react'
import { View, TextInput, Image, Button } from 'react-native'
import firebase from 'firebase'
require('firebase/firestore')
require('firebase/firebase-storage')
export default function Save(props) {
const [caption, setCaption] = useState('')
const uploadImage = async () => {
const uri = props.route.params.image;
const childPath =
`post/${firebase.auth().currentUser.uid}/${Math.random().toString(36)}`;
const response = await fetch(uri);
const blob = await response.blob();
const task = firebase
.storage()
.ref()
.child(childPath)
.put(blob);
const taskProgress = snapshot => {
console.log(`transferred: ${snapshot.bytesTransferred}`)
};
const taskCompleted = () => {
task.snapshot.ref.getDownloadURL().then((snapshot) => {
savePostData(snapshot);
})
};
const taskError = snapshot => {
console.log(snapshot)
};
task.on('state_changed', taskProgress, taskError, taskCompleted);
};
const savePostData = (downloadURL) => {
firebase.firestore()
.collection('posts')
.doc(firebase.auth().currentUser.uid)
.collection('userPosts')
.add({
downloadURL,
caption,
likesCount: 0,
estate: '',
city: '',
creation: firebase.firestore.FieldValue.serverTimestamp()
}).then((function () {
props.navigation.popToTop()
}))
};
return (
<View style={{ flex: 1 }}>
<Image source={{ uri: props.route.params.image }} />
<TextInput
placeholder='Write a Caption . . .'
onChangeText={(caption) => setCaption(caption)}
/>
<Button title='Save' onPress={() => uploadImage()} />
</View>
);
};
The SaveProfileScreen is this other one:
import React, { useState } from 'react'
import { View, TextInput, Image, Button } from 'react-native'
import firebase from 'firebase'
require('firebase/firestore')
require('firebase/firebase-storage')
export default function ProfileImage(props) {
const [caption, setCaption] = useState('')
const uploadImage = async () => {
const uri = props.route.params.image;
const childPath =
`profileImage/${firebase.auth().currentUser.uid}/
${Math.random().toString(36)}`;
console.log(childPath);
const response = await fetch(uri);
const blob = await response.blob();
const task = firebase
.storage()
.ref()
.child(childPath)
.put(blob);
const taskProgress = snapshot => {
console.log(`transferred: ${snapshot.bytesTransferred}`)
};
const taskError = snapshot => {
console.log(snapshot)
};
const taskCompleted = () => {
task.snapshot.ref.getDownloadURL().then((snapshot) => {
savePictureData(snapshot);
})
};
task.on('state_changed', taskProgress, taskError, taskCompleted);
};
return (
<View style={{ flex: 1 }}>
<Image source={{ uri: props.route.params.profileImage }} />
<Button title='Save' onPress={() => uploadImage()} />
</View>
);
};