I have made a Login page in React Native with React hooks along with redux thunk connect.
In that Login Page, i have import a LoginForm as customComponent and I have properly export and import that component.But still it produce Element type error.
LoginPage
import React, { useState } from "react";
import {
View,
Text,
TouchableOpacity,
StyleSheet,
Dimensions,
} from "react-native";
import PropsType from "prop-types";
import { connect } from "react-redux";
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scrollview";
import SimpleReactValidator from "simple-react-validator";
import Img from "../../common/Img";
import Loader from "../../common/Loader";
import styles from "../../globalStyle";
import * as theme from "../../theme";
import AxiosInstance from "../../helper/axios.interceptor";
import * as globalHelper from "../../helper/globalHelper";
import { setAuth } from "../../store/auth/auth.action";
import LoginForm from "./Component/LoginForm";
const { height } = Dimensions.get("screen");
const propsType = {
authAction: PropsType.func,
};
let request = { type: "mobile" };
const validator = new SimpleReactValidator();
const SignIn = ({ navigation, authAction }) => {
const [errors, setErrors] = useState({});
const [state, setState] = useState({ loader: false }),
updateState = (key, value) => {
setState((preState) => ({
...preState,
[key]: value,
}));
},
openLoader = (val) => {
updateState("loader", val);
};
const submit = async () => {
try {
openLoader(true);
let body = {
type: "mobile",
userName: request.userName,
password: request.password,
};
console.log("body", body);
const response = await AxiosInstance.post("auth/login", body);
console.log("loginresponse...", response);
if (response.status) {
await globalHelper.setAsyncStore("user", response);
await globalHelper.setAsyncStore(
"userpermission",
response.data.permissionJson
);
authAction(response);
navigation.replace("AppNavigation");
}
openLoader(false);
} catch (err) {
openLoader(false);
console.log("login error", err);
}
};
//
const ForgotPassword = () => (
<TouchableOpacity
onPress={() => {
validator.hideMessages();
setErrors("");
navigation.push("ForgotPassword");
}}
>
<Text style={[localStyle.forgotText]}>Forgot Password?</Text>
</TouchableOpacity>
);
return (
<>
<KeyboardAwareScrollView>
<View style={[styles.flexCenter, { height }]}>
<Img
src={require("../../assets/logo/logoNew.png")}
style={{ width: 237, height: 250, marginBottom: -20 }}
resizeMode="contain"
/>
<View style={localStyle.authButton}>
<LoginForm
validator={validator}
onTextChanging={(data) => {
request = { ...request, ...data };
}}
onSubmit={() => {
submit();
}}
errors={errors}
setErrors={setErrors}
/>
</View>
<View style={[styles.flexCenter]}>
<ForgotPassword />
</View>
</View>
</KeyboardAwareScrollView>
{state.loader && <Loader />}
</>
);
};
const { color } = theme;
const localStyle = StyleSheet.create({
authButton: {
width: "80%",
borderRadius: 5,
},
forgotText: {
marginTop: 20,
color: color.hashTextColor,
},
});
SignIn.propsType = propsType;
const mapDispatchToProps = {
authAction: setAuth,
};
export default connect(null, mapDispatchToProps)(SignIn);
and the customComponent LoginForm as
import React, { useState, useEffect, useRef } from "react";
import {
View,
Text,
TouchableOpacity,
ScrollView,
StyleSheet,
} from "react-native";
import { Form, Icon } from "native-base";
import PropsType from "prop-types";
import { has } from "lodash";
import RegularInput from "../../../common/Input";
import styles from "../../../globalStyle";
import AuthButton from "../../../common/Button/AuthButton";
const propsType = {
onTextChanging: PropsType.func.isRequired,
onSubmit: PropsType.func.isRequired,
};
// const validator = new SimpleReactValidator();
const LoginForm = ({
onTextChanging,
onSubmit,
validator,
errors,
setErrors,
}) => {
const [model, setModel] = useState({ focus: "username", secured: true });
const isValid = (key) => !!(has(errors, key) && errors[key]);
const [state, setState] = useState({
userName: "",
password: "",
});
const secondTextInput = useRef(null);
useEffect(() => {
onTextChanging(state);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [state]);
const updateState = (key, value) => {
setState((preState) => ({
...preState,
[key]: value,
}));
};
const submit = () => {
if (validator.allValid()) {
onSubmit();
} else {
console.log("getErrorMessages");
setErrors(validator.getErrorMessages());
validator.showMessages();
}
};
return (
<ScrollView keyboardShouldPersistTaps="handled">
<Form style={[styles.top50, { width: "100%" }]}>
<View style={{ width: "100%", paddingBottom: 15 }}>
<RegularInput
title="Email"
value={state.userName}
attri="userName"
placeHolder={"Email"}
keyboardType="ascii-capable"
updateStateFunc={updateState}
error={isValid("Username")}
isFocus={model.focus === "username"}
onFocusFun={() => setModel({ focus: "username" })}
otherProps={{
onSubmitEditing: () => {
console.log(secondTextInput);
secondTextInput.current.focus();
},
blurOnSubmit: false,
}}
/>
</View>
{!!validator.message("Email", state.userName, "email") && (
<Text style={[styles.error]}>
{validator.message("Email", state.userName, "email")}
</Text>
)}
<View
style={[
styles.flexCenter,
styles.flexRow,
localStyle.absoluteContainer,
]}
>
<RegularInput
reff={secondTextInput}
title="Password"
value={state.password}
attri="password"
placeHolder={"Password"}
updateStateFunc={updateState}
dataDetectorTypes="phoneNumber"
secureTextEntry={!state.secured}
error={isValid("Password")}
isFocus={model.focus === "password"}
onFocusFun={() => setModel({ focus: "password" })}
/>
<TouchableOpacity
style={localStyle.eyeIcon}
onPress={() => {
updateState("secured", !state.secured);
}}
>
{state.secured && (
<Icon style={{ fontSize: 16 }} name="eye" type="Entypo" />
)}
{!state.secured && (
<Icon
style={{ fontSize: 16 }}
name="eye-with-line"
type="Entypo"
/>
)}
</TouchableOpacity>
</View>
{!!validator.message("Password", state.password, "required") && (
<Text style={[styles.error]}>
{validator.message("Password", state.password, "required")}
</Text>
)}
<View style={[styles.flexCenter, localStyle.authButton]}>
<AuthButton title="LOGIN" onPress={() => submit()} />
</View>
</Form>
</ScrollView>
);
};
const localStyle = StyleSheet.create({
authButton: {
marginTop: 10,
borderRadius: 5,
},
inputStyle: {
height: 40,
borderColor: "gray",
borderBottomWidth: 1,
},
absoluteContainer: {
position: "relative",
overflow: "hidden",
width: "100%",
},
eyeIcon: {
position: "absolute",
// right: 0,
width: 25,
height: 25,
elevation: 999,
zIndex: 999,
top: 42,
right: 5,
},
});
LoginForm.propsType = propsType;
export default LoginForm;
I have tried
import LoginForm from "./Component/LoginForm";
as
import {LoginForm} from "./Component/LoginForm";
It produce check the render method of SignIn
and also use connect in LoginForm,
export default connect(null, null)(LoginForm);
but it gives same error. I don't know what mistake i have made.unable to find any cause. Thanks in advance
Element type Error
Related
I'm not sure what happened, but I had to clean/rebuild gradle from a version issue with react-native-screens, and when I finally got everything back and working, the screens don't show on Android, but they work exactly as they did before with the issue. I'm not even sure how or where to look. I know it's the Drawer Navigator because if I pass a view instead it will show. Keep in mind - before I had the gradle issue, EVERYTHING worked - which is why I'm so stumped.
Here is my code:
App.tsx
import 'react-native-gesture-handler';
import React, { useEffect, useRef, useState } from 'react';
import { NavigationContainer, DefaultTheme, DarkTheme, LinkingOptions, NavigatorScreenParams, useNavigationContainerRef } from '#react-navigation/native';
import { createNativeStackNavigator, NativeStackHeaderProps } from '#react-navigation/native-stack';
import { Platform, StatusBar, View } from 'react-native';
import { enableScreens } from 'react-native-screens';
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
import Orientation from 'react-native-orientation-locker';
import OneSignal from 'react-native-onesignal';
import analytics from '#react-native-firebase/analytics';
import {
StyleSheet
} from 'react-native';
import MenuButton from './src/components/layout/MenuButton';
// Import pages and components
import { colors } from './src/styles/colorPalette';
import MainHeader from './src/components/headers/MainHeader';
import DrawerNavigator from './src/navigators/DrawerNavigator';
import ThemeContextProvider, { useTheme } from './src/contexts/ThemeContext';
import AuthContextProvider from './src/contexts/AuthContext'
import { useAuth } from './src/contexts/AuthContext';
import LoginRegister from './src/components/LoginRegister';
import EmailPhoneCollector from './src/components/EmailPhoneCollector';
import Search from './src/components/Search';
import SearchHeader from './src/components/headers/SearchHeader'
import DetailView from './src/components/DetailView';
import FullScreenLoader from './src/components/FullScreenLoading';
// Import theme and styling files
import { testAppDefaultTheme, testAppDefaultDarkTheme } from './src/styles/theme';
import Home from './src/pages/home';
// Globals (fonts, etc)
const sequel100Wide = Platform.OS == 'ios' ? 'Sequel100Wide-65' : 'Sequel100Wide-85'
type DrawerParamsList = {
Home: undefined
Videos: undefined
News: undefined
Podcasts: undefined
}
type RootStackParamList = {
Drawer: NavigatorScreenParams<DrawerParamsList>
DetailView: undefined
Search: undefined
config?: undefined
}
const Stack = createNativeStackNavigator()
enableScreens()
export default function App(props: any) {
// To override login functionality
const bypassLogin = false
const clearOnRestart = false
const CustomApp = () => {
const { clearID, logout, user, emailSMSId, timeExceeded } = useAuth()
const { darkMode, showNavBar } = useTheme()
const navigationRef = useNavigationContainerRef()
const routeNameRef = useRef<string | undefined>('')
// Where we display pre-prompt on login and iOS platform
const checkIosSoftPrompt = async () => {
const deviceState = await OneSignal.getDeviceState()
if (!deviceState?.isSubscribed) {
OneSignal.addTrigger('ios_prompt', 'true')
}
}
const linking: LinkingOptions<RootStackParamList> = {
prefixes: ['testApp://', 'https://testApp.com', 'https://*.testApp.com'],
config: {
screens: {
Drawer: {
initialRouteName: 'Home',
screens: {
Videos: 'tv',
News: 'news',
Podcasts: 'podcasts',
Home: ''
}
},
DetailView: {
path: ':category?/:type/:slug',
},
Search: {
path: 'search',
}
}
},
}
const HeaderComponent = (props: any) => <MainHeader {...props} />
useEffect(() => {
if (bypassLogin && user) logout()
/* if ((bypassLogin || (user || emailSMSId)) && Platform.OS == 'ios') {
checkIosSoftPrompt()
} */
// More aggressive targeting for Pre-Prompt message
if (Platform.OS === 'ios' && (emailSMSId)) checkIosSoftPrompt();
}, [bypassLogin, user, emailSMSId])
useEffect(() => {
if (clearOnRestart) clearID()
}, [])
const checkRoutes = async () => {
const previousRouteName = routeNameRef.current;
const currentRouteName = navigationRef.current?.getCurrentRoute()?.name ?? '';
if (previousRouteName !== currentRouteName) {
await analytics().logScreenView({ screen_name: currentRouteName });
}
// Save the current route name for later comparison
routeNameRef.current = currentRouteName;
}
const setRefs = () => {
routeNameRef.current = navigationRef.current?.getCurrentRoute()?.name;
}
return(
<SafeAreaProvider>
<StatusBar translucent barStyle={darkMode ? 'light-content' : 'dark-content'} backgroundColor="transparent" />
<NavigationContainer
ref={navigationRef}
onReady={ setRefs }
onStateChange={ checkRoutes }
theme={darkMode ? testAppDefaultDarkTheme : testAppDefaultTheme}
linking={linking}
fallback={<FullScreenLoader message='Loading...'/>}
>
{ bypassLogin || !!user || emailSMSId || (timeExceeded != null && !timeExceeded ) ?
<Stack.Navigator
screenOptions={{ header: HeaderComponent, contentStyle:{ backgroundColor: darkMode ? colors.background.darkHeaderBg : colors.background.lightSubdued } }}
>
<Stack.Screen name="Drawer" component={DrawerNavigator} options={{ headerShown: false }} />
{ /* Common screens outside flow of Drawer Navigator */}
<Stack.Screen name="Search" component={Search}
options={{
header: SearchHeader,
headerShown: false,
animation: 'fade_from_bottom',
headerTitle: '',
headerStyle: {
backgroundColor: darkMode ? colors.primary.blue7 : '#fff',
},
contentStyle: {
backgroundColor: darkMode ? colors.primary.blue7 : '#fff',
}
}}
/>
<Stack.Screen name="DetailView" component={DetailView}
options={{
presentation: 'containedModal',
headerShown: false,
contentStyle: {
backgroundColor: darkMode
? colors.primary.blue7
: '#fff',
},
orientation:
Platform.OS === 'ios' ? 'portrait_up' : undefined,
headerBackButtonMenuEnabled: false,
headerBackVisible: false,
headerTitle: ''
}}
/>
</Stack.Navigator>
:
<EmailPhoneCollector />
}
</NavigationContainer>
</SafeAreaProvider>
)
}
// Main app useEffect hook
useEffect(() => {
// Lock orientation with Orientation (rather than React Navigation)
Orientation.lockToPortrait()
// OneSignal initializer
OneSignal.setLogLevel(6, 0);
OneSignal.setAppId("99a9aeab-f82e-49e8-bc40-8f471c7bf1f9");
/* //Prompt for push on iOS
OneSignal.promptForPushNotificationsWithUserResponse((response: any) => { // Replace this for pre-prompt flow
console.log("Prompt response:", response);
}); */
//Method for handling notifications received while app in foreground
OneSignal.setNotificationWillShowInForegroundHandler(notificationReceivedEvent => {
console.log("OneSignal: notification will show in foreground:", notificationReceivedEvent);
let notification = notificationReceivedEvent.getNotification();
console.log("notification: ", notification);
const data = notification.additionalData
console.log("additionalData: ", data);
// Complete with null means don't show a notification.
notificationReceivedEvent.complete(notification);
});
//Method for handling notifications opened
OneSignal.setNotificationOpenedHandler((notification) => {
// console.log("OneSignal: notification opened:", notification);
});
}, [])
return (
<AuthContextProvider>
<ThemeContextProvider>
<CustomApp />
</ThemeContextProvider>
</AuthContextProvider>
);
}
const styles = StyleSheet.create({
drawerIcon: {
width: 30,
height: 30,
}
})
DrawerNavigator:
import React, { useEffect, useState } from 'react';
import { Linking, View, TouchableOpacity, Platform } from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import { SafeAreaView } from 'react-native-safe-area-context';
import FontAwesome from 'react-native-vector-icons/FontAwesome';
import { Divider, Switch } from 'react-native-elements';
import { DrawerContentScrollView, DrawerItem, DrawerItemList } from '#react-navigation/drawer';
import { colors } from '../../styles/colorPalette';
import { useTheme } from '../../contexts/ThemeContext';
import HomeLogo from './HomeLogo';
import { Button } from 'react-native-elements/dist/buttons/Button';
import Text from '../TextComponents'
// Contexts
import { useAuth } from '../../contexts/AuthContext'
import VersionCheck from 'react-native-version-check';
// Globals (fonts, etc)
const sequel100Wide6585 = Platform.OS == 'ios' ? 'Sequel100Wide-65' : 'Sequel100Wide-85'
export default function DrawerWrapper(props: any) {
const darkMode = useTheme().darkMode;
const { navigation, route, state } = props;
const { appInfo, user, logout } = useAuth();
const goToStore = async () => {
if (Platform.OS == 'ios') {
VersionCheck.getAppStoreUrl({ appID: "1609502829" })
.then(res => {
if (res) {
Linking.canOpenURL(res)
.then(supported => {
console.log('the link: ', res)
supported && Linking.openURL(res)
},
(err) => console.log('Error opening link: ', err)
)
}
})
}
else if (Platform.OS == 'android') {
VersionCheck.getPlayStoreUrl({ packageName: "com.testAppmobile.testApp" })
.then(res => {
if (res) {
Linking.canOpenURL(res)
.then(supported => {
console.log('the link: ', res)
supported && Linking.openURL(res)
},
(err) => console.log('Error opening link: ', err)
)
}
})
}
}
return (
<SafeAreaView style={{flex: 1}} edges={[]}>
<View style={{ flexDirection: "row", padding: 20, alignItems: 'center', alignContent: 'center', marginTop: Platform.OS == 'android' ? 20 : 0 }}>
<TouchableOpacity onPress={() => navigation.closeDrawer()}>
<Icon name="md-close" size={40} color={darkMode ? colors.primary.blue2 : 'black'}/>
</TouchableOpacity>
<HomeLogo />
<TouchableOpacity style={{flexGrow: 1, display: 'none'}}>
<FontAwesome style={{textAlign: 'right'}} name="cog" size={40} color={darkMode ? colors.primary.blue2 : 'black'} />
</TouchableOpacity>
</View>
{ user && <View style={{flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-end', paddingHorizontal: 20, paddingBottom: 10}}>
<Text style={{marginRight: 20}}>{user?.username ?? user?.name ?? user?.email}</Text>
<Button title="Logout" titleStyle={{fontSize: 12, textTransform: 'uppercase'}} style={{backgroundColor: 'red'}} onPress={() => logout()}/>
</View>}
<Divider />
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} labelStyle={{fontFamily: sequel100Wide6585}}/>
<DrawerItem
onPress={() => Linking.openURL('https://testApp.com')}
activeBackgroundColor={darkMode ? colors.primary.blue5 : colors.primary.blue1} {...props}
label="testApp.com"
labelStyle={{fontFamily: sequel100Wide6585,
fontWeight: 'normal',
fontSize: 13,
paddingVertical: 15,
color: darkMode ? colors.primary.blue2 : '#000'}} icon={({focused, size, color}) => <FontAwesome size={30} style={{color: darkMode ? '#98D4FF' : "#000", marginLeft: -5}} name="external-link-square"/>}
/>
</DrawerContentScrollView>
<View style={{flexDirection: 'row', alignItems: 'center'}}>
{ !!appInfo?.appVersion && <Text variant="h5" style={{ color: darkMode ? '#fff' : '#000', paddingLeft: 10 }}>{`v${appInfo.appVersion}`}</Text> }
{ !!appInfo?.needsUpdate && <Button onPress={goToStore} buttonStyle={{borderRadius: 10, backgroundColor: 'green', marginLeft: 10}} title={<Text variant="h5" style={{color: '#fff'}}>Update Available</Text>} />}
</View>
</SafeAreaView>
)
}
My Home Stack:
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { ActivityIndicator, FlatList, Platform, RefreshControl, RefreshControlProps, StyleSheet, TouchableOpacity, View } from 'react-native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import { useHeaderHeight } from '#react-navigation/elements';
import PostCard from '../../components/PostCard';
import FullScreenLoader from '../../components/FullScreenLoading';
import { colors } from '../../styles/colorPalette';
import { useTheme } from '../../contexts/ThemeContext';
import LiveVideoCard from '../../components/LiveVideoCard';
import HorizontalCarousel from '../../components/HorizontalCarousel';
import { KeyboardAwareFlatList, KeyboardAwareFlatListProps } from 'react-native-keyboard-aware-scroll-view';
import FooterStack from '../../components/layout/FooterStack';
import PersonDetails from '../../components/PersonDetails';
import Text from '../../components/TextComponents';
import NoResults from '../../components/NoResults';
import Animated, { enableLayoutAnimations, FadeIn, Layout } from 'react-native-reanimated';
const Home = (props: any) => {
const { navigation } = props;
const darkMode = useTheme().darkMode;
const headerHeight = useHeaderHeight();
const Stack = createNativeStackNavigator();
const styles = StyleSheet.create({
button: {
backgroundColor: 'transparent',
borderRadius: 50,
flex: 1,
width: 120,
height: 40,
marginHorizontal: 10,
},
container: {
flexGrow: 1,
flex: 1,
},
image: {
height: 300,
resizeMode: 'contain',
},
logo: {
height: 100,
width: '100%',
resizeMode: 'contain',
},
posts: {
borderStyle: 'solid',
borderWidth: 5,
borderColor: 'red',
},
subtitle: {
fontSize: 12,
marginTop: 3,
color: '#777777',
flex: 1,
},
title: {
fontSize: 24,
fontWeight: 'bold',
flex: 1,
},
videoContainer: {
position: 'relative',
height: 300,
marginVertical: 25,
flex: 1,
},
watchMore: {
flexDirection: 'row',
justifyContent: 'space-between',
padding: 20,
borderTopWidth: 1,
borderBottomWidth: 1,
borderStyle: 'solid',
borderColor: 'rgba(0,0,0,0.25)',
},
});
const HomeHeader = React.memo(() => {
const darkMode = useTheme().darkMode
return (
<>
<LiveVideoCard />
<View>
<View style={[styles.watchMore, { backgroundColor: darkMode ? colors.background.darkBg : '#fff' }]}>
<Text style={{color: darkMode ? '#fff' : '#000', fontSize: 15, fontWeight: 'bold'}}>
Watch
</Text>
<TouchableOpacity onPress={() => navigation.navigate('Videos')}>
<Text style={{color: darkMode ? colors.primary.blue2 : colors.primary.blueMain, fontWeight: 'bold', fontSize: 14}}>
Watch More
</Text>
</TouchableOpacity>
</View>
<HorizontalCarousel />
</View>
</>
)
}, (prev, next) => {
return true
})
const FooterItem = React.memo(() => {
const darkMode = useTheme().darkMode
return(
<View style={{paddingBottom: 100}}>
<Text variant="h6" style={{alignSelf: 'center', color: darkMode ? '#fff' : '#000'}}>Loading more posts...</Text>
<ActivityIndicator size={'large'} color={darkMode ? '#fff' : '#777'} style={{ paddingVertical: 20 }} />
</View>
)}, () => true)
const renderItem = useCallback((props: any) => {
return (
<PostCard data={props.item.node} categoryOverlay="Home" index={props.index} navigation={navigation} />
)
}, [])
const CustomRefreshControl = (props: RefreshControlProps) => {
const darkMode = useTheme().darkMode
return <RefreshControl
progressViewOffset={headerHeight}
refreshing={props.refreshing}
onRefresh={props.onRefresh}
tintColor={darkMode ? '#fff' : '#000'}
titleColor={darkMode ? '#fff' : '#000'}
/>
}
const HomeComponent = useCallback(() => {
const [data, setData] = useState([]);
const flatlistRef = useRef<FlatList>(null)
const onEndReachedCalledDuringMomentum = useRef(false)
const [loading, setLoading] = useState(true);
const [pageNum, setPageNum] = useState(0);
const [refreshing, setRefreshing] = useState(false);
const controller = new AbortController()
const fetchData = async (newPageNum?: number) => {
if (newPageNum !== 0 && newPageNum == pageNum) return console.log("They're the same")
const timer = setTimeout(() => {
controller?.abort()
}, 10000)
try {
// Here is where we try to fetch data
const videos = await fetch(
`https://home.testApp.com/m/shows/latest?page=${newPageNum ?? pageNum}`,
{
method: 'GET',
mode: 'cors',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'User-Agent': 'testApp/2.0'
},
signal: controller.signal
}
);
const json = await videos
.json()
.catch((e: any) =>
console.log('this is an error in the json output: ', e),
);
clearTimeout(timer)
setData(data.concat(json.nodes));
if (!!newPageNum) setPageNum(newPageNum)
} catch (e) {
// Manage the errors here
console.log('Error returning from testApp videos: ', e);
} finally {
if (newPageNum === 0 || !!loading) setLoading(false)
if (!!refreshing) setRefreshing(false)
controller?.abort()
}
};
const keyExtractor = (item: any, idx: number) => {
return `${item.node.nid}-${idx}`
};
const handleLoadMore = async (props: any) => {
if (onEndReachedCalledDuringMomentum.current === true) return console.log('momentum blocked')
fetchData(pageNum + 1)
onEndReachedCalledDuringMomentum.current = true
}
const handleRefresh = async () => {
setRefreshing(true);
setLoading(true);
setData([]);
fetchData(0);
setRefreshing(false)
}
const _onMomentumScrollBegin = () => {
onEndReachedCalledDuringMomentum.current = false
}
useEffect(() => {
// This is where we fetch the data from the website
if (loading) fetchData();
// Pass ref to navigation object for scrollTo logic
navigation.setOptions({
homeScrollRef: flatlistRef
})
return () => controller?.abort()
}, []);
if (!!loading) return <FullScreenLoader message={'Loading Home posts'} />
return (
<FlatList
ref={flatlistRef}
contentContainerStyle={{marginTop: headerHeight ?? undefined }}
style={{flex: 1 }}
scrollIndicatorInsets={{ right: 1 }}
showsVerticalScrollIndicator={false}
initialNumToRender={5}
maxToRenderPerBatch={15}
windowSize={15}
data={data}
keyExtractor={keyExtractor}
ListEmptyComponent={<NoResults />}
ListHeaderComponent={HomeHeader}
ListFooterComponent={FooterItem}
renderItem={renderItem}
onEndReached={handleLoadMore}
onEndReachedThreshold={0.5}
onMomentumScrollBegin={_onMomentumScrollBegin}
scrollEventThrottle={16}
refreshControl={<CustomRefreshControl
refreshing={refreshing}
onRefresh={handleRefresh}
/>}
/>
)
}, [])
return (
<Stack.Navigator screenOptions={{ orientation: Platform.OS === 'ios' ? 'portrait_up' : 'portrait_up', headerShown: false, contentStyle: { backgroundColor: darkMode ? colors.background.darkHeaderBg : colors.background.lightSubdued }}}>
<Stack.Screen name="HomeView" options={{title: 'Home' }} component={HomeComponent} />
<Stack.Screen name="FooterLinks" component={FooterStack} />
</Stack.Navigator>
);
}
export default Home
try disable animation on android
import { enableLayoutAnimations } from "react-native-reanimated";
if (Platform.OS === 'android') {
enableLayoutAnimations(false);
}
import React, {useState} from 'react';
import {
SafeAreaView,
TextInput,
Button,
ActivityIndicator,
StyleSheet,
Text,
View,
} from 'react-native';
import {Formik} from 'formik';
import * as yup from 'yup';
import StyledInput from './styledInput';
import validationSchema from './validationSchema';
import ActionButton from 'react-native-action-button';
import ImagePicker from 'react-native-image-crop-picker';
import Icon from 'react-native-vector-icons/Ionicons';
handleFileUpload = event => {
this.setState({WAHTEVETKEYYOUNEED: event.currentTarget.files[0]});
};
const choosePhotoFromLibrary = () => {
ImagePicker.openPicker({
width: 1200,
height: 780,
cropping: true,
}).then(image => {
console.log(image);
const imageUri = Platform.OS === 'ios' ? image.sourceURL : image.path;
setImage(imageUri);
});
};
const takePhotoFromCamera = () => {
ImagePicker.openCamera({
width: 1200,
height: 780,
cropping: true,
}).then(image => {
console.log(image);
const imageUri = Platform.OS === 'ios' ? image.sourceURL : image.path;
setImage(imageUri);
});
};
const uploadImage = async () => {
console.log('Uploading Image');
if (image == null) {
return null;
}
const uploadUri = image;
console.log('Upload URI:', uploadUri);
let filename = uploadUri.substring(uploadUri.lastIndexOf('/') + 1);
// Add timestamp to File Name
const extension = filename.split('.').pop();
const name = filename.split('.').slice(0, -1).join('.');
filename = name + Date.now() + '.' + extension;
setUploading(true);
const storageRef = storage().ref(`photos/${filename}`);
try {
const task = await storageRef.putFile(uploadUri);
} catch (e) {
console.error(e);
return null;
}
try {
let imageUrl = await storageRef.getDownloadURL();
setUploading(false);
return imageUrl;
} catch (e) {
setUploading(false);
console.error(e);
return null;
}
};
const ProductListingForm = () => (
<SafeAreaView style={{marginTop: 90}}>
<Formik
initialValues={{
listingTheme: '',
size: '',
barcode: '',
description: '',
units: '',
listingName: '',
quantity: '',
}}
onSubmit={(values, actions) => {
console
.log(values)
.then(async () => {
let imageUrl = await uploadImage();
console.log('Image Url: ', imageUrl);
console.log('Listing: ', listing);
});
firestore()
.collection('listings')
.add({
userId: auth().currentUser.uid,
listingTheme: values.listingTheme,
size: values.size,
barcode: values.barcode,
description: values.description,
units: values.units,
listingName: values.listingName,
quantity: values.quantity,
})
.then(() => {
console.log('Listing Added!');
Alert.alert(
'Listing published!',
'Your Listing has been published Successfully!',
);
setListing(null);
})
.catch(error => {
console.log(
'Something went wrong with added Listing to firestore.',
error,
);
});
actions.resetForm();
}}
validationSchema={validationSchema}>
{formikProps => (
<React.Fragment>
<StyledInput
label={'Product Name'}
formikProps={formikProps}
formikKey="listingName"
placeholderText={'Enter Product Name'}
autoFocus
value={formikProps.values.listingName}
/>
<StyledInput
label={'Product Theme'}
formikProps={formikProps}
formikKey="listingTheme"
placeholderText={'Enter a theme that describes your product'}
value={formikProps.values.listingTheme}
/>
<StyledInput
label={'Barcode'}
formikProps={formikProps}
formikKey="barcode"
placeholderText={'Scan/Enter Barcode'}
value={formikProps.values.barcode}
/>
<StyledInput
label={'Size'}
formikProps={formikProps}
formikKey="size"
placeholderText={'Enter Product Size'}
value={formikProps.values.size}
/>
<StyledInput
label={'Units'}
formikProps={formikProps}
formikKey="units"
placeholderText={'Enter Product Units'}
value={formikProps.values.units}
/>
<StyledInput
label={'Description'}
formikProps={formikProps}
formikKey="description"
placeholderText={'Describe your product'}
value={formikProps.values.description}
/>
<ActionButton buttonColor="#5D05B6">
<ActionButton.Item
buttonColor="#5D05B6"
title="Take Photo"
onPress={takePhotoFromCamera}>
<Icon name="camera-outline" style={styles.actionButtonIcon} />
</ActionButton.Item>
<ActionButton.Item
buttonColor="#5D05B6"
title="Choose Photo"
onPress={choosePhotoFromLibrary}>
<Icon name="md-images-outline" style={styles.actionButtonIcon} />
</ActionButton.Item>
</ActionButton>
{formikProps.isSubmitting ? (
<ActivityIndicator />
) : (
<Button title="Submit" onPress={formikProps.handleSubmit} />
)}
</React.Fragment>
)}
</Formik>
</SafeAreaView>
);
export default ProductListingForm;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
actionButtonIcon: {
fontSize: 20,
height: 22,
color: 'white',
},
});
So above is my product listing form and I want to save this form data to my firebase collection. Currently on VS code there are no errors with the form itself and I am able to see it within my simulator. I can choose an image but I can't set image yet because I haven't added setimage state yet but the main issue I'm having now is the handleSumbit. When I click the submit button none of the values are logged in my terminal nor my firestore database.
I need help submitting and send this form data to firebase. Where did I go wrong here?
Below are images of my Product Listing Form. As you can see there are no issues displaying it.
Product Listing Form
Product Listing Form with Action Button triggered
Below is my Styled Input file for reference. It's the input I am using throughout this form.
import React from 'react';
import {
SafeAreaView,
TextInput,
Button,
ActivityIndicator,
Text,
View,
} from 'react-native';
import {Formik} from 'formik';
import * as yup from 'yup';
const StyledInput = ({
label,
formikProps,
formikKey,
placeholderText,
...rest
}) => {
const inputStyles = {
borderWidth: 1,
borderColor: 'black',
padding: 10,
marginBottom: 3,
};
if (formikProps.touched[formikKey] && formikProps.errors[formikKey]) {
inputStyles.borderColor = 'red';
}
return (
<View style={{marginHorizontal: 20, marginVertical: 5}}>
<Text style={{marginBottom: 3}}>{label}</Text>
<TextInput
placeholder={placeholderText}
style={inputStyles}
onChangeText={formikProps.handleChange(formikKey)}
onBlur={formikProps.handleBlur(formikKey)}
{...rest}
/>
<Text style={{color: 'red'}}>
{formikProps.touched[formikKey] && formikProps.errors[formikKey]}
</Text>
</View>
);
};
export default StyledInput;
Hi I have a add minus component button that I want to pass the value to selected value to the "To Cart" button. If the value is zero the orders will not be added but if the value is > 0 then it will be added to cart.
below is my Add Minus button component
AddMinusButton.js
import React, { useState } from "react";
import { View, TouchableOpacity, Text, StyleSheet } from "react-native";
import Colors from "../../constants/Colors";
const AddMinusButton = () => {
const [value, setValue] = useState(0);
const minusValue = () => {
if (value > 0) {
setValue(value - 1);
} else {
setValue(0);
}
};
const plusValue = () => {
setValue(value + 1);
};
return (
<View style={styles.container}>
<TouchableOpacity style={styles.buttonLeft} onPress={minusValue}>
<Text>-</Text>
</TouchableOpacity>
<Text style={styles.quantity}> {value}</Text>
<TouchableOpacity style={styles.buttonRight} onPress={plusValue}>
<Text>+</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container: {
flexDirection: "row",
alignItems: "center",
borderWidth: 1,
borderRadius: 10,
height: 35,
overflow: "hidden",
borderColor: "black",
},
quantity: {
paddingHorizontal: 10,
marginRight: 3,
},
buttonLeft: {
alignItems: "center",
backgroundColor: Colors.primary,
borderRightWidth: 1,
borderRightColor: "black",
padding: 10,
},
buttonRight: {
alignItems: "center",
backgroundColor: Colors.primary,
borderLeftWidth: 1,
borderLeftColor: "black",
padding: 10,
},
});
export default AddMinusButton;
This is my Product Screen. I want to pass the value that is selected by the AddMinusButton component to the "To Cart". I am only able to made the button click and it will add one by one.
ProductOverviewScreen.js
import React, { useState, useEffect, useCallback } from "react";
import {
View,
Text,
FlatList,
Button,
Platform,
ActivityIndicator,
StyleSheet,
} from "react-native";
import { useSelector, useDispatch } from "react-redux";
import { HeaderButtons, Item } from "react-navigation-header-buttons";
import HeaderButton from "../../components/UI/HeaderButton";
import ProductItem from "../../components/shop/ProductItem";
import * as cartActions from "../../store/actions/cart";
import * as productsActions from "../../store/actions/products";
import Colors from "../../constants/Colors";
import * as AddMinusButton from "../../components/UI/AddMinusButton";
const ProductsOverviewScreen = (props) => {
const [isLoading, setIsLoading] = useState(false);
const [isRefreshing, setIsRefreshing] = useState(false);
const [error, setError] = useState();
const products = useSelector((state) => state.products.availableProducts);
const dispatch = useDispatch();
const loadProducts = useCallback(async () => {
setError(null);
setIsRefreshing(true);
try {
await dispatch(productsActions.fetchProducts());
} catch (err) {
setError(err.message);
}
setIsRefreshing(false);
}, [dispatch, setIsLoading, setError]);
useEffect(() => {
const willFocusSub = props.navigation.addListener(
"willFocus",
loadProducts
);
return () => {
willFocusSub.remove();
};
}, [loadProducts]);
useEffect(() => {
setIsLoading(true);
loadProducts().then(() => {
setIsLoading(false);
});
}, [dispatch, loadProducts]);
const selectItemHandler = (id, title) => {
props.navigation.navigate("ProductDetail", {
productId: id,
productTitle: title,
});
};
if (error) {
return (
<View style={styles.centered}>
<Text>An error occurred!</Text>
<Button
title="Try again"
onPress={loadProducts}
color={Colors.primary}
/>
</View>
);
}
if (isLoading) {
return (
<View style={styles.centered}>
<ActivityIndicator size="large" color={Colors.primary} />
</View>
);
}
if (!isLoading && products.length === 0) {
return (
<View style={styles.centered}>
<Text>No products found. Maybe start adding some!</Text>
</View>
);
}
return (
<FlatList
onRefresh={loadProducts}
refreshing={isRefreshing}
data={products}
keyExtractor={(item) => item.id}
renderItem={(itemData) => (
<ProductItem
image={itemData.item.imageUrl}
title={itemData.item.title}
price={itemData.item.price}
onSelect={() => {
selectItemHandler(itemData.item.id, itemData.item.title);
}}
>
<AddMinusButton />
<Button
color={Colors.primary}
title="To Cart"
onPress={() => {
dispatch(cartActions.addToCart(itemData.item));
}}
/>
</ProductItem>
)}
/>
);
};
ProductsOverviewScreen.navigationOptions = (navData) => {
return {
headerTitle: "All Products",
headerLeft: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Menu"
iconName={Platform.OS === "android" ? "md-menu" : "ios-menu"}
onPress={() => {
navData.navigation.toggleDrawer();
}}
/>
</HeaderButtons>
),
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Cart"
iconName={Platform.OS === "android" ? "md-cart" : "ios-cart"}
onPress={() => {
navData.navigation.navigate("Cart");
}}
/>
</HeaderButtons>
),
};
};
const styles = StyleSheet.create({
centered: { flex: 1, justifyContent: "center", alignItems: "center" },
});
export default ProductsOverviewScreen;
Here I have used Redux to manage state but i am getting this type of Error: TypeError: navigation.state.params.AddBlack is not a function. (In 'navigation.state.params.AddBlack({
noteTitle: noteTitle,
noteValue: noteValue
})', 'navigation.state.params.AddBlack' is undefined)
ViewBlacks.js
import React from 'react'
import { Button, StyleSheet, View, FlatList } from 'react-native'
import { Text, FAB, List } from 'react-native-paper'
import { useSelector, useDispatch } from 'react-redux'
import { addblack, deleteblack } from '../redux/notesApp'
import Header from '../components/Header'
function ViewBlacks({ navigation }) {
const blacks = useSelector(state => state.number)
const dispatch = useDispatch()
const addBlack = black => dispatch(addblack(black))
const deleteBlack = id => dispatch(deleteblack(id))
return (
<>
<Header titleText='Simple Note Taker' />
<Button title="Go back" onPress={() => navigation.goBack()} />
<View style={styles.container}>
{blacks.length === 0 ? (
<View style={styles.titleContainer}>
<Text style={styles.title}>You do not have any notes</Text>
</View>
) : (
<FlatList
data={blacks}
renderItem={({ item }) => (
<List.Item
title={item.black.noteTitle}
description={item.black.noteValue}
descriptionNumberOfLines={1}
titleStyle={styles.listTitle}
onPress={() => deleteBlack(item.id)}
/>
)}
keyExtractor={item => item.id.toString()}
/>
)}
<FAB
style={styles.fab}
small
icon='plus'
label='Add new note'
onPress={() =>
navigation.navigate('AddBlacks', {
addBlack
})
}
/>
</View>
</>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingHorizontal: 10,
paddingVertical: 20
},
titleContainer: {
alignItems: 'center',
justifyContent: 'center',
flex: 1
},
title: {
fontSize: 20
},
fab: {
position: 'absolute',
margin: 20,
right: 0,
bottom: 10
},
listTitle: {
fontSize: 20
}
})
export default ViewBlacks
AddBlack.js
import React, { useState } from 'react'
import { View, StyleSheet } from 'react-native'
import { IconButton, TextInput, FAB } from 'react-native-paper'
import Header from '../components/Header'
function AddBlack({ navigation }) {
const [noteTitle, setNoteTitle] = useState('')
const [noteValue, setNoteValue] = useState('')
function onSaveNote() {
navigation.state.params.addBlack({ noteTitle, noteValue })
navigation.goBack()
}
return (
<>
<Header titleText='Add a new note' />
<IconButton
icon='close'
size={25}
color='white'
onPress={() => navigation.goBack()}
style={styles.iconButton}
/>
<View style={styles.container}>
<TextInput
label='Add Title Here'
value={noteTitle}
mode='outlined'
onChangeText={setNoteTitle}
style={styles.title}
/>
<TextInput
label='Add Note Here'
value={noteValue}
onChangeText={setNoteValue}
mode='flat'
multiline={true}
style={styles.text}
scrollEnabled={true}
returnKeyType='done'
blurOnSubmit={true}
/>
<FAB
style={styles.fab}
small
icon='check'
disabled={noteTitle == '' ? true : false}
onPress={() => onSaveNote()}
/>
</View>
</>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingHorizontal: 20,
paddingVertical: 20
},
iconButton: {
backgroundColor: 'rgba(46, 113, 102, 0.8)',
position: 'absolute',
right: 0,
top: 40,
margin: 10
},
title: {
fontSize: 24,
marginBottom: 20
},
text: {
height: 300,
fontSize: 16
},
fab: {
position: 'absolute',
margin: 20,
right: 0,
bottom: 0
}
})
export default AddBlack
notesReducer.js
import remove from 'lodash.remove'
// Action Types
export const ADD_NOTE = 'ADD_NOTE'
export const DELETE_NOTE = 'DELETE_NOTE'
export const ADD_BLACK = 'ADD_BLACK'
export const DELETE_BLACK = 'DELETE_BLACK'
// Action Creators
let noteID = 0
let blackID = 0
export function addnote(note) {
return {
type: ADD_NOTE,
id: noteID++,
note
}
}
export function deletenote(id) {
return {
type: DELETE_NOTE,
payload: id
}
}
export function addblack(black) {
return {
type: ADD_BLACK,
id: blackID++,
black
}
}
export function deleteblack(id) {
return {
type: DELETE_BLACK,
payload: id
}
}
// reducer
const INITIAL_STATE = {
note: [], // for holds notes
number: [] // for holds numbers
};
function notesReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case ADD_NOTE:
return {
...state,
note: [
...state.note,
{
id: action.id,
note: action.note
}
]
};
case DELETE_NOTE:
const note = remove(state.note, obj => obj.id != action.payload);
return {...state, note};
case ADD_BLACK:
return {
...state,
number: [
...state.number,
{
id: action.id,
number: action.number
}
]
};
case DELETE_BLACK:
const number = remove(state.number, obj => obj.id != action.payload);
return {...state, number}
default:
return state
}
}
export default notesReducer
I am a beginner with react-native/redux, could not find a simple to use example of how to use an api call to post data in a redux app.So I tried doing mySelf. For backend I'm using express,nodejs,mongodb. If possible then give some reference of example.
type.js
export const POST_BOOKS_SUCCESS = "POST_BOOKS_SUCCESS"
BookAction.js
import {
POST_BOOKS_SUCCESS
} from "./types";
export const postBooksSuccess = ({ prop, value}) => ({
type:POST_BOOKS_SUCCESS,
payload:{ prop, value}
});
export const createBooks = ({title,description}) => {
console.log(title,description)
return (dispatch) => {
fetch("http://localhost:3000/api/Books",{
method:"POST",
headers:{
"content-type":"application/json"
},
body:JSON.stringify({title,description})
})
.then(res=>res.json())
.then(post => dispatch(postBooksSuccess({title,description})))
}
}
AddBookReducer.js
import {
POST_BOOKS_SUCCESS
} from "../actions/types";
const INITIAL_STATE = {
title:"",
description:""
};
export default (state = INITIAL_STATE, action)=>{
console.log(action);
switch(action.type){
case POST_BOOKS_SUCCESS:
return { ...state, [action.payload.prop]: action.payload.value }
default:
return state;
}
};
Addbook.js
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
KeyboardAvoidingView,
ScrollView,
TextInput,
TouchableOpacity
} from 'react-native';
import { connect } from 'react-redux';
import createBooks from "../redux/actions/BooksActions";
class AddBooks extends Component {
static navigationOptions = ({navigation}) => ({
// title: 'Add books',
headerTintColor: '#ffffff',
headerStyle: {
backgroundColor: '#2F95D6',
borderBottomColor: '#ffffff',
borderBottomWidth: 3,
},
headerTitleStyle: {
fontSize: 18,
},
});
onButtonPress() {
const { title, description } = this.props;
this.props.createBooks({ title, description })
}
render() {
return (
<View style={styles.container}>
<KeyboardAvoidingView
behavior="padding"
style={styles.container}>
<ScrollView>
<View>
<TextInput style={styles.textInput}
placeholder="Title"
placeholderTextColor="black"
underlineColorAndroid="rgba(0,0,0,0)"
onChangeText={text => this.props.createBooks({
prop:"title", value: text })}
value={this.props.title}
/>
</View>
<View>
<TextInput style={styles.textInput}
placeholder="Description"
placeholderTextColor="black"
underlineColorAndroid="rgba(0,0,0,0)"
onChangeText={text => this.props.createBooks({ prop:"description", value: text })}
value={this.props.description}
/>
</View>
<TouchableOpacity
style={styles.button}
onPress={this.onButtonPress.bind(this)}
>
<Text style={styles.buttonText}>
Submit
</Text>
</TouchableOpacity>
</ScrollView>
</KeyboardAvoidingView>
</View>
);
}
}
mapStateToProps = (state) => {
const {title,description} = state.addBookReducers;
return {
title,
description
}
}
export default connect(mapStateToProps,{createBooks})(AddBooks)
const styles = StyleSheet.create({
container:{
backgroundColor:"#EBF5FB",
flex:1
},
textInput:{
marginTop: 10,
fontSize:20,
height:50,
borderColor:"black",
borderWidth: 1,
width:"100%"
},
button:{
width:"100%",
backgroundColor: "#19B5FE",
marginVertical: 10,
paddingVertical: 16,
borderRadius: 30,
},
buttonText:{
fontSize:16,
fontWeight:"500",
textAlign:"center",
color:"black"
},
})
reducer/index.js
import { combineReducers } from "redux";
import BookReducers from "./BooksReducers";
import AddBookReducers from "./AddBookReducers";
export default combineReducers({
bookReducers: BookReducers,
addBookReducers: AddBookReducers
})