Conditional rendering not working React Native IOS App - react-native

I am a developing an Iphone App using React Native. I want to display table using DataTable. For a User, they have specific devices assigned. I used conditional rendering and getting the table with assigned devices for a user correctly, but when the user has no specific devices I want a message to be displayed as 'No Devices to display'. Below is the code,
ManageDevice.js:
import React, {useEffect} from 'react';
import {View,Text,StyleSheet,ScrollView} from 'react-native';
import { HeaderButtons, Item } from 'react-navigation-header-buttons';
import HeaderButton from '../../components/UI/HeaderButton';
import {useDispatch, useSelector} from "react-redux";
import {DataTable, TextInput} from 'react-native-paper';
import AsyncStorage from '#react-native-community/async-storage';
import * as authActions from "../../store/actions/auth";
const ManageDevice = props =>{
const dispatch = useDispatch();
const devices = useSelector(state => state?.auth?.availableDevice);
useEffect(() => {
const onScreenLoad = async () => {
const useridfordevices = await
AsyncStorage.getItem("userDatauserid");
const obj = JSON.parse(useridfordevices);
const {userid} = obj;
var userid1 = userid[0];
await dispatch(authActions.getDeviceInfo(userid1))
};
onScreenLoad();
}, [dispatch]);
if (devices)
{
return (
<ScrollView showsVerticalScrollIndicator={true}>
<View>
<DataTable>
<DataTable.Header>
<DataTable.Title>
<Text>Target Id</Text>
</DataTable.Title>
<DataTable.Title>
<Text>Target Name</Text>
</DataTable.Title>
</DataTable.Header>
{devices?.map((item, key) => (
<DataTable.Row>
<DataTable.Cell>{item.TargetId}</DataTable.Cell>
<DataTable.Cell><TextInput
editable={true}
value={item.TargetName}
theme={{ colors: { placeholder: "#f5f5f5",
background: "transparent",
text: 'green', primary: '#D43790' } }}>
</TextInput>
</DataTable.Cell>
</DataTable.Row>
)
)}
</DataTable>
<View style={styles.textview}>
<Text style={styles.textstyle}>Click on the Target Name to edit</Text>
</View>
</View>
</ScrollView>
)
}
else if(!devices){
return(
<View><Text>No Devices to display</Text></View>
)
}
}
ManageDevice.navigationOptions = navData =>{
return{
headerTitle: 'Manage Devices',
headerTitleStyle:{
color:'white',
},
headerTitleAlign:"left",
headerStyle: {
backgroundColor: '#0437F2',
},
headerLeft: () =>
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
iconName={'chevron-back-outline'}
onPress={() => {
navData.navigation.navigate('Home');
}}
/>
</HeaderButtons>
}
};
const styles = StyleSheet.create({
textview:{
top:'5%'
},
textstyle:{
fontWeight:'bold',
color:'orange'
}
})
export default ManageDevice;
Can anyone tell where I am going wrong? When 'devices' is empty, I want the 'No Devices to display' message. Thanks in Advance

Do it like this:
if(devices && devices?.length > 0){
return(
//Do Something
)
}
else{
return(
<View>
<Text>No Devices to display</Text>
</View>
)
}
Hope this works for you.

Related

AsyncStorage doesn't save onboarding when closing react native app

Well, I made a presentation onboarding screen, as soon as the user opens the app, the screen is shown, but I want this to be saved using AsyncStorage, if I close and open the app, the onboarding screen is not shown, but the screen of login.
I did all the code but nothing happens and the screen is displayed every time I close and open the app, I don't know what I'm doing wrong, code below.
App.js
import React, { useEffect, useState } from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import {AuthProvider} from './src/providers/Auth';
import AsyncStorage from '#react-native-async-storage/async-storage';
import { OnBoarding } from './src/screen/OnBoarding';
import { SignIn } from './src/screen/SignIn';
import { HomeScreen } from './src/screen/HomeScreen';
import { ActivityIndicator } from 'react-native';
const Stack = createNativeStackNavigator();
const Loading = () => {
return (
<View>
<ActivityIndicator size="large" />
</View>
);
}
export function App(){
const [loading, setLoading] = useState(true);
const [viewedOnboarding, setViewedOnboarding] = useState(false);
useEffect(() => {
checkOnBoarding();
}, [])
const checkOnBoarding = async () => {
try{
const value = await AsyncStorage.getItem('#viewedOnboarding');
if(value !== null){
setViewedOnboarding(true);
}
console.log(value);
}catch(err) {
console.log('Error #checkOnboarding: ', err);
}finally {
setLoading(false)
}
}
return (
<AuthProvider>
<NavigationContainer>
<Stack.Navigator
initialRouteName={loading ? <Loading /> : viewedOnboarding ? <HomeScreen /> : <OnBoarding />}
screenOptions={
{headerShown: false}
}
>
<Stack.Screen
name="SignIn"
component={SignIn}
/>
</Stack.Navigator>
</NavigationContainer>
</AuthProvider>
);
}
Onboarding.js
import React, { useEffect, useState } from 'react';
import {Text, View, StyleSheet, Image, TouchableOpacity, Button} from 'react-native';
import AppIntroSlider from 'react-native-app-intro-slider';
import Icon from 'react-native-vector-icons/FontAwesome';
import AsyncStorage from '#react-native-async-storage/async-storage';
const slides = [
{
key: 1,
title: 'Only Books Can Help You',
text: 'Books can help you to increase your knowledge and become more successfully.',
image: require('../../assets/imagem1.png'),
},
{
key: 2,
title: 'Learn Smartly',
text: 'It’s 2022 and it’s time to learn every quickly and smartly. All books are storage in cloud and you can access all of them from your laptop or PC.',
image: require('../../assets/imagem2.png'),
},
{
key: 3,
title: 'Learn Smartly',
text: 'It’s 2022 and it’s time to learn every quickly and smartly. All books are storage in cloud and you can access all of them from your laptop or PC.',
image: require('../../assets/imagem2.png'),
},
];
export function OnBoarding(){
function renderItem({item}){
return (
<View style={styles.container}>
<Image style={styles.image} source={item.image} />
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.content}>{item.text}</Text>
</View>
);
}
function _renderPrevButton() {
return (
<View style={styles.buttonCircle}>
<Icon
name="angle-left"
color="#000"
size={40}
/>
</View>
);
};
function _renderNextButton() {
return (
<View style={styles.buttonCircle}>
<Icon
name="angle-right"
color="#000"
size={40}
/>
</View>
);
};
const onPressFinish = async () => {
try{
await AsyncStorage.setItem('#viewedOnboarding', 'true')
navigation.navigate('SignIn');
}catch(err) {
console.log("Error #setitem ", err);
}
};
const renderDoneButton = () => {
return (
<TouchableOpacity onPress={onPressFinish}>
<Text style={{color: "#000"}}>Done</Text>
</TouchableOpacity>
);
};
return (
<AppIntroSlider
data={slides}
renderItem={renderItem}
keyExtractor={item => item.key}
renderPrevButton={_renderPrevButton}
renderNextButton={_renderNextButton}
renderDoneButton={renderDoneButton}
showPrevButton
showDoneButton
dotStyle={{backgroundColor: '#9D9D9D'}}
activeDotStyle={{backgroundColor: '#DE7773'}}
/>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: "#fff"
},
title: {
color: "#292B38",
fontSize: 24,
fontWeight: 'bold',
marginTop: 50
},
content: {
color: "#4D506C",
textAlign: 'center',
padding: 25,
lineHeight: 18
},
image: {
width: 300,
height: 300,
},
button: {
color: "#000",
backgroundColor: "transparent"
}
})
Probably because of the async nature of AsyncStore when you open your app first you init viewedOnboarding with false, then start reading storage and then you check is onboarding done or not. And of course because storage is async first time you check in Navigation condition you always get
I have modified your checkOnBoarding function.
Here is the code:
const checkOnBoarding = () => {
try{
AsyncStorage.getItem('#viewedOnboarding').then(value => {
if(value !== null){
setViewedOnboarding(true);
}
console.log(value);
})
}catch(err) {
console.log('Error #checkOnboarding: ', err);
}finally {
setLoading(false)
}
}

Getting undefined device.map error while displaying table react-native-table-component

I am a developing a React Native app.My requirement is to display data from API in the table format.I am using react-native-table-component.I am not able to traverse through an array to display data in the table.Below is my code:
TargetSetUpPage.js:
import React, { useEffect } from "react";
import { StyleSheet, Text, Button, TextInput, ScrollView ,View}
from "react-native";
import { useDispatch,useSelector } from "react-redux";
import * as authActions from "../../store/actions/auth";
import AsyncStorage from '#react-native-
community/asyncstorage';
import { Table, Row, Rows } from 'react-native-table-
component';
const TargetSetUpPage = (props) => {
const [targetid, setTargetId] = React.useState("");
const dispatch = useDispatch();
const devices = [useSelector(state =>
state.auth.availableDevice)];
//console.log('The devices are: '+JSON.stringify(devices));
if (typeof(devices) !== 'undefined' && devices != null) {
console.log('Not Undefined and Not Null')
} else {
console.log('Undefined or Null')
const devices = [useSelector(state =>
state.auth.availableDevice)];
}
const tableHead = ['Target Id','Target Name']
const tableRow = devices;
console.log(devices);
const tableRows = (devices || []).map(item => ({ name:
item.name
}));
useEffect(() => {
const onScreenLoad = async() => {
const useridfordevices = await
AsyncStorage.getItem("userDatauserid");
const obj = JSON.parse(useridfordevices);
const { userid } = obj;
var userid1 = userid[0];
await dispatch(authActions.getDeviceInfo(userid1))
};
onScreenLoad();
},[dispatch]);
return (
<ScrollView showsVerticalScrollIndicator={true}>
<View>
{/* <Table borderStyle={{borderWidth: 2, borderColor:
'#c8e1ff'}}>
<Row data={tableHead} style={styles.head} textStyle=
{styles.text}/>
<Rows data={tableRows} textStyle={styles.text}/>
</Table> */}
<FlatList
data={devices}
keyExtractor={item => item.TargetId}
renderItem={itemData => (
<View style={styles.card}>
{/* <Text style={styles.text}>{itemData.item.TargetName}
</Text> */}
<Button title={itemData.item.TargetName} onPress={()=>{}}/>
</View>
)}
numColumns={2}
/>
<Text style={styles.headingTitle}>
Set your target and start running:
</Text>
<Text style={styles.textstyle}>Target ID</Text>
<TextInput
style={styles.input}
value={targetid}
onChangeText={(targetid) => setTargetId(targetid)}
></TextInput>
<Button
title="Add"
// onPress = {() => }
/>
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
input: {
height: 40,
width: "80%",
margin: 12,
borderWidth: 1,
padding: 10,
},
headingTitle: {
fontSize: 30,
},
textstyle: {
paddingTop: 10,
fontSize: 20,
},
compact: {
flexDirection: "row",
},});
export default TargetSetUpPage;
After executing it is showing the below tableRow array in console:
Not Undefined and Not Null
Array [
undefined,
]
TypeError: undefined is not an object (evaluating 'item.name')
But If I remove Table and display FlatList,I am able to display the list without any problem .I dont know what mistake I am doing in displaying the Table.Thanks in Advance.
// devices will be the same as auth.availableDevice in your redux
// If auth.availableDevice is undefined at any point, like during boot
// then devices will be undefined too
const devices = useSelector(state => state.auth.availableDevice);
// Let's write our map code so that it handles the case where devices is undefined
const tableRows = (devices || []).map(item => ({ name: item.name }));
I used DataTable from react-native-paper to get the table.Below is the code that renders data from API.
TargetSetUpPage.js:
import React, {useEffect} from "react";
import {StyleSheet, Text, Button, TextInput, ScrollView, View, FlatList}
from "react-native";
import {useDispatch, useSelector} from "react-redux";
import * as authActions from "../../store/actions/auth";
import AsyncStorage from '#react-native-community/async-storage';
import {DataTable} from 'react-native-paper';
const TargetSetUpPage = (props) => {
const [targetid, setTargetId] = React.useState("");
const dispatch = useDispatch();
const devices = useSelector(state => state.auth.availableDevice);
useEffect(() => {
const onScreenLoad = async () => {
const useridfordevices = await
AsyncStorage.getItem("userDatauserid");
const obj = JSON.parse(useridfordevices);
const {userid} = obj;
var userid1 = userid[0];
await dispatch(authActions.getDeviceInfo(userid1))
};
onScreenLoad();
}, [dispatch]);
return (
<ScrollView showsVerticalScrollIndicator={true}>
<View>
<DataTable>
<DataTable.Header>
<DataTable.Title>
<Text>Target Id</Text>
</DataTable.Title>
<DataTable.Title>
<Text>Target Name</Text>
</DataTable.Title>
</DataTable.Header>
{devices.map((item, key) => (
<DataTable.Row>
<DataTable.Cell>{item.TargetId}</DataTable.Cell>
<DataTable.Cell>{item.TargetName}</DataTable.Cell>
</DataTable.Row>
)
)}
</DataTable>
<Text style={styles.headingTitle}>
Set your target and start running:
</Text>
<Text style={styles.textstyle}>Target ID</Text>
<TextInput
style={styles.input}
value={targetid}
onChangeText={(targetid) => setTargetId(targetid)}
></TextInput>
<Button title="Add"
// onPress = {() => }
/>
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
input: {
height: 40,
width: "80%",
margin: 12,
borderWidth: 1,
padding: 10,
},
headingTitle: {
fontSize: 30,
},
textstyle: {
paddingTop: 10,
fontSize: 20,
},
compact: {
flexDirection: "row",
},
});
export default TargetSetUpPage;
which displays the table on the screen as shown below.

Add a loader for authentication flow on React Native Expo

I just added the loader that is loaded when the user sends the request to login by clicking the button with this code:
import React, { useState } from 'react';
import { Text, TextInput, SafeAreaView, StyleSheet, TouchableOpacity, Image, KeyboardAvoidingView, ActivityIndicator } from 'react-native';
import {AuthContext} from './utils';
// Creating Login Activity.
export function SignInScreen() {
const [isLoading, setLoading] = useState(false);
const [email, setEmail] = React.useState('');
const [password, setPassword] = React.useState('');
const { signIn } = React.useContext(AuthContext);
return (
<KeyboardAvoidingView behavior={Platform.OS == 'ios' ? 'padding' : 'height'} style={styles.keyboard_login}>
<SafeAreaView style={styles.contenitore_login}>
{isLoading == true ? <ActivityIndicator size="large"/> : (
<>
<Image style={styles.logo} source={require('../assets/logo.png')} />
<TextInput
style={styles.campo_login}
placeholder="Email"
value={email}
onChangeText={setEmail}
autoCapitalize='none'
textContentType='emailAddress'
keyboardType='email-address'
/>
<TextInput
style={styles.campo_login}
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<TouchableOpacity style={styles.cta_login} onPress={() => { setLoading(true); signIn({ email, password });}}>
<Text style={styles.testo_cta_login}>Accedi</Text>
</TouchableOpacity>
</>
)}
</SafeAreaView>
</KeyboardAvoidingView>
);
}
Everything works perfectly when the user enters the correct email and password, but when they enter the wrong credentials the loader keeps going.
}).then((response) => response.json()).then((responseJson) => {
// If server response message same as Data Matched
if(responseJson === 'Data Matched'){
dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' });
}else{
Alert.alert(responseJson);
}
}).catch((error) => {
console.error(error);
});
As you can see if the fetch URL says "Data Matched" then it is assigned SIGN_IN which then returns the user to the home of my app, but in the case where the logins don't match I don't know what to do to send the user back to the login page.
What is the navigation code that is used in this case? Thanks!
We can have variable let's say loading
const [loading, setLoading] = useState(false)
And as far I can see your Sigin function connects to db. So do something like
const authContext = React.useMemo(
() => ({
signIn: async data => {
setLoading(true);
// Some code here
setLoading(false);
}
})
)
And then show an Activity indicator or any component you want to show fetching something like this
import React from 'react';
import PropTypes from 'prop-types';
import {View, ActivityIndicator, Modal} from 'react-native';
import styles from './Styles/FullScreenLoaderStyles';
const FullScreenLoader = ({loading}) => (
<Modal transparent={true} animationType={'none'} visible={loading}>
<View style={styles.modalBackground}>
<View style={styles.activityIndicatorWrapper}>
<ActivityIndicator color="#000" animating={loading} />
</View>
</View>
</Modal>
);
FullScreenLoader.propTypes = {
loading: PropTypes.bool,
};
export default FullScreenLoader;
I have updated the loading screen code. Now you can import this in your google signin screen like
import FullScreenLoader from './FullScreenLoader';
// some code
return(
<View>
<FullScreenLoader loading={isLoading} />
</View>
)
FullScreenLoaderStyles.js
import {StyleSheet} from 'react-native';
import colors from '../../Themes/Colors';
export default StyleSheet.create({
modalBackground: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#00000040',
},
activityIndicatorWrapper: {
backgroundColor: colors.grey,
height: 50,
width: 50,
borderRadius: 10,
alignItems: 'center',
justifyContent: 'center',
},
});

Unable to store data in redux store

i am trying to build a react-native app in that user can add a routine or daily task in EditRoutine.js file and it can be seen in RoutineOverviewScreen.js and i am using redux for storing these data and using hooks for storing and fetching data.
Below is the EditRoutine.js code snippet
import React, { useState, useEffect, useCallback } from "react";
import { View, StyleSheet, Text, TextInput, ScrollView } from "react-native";
import { HeaderButtons, Item } from "react-navigation-header-buttons";
import Card from "../components/Card";
import { useSelector, useDispatch } from "react-redux";
import * as routinesActions from "../store/actions/routine";
import Routine from "../models/routine";
import HeaderButton from "../components/HeaderButton";
const EditRoutine = (props) => {
const dispatch = useDispatch();
const [title, setTitle] = useState("");
const [detail, setDetail] = useState("");
const [time, setTime] = useState("");
const submitHandler = useCallback(() => {
dispatch(routinesActions.createRoutine(title, detail, time));
props.navigation.goBack();
}, [dispatch,title, detail, time]);
useEffect(() => {
props.navigation.setParams({ submit: submitHandler });
}, [submitHandler]);
return (
<Card style={styles.container}>
<Text>Title</Text>
<TextInput
style={styles.input}
value={title}
onChangeText={(text) => setTitle(text)}
/>
<Text>Details</Text>
<TextInput
style={styles.input}
multiline
numberOfLines={4}
value={detail}
onChangeText={(text) => setDetail(text)}
/>
<Text>Time</Text>
<TextInput
style={styles.input}
value={time}
onChangeText={(text) => setTime(text)}
/>
</Card>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
padding: 10,
width: "100%",
},
input: {
paddingHorizontal: 2,
borderBottomColor: "#ccc",
borderBottomWidth: 1,
width: "100%",
marginVertical: 15,
},
});
EditRoutine.navigationOptions = (navData) => {
const submitFn = navData.navigation.getParam("submit");
return {
headerTitle: "Edit Routine",
headerTitle: "Your Routines",
headerLeft: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Menu"
iconName={
Platform.OS === "android" ? "md-arrow-back" : "ios-arrow-back"
}
onPress={() => {
navData.navigation.goBack();
}}
/>
</HeaderButtons>
),
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Save"
iconName={
Platform.OS === "android" ? "md-checkmark" : "ios-checkmark"
}
onPress={submitFn}
/>
</HeaderButtons>
),
};
};
export default EditRoutine;
and this is my RoutineOverviewScreen.js file where i am trying to show the created routine
import React from "react";
import { View, StyleSheet, Text, FlatList } from "react-native";
import Card from "../components/Card";
import { HeaderButtons, Item } from "react-navigation-header-buttons";
import HeaderButton from "../components/HeaderButton";
import { useSelector } from "react-redux";
const RoutineOverViewScreen = (props) => {
const routines = useSelector((state) => state.routines.myRoutine);
return (
<FlatList
data={routines}
keyExtractor={(item) => item.id}
renderItem={(itemData) => (
<Card>
<View>
<Text>{itemData.item.id} </Text>
</View>
<View>
<Text>{itemData.item.title} </Text>
</View>
<View>
<Text>{itemData.item.detail} </Text>
<View>
<Text>{itemData.item.time} </Text>
</View>
</View>
</Card>
)}
/>
);
};
const styles = StyleSheet.create({});
RoutineOverViewScreen.navigationOptions = (navData) => {
return {
headerTitle: "Your Routines",
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="Add"
iconName={
Platform.OS === "android" ? "md-add-circle" : "ios-add-circle"
}
onPress={() => {
navData.navigation.navigate("Edit");
}}
/>
</HeaderButtons>
),
};
};
export default RoutineOverViewScreen;
Below is my action file routine.js snippet
export const CREATE_ROUTINE= 'CREATE_ROUTINE';
export const deleteRoutine = routineId => {
return { type: DELETE_ROUTINE, pid: routineId };
};
export const createRoutine = (title, detail, time) => {
return {
type: CREATE_ROUTINE,
routineData: {
title,
detail,
time
}
};
};
Below is my reducer file reducer.js snippet
import {
DELETE_ROUTINE,
CREATE_ROUTINE,
UPDATE_ROUTINE,
} from "../actions/routine";
import Routine from "../../models/routine";
const initialState = {
myRoutine: {},
id: 1,
};
export default (state = initialState, action) => {
switch (action.type) {
case CREATE_ROUTINE:
const newRoutine = new Routine(
state.id,
action.routineData.title,
action.routineData.detail,
action.routineData.time
);
return {
...state,
items: { ...state.items, [state.id]: newRoutine },
id: state.id + 1,
};
default: {
return state;
}
}
return state;
};
and this is my app.js file snippet
import React, { useState } from 'react';
import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import { AppLoading } from 'expo';
import * as Font from 'expo-font';
import routinesReducer from './store/reducers/routine';
import AppNavigator from './navigator/RoutineNavigator';
const rootReducer = combineReducers({
routines: routinesReducer,
});
const store = createStore(rootReducer);
const fetchFonts = () => {
return Font.loadAsync({
'open-sans': require('./assets/fonts/OpenSans-Regular.ttf'),
'open-sans-bold': require('./assets/fonts/OpenSans-Bold.ttf')
});
};
export default function App() {
const [fontLoaded, setFontLoaded] = useState(false);
if (!fontLoaded) {
return (
<AppLoading
startAsync={fetchFonts}
onFinish={() => {
setFontLoaded(true);
}}
/>
);
}
return (
<Provider store={store}>
<AppNavigator />
</Provider>
);
}
with these above code i am able to create a new routine but i am not knowing whether my input are getting stored in to the App central state because when i am trying to render those saved data i am unable to see in my RoutineOverviewScreen screen.please help me and
About Me: Govind Kumar Thakur ( iamgovindthakur )
email: iamgovindthakur#gmail.com
Thank You :)
You are trying to use the data of "myRoutine" but never save data to this property in the reducer.
I think that the issue you are having is due to the following line in your reducer:
items: { ...state.items, [state.id]: newRoutine },

react native stack navigation undefined is not an object (evalutating 'props.navigation')

‌‌I'm making an app in react-native and I would like to navigate to different page by clicking on a button using stack navigation:
Here is my code :
app.js
import React from 'react';
import { AppRegistry } from 'react-native';
import { StackNavigator } from 'react-navigation';
import Home from './Screens/Home';
import VideoListItems from './Screens/VideoListItems';
import TrackPlayer from './Screens/TrackPlayer';
const reactNavigationSample = props => {
return <VideoListItems navigation={props.navigation} />;
};
reactNavigationSample.navigationOptions = {
title: "VideoListItems"
};
const AppNavigator = StackNavigator({
Home: { screen: Home, navigationOptions: { header: null }},
VideoListItems: { screen: VideoListItems, navigationOptions: { header: null }},
TrackPlayer: { screen: TrackPlayer, navigationOptions: { header: null }},
}
);
export default class App extends React.Component {
render() {
return (
<AppNavigator />
);
}
}
VideoListItems where the button to navigate is :
import {StackNavigator} from 'react-navigation';
const VideoListItems = ({ video, props }) => {
const { navigate } = props.navigation;
const {
cardStyle,
imageStyle,
contentStyle,
titleStyle,
channelTitleStyle,
descriptionStyle
} = styles;
const {
title,
channelTitle,
description,
thumbnails: { medium: { url } }
} = video.snippet;
const videoId = video.id.videoId;
return(
<View>
<Card title={null} containerStyle={cardStyle}>
<Image
style={imageStyle}
source= {{ uri: url}}
/>
<View style={contentStyle}>
<Text style={titleStyle}>
{title}
</Text>
<Text style={channelTitleStyle}>
{channelTitle}
</Text>
<Text style={descriptionStyle}>
{description}
</Text>
<Button
raised
title="Save And Play"
icon={{ name: 'play-arrow' }}
containerViewStyle={{ marginTop: 10 }}
backgroundColor="#E62117"
onPress={() => {
navigate('TrackPlayer')
}}
/>
</View>
</Card>
</View>
);
};
export default VideoListItems;
But I'm getting this error :
TypeError: undefined is not an object (evaluating 'props.navigation')
I don't know how to pass the props navigation and make it able to navigate when clicking on the button, I don't know where is my error, any ideas ?
[EDIT]
My new VideoItemList :
const VideoListItems = props => {
const {
cardStyle,
imageStyle,
contentStyle,
titleStyle,
channelTitleStyle,
descriptionStyle
} = styles;
const {
title,
channelTitle,
description,
thumbnails: { medium: { url } }
} = props.video.snippet;
const videoId = props.video.id.videoId;
const { navigate } = props.navigation;
return(
<View>
<Card title={null} containerStyle={cardStyle}>
<Image
style={imageStyle}
source= {{ uri: url}}
/>
<View style={contentStyle}>
<Text style={titleStyle}>
{title}
</Text>
<Text style={channelTitleStyle}>
{channelTitle}
</Text>
<Text style={descriptionStyle}>
{description}
</Text>
<Button
raised
title="Save And Play"
icon={{ name: 'play-arrow' }}
containerViewStyle={{ marginTop: 10 }}
backgroundColor="#E62117"
onPress={() => {
navigate.navigate('TrackPlayer')
}}
/>
</View>
</Card>
</View>
);
};
This is the file where I display all my components :
import React, { Component } from 'react';
import { View } from 'react-native';
import YTSearch from 'youtube-api-search';
import AppHeader from './AppHeader';
import SearchBar from './SearchBar';
import VideoList from './VideoList';
const API_KEY = 'ApiKey';
export default class Home extends Component {
state = {
loading: false,
videos: []
}
componentWillMount(){
this.searchYT('');
}
onPressSearch = term => {
this.searchYT(term);
}
searchYT = term => {
this.setState({ loading: true });
YTSearch({key: API_KEY, term }, videos => {
console.log(videos);
this.setState({
loading: false,
videos
});
});
}
render() {
const { loading, videos } = this.state;
return (
<View style={{ flex: 1, backgroundColor: '#ddd' }}>
<AppHeader headerText="Project Master Sound Control" />
<SearchBar
loading={loading}
onPressSearch={this.onPressSearch} />
<VideoList videos={videos} />
</View>
);
}
}
And my VideoList where I use VideoListItems :
import React from 'react';
import { ScrollView, View } from 'react-native';
import VideoListItems from './VideoListItems';
const VideoList = ({ videos }) => {
const videoItems = videos.map(video =>(
<VideoListItems
key={video.etag}
video={video}
/>
));
return(
<ScrollView>
<View style={styles.containerStyle}>
{videoItems}
</View>
</ScrollView>
);
};
const styles = {
containerStyle: {
marginBottom: 10,
marginLeft: 10,
marginRight: 10
}
}
export default VideoList;
That's because you try to extract navigation from a prop named props (that doesn't exist), you have many ways to solve this problem :
Use the rest operator to group all props except video inside a props variable
const VideoListItems = ({ video, ...props }) => {
Don't destructure you props object
const VideoListItems = props => {
// don't forget to refactor this line
const videoId = props.video.id.videoId;
Extract navigation from props
const VideoListItems = ({ video, navigation }) => {
const { navigate } = navigation;