How to avoid data duplicate when navigate screen? - react-native

My situation is i have two FlatList component (A and B).
A is my first screen. I use react-navigation to navigate A to B , B will show back arrow on headerLeft. When i click the arrow it will back to A . But the FlatList data is still show B even it is really in A...
My data is from fetch API by react-redux, i think the problem is come from react-redux. Because i test a simple test without react-redux. The problem is gone.
I want to use react-redux create my project. I try to use shouldComponentUpdate like
shouldComponentUpdate = (nextProps, nextState) => {
if (nextProps.movieList === this.props.movieList) {
return false;
}
return true;
};
It is still can't fix my problem when goBack() to another component
I console.log it try to find what is going on with my props data.
When i navigate to B from A. My console.log will show like this, i find A component will be rendered...
Then i click the back arrow on headerLeft to A. The screen is A but the data is still B add my console.log is empty at the same time.
I can't figure it out. Any help would be appreciated. Thanks in advance.
Here is my A component file (B is similar with A):
import React, { Component } from 'react';
import {
View, FlatList, Dimensions,
TouchableOpacity, Image,
ActivityIndicator, Alert, Platform
} from 'react-native';
import { Icon } from 'react-native-elements';
import { connect } from 'react-redux';
import { fetchMainMovieList } from '../actions';
const { width, height } = Dimensions.get('window');
const equalWidth = (width / 2);
class MainActivity extends Component {
static navigationOptions = ({ navigation }) => ({
title: 'MainActivity',
headerLeft:
<TouchableOpacity style={{ marginLeft: 10 }} onPress={() => navigation.navigate('DrawerOpen')} >
<Icon name='menu' />
</TouchableOpacity>
});
componentWillMount() {
this.props.fetchMainMovieList();
}
renderItem({ item }) {
return (
<View>
<Image
source={{ uri: item.photoHref }}
style={{ height: 220, width: equalWidth }}
resizeMode="cover"
/>
</View>
);
}
render() {
const movieData = this.props.movieList.movie;
console.log('A component this.props=>');
console.log(this.props);
if (movieData === []) {
return (
<View style={styles.loadingStyle}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{ flex: 1 }}>
<FlatList
data={movieData}
renderItem={this.renderItem}
numColumns={2}
horizontal={false}
keyExtractor={(item, index) => index}
/>
</View>
);
}
}
const styles = {
loadingStyle: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
}
};
const mapStateToProps = (state) => {
const movieList = state.movieList;
return { movieList };
};
export default connect(mapStateToProps, { fetchMainMovieList })(MainActivity);
Here is my B component file:
import React, { Component } from 'react';
import {
View, FlatList, Dimensions,
Image, ActivityIndicator, Text
} from 'react-native';
import { connect } from 'react-redux';
import { fetchThisWeek } from '../actions';
const { width, height } = Dimensions.get('window');
const equalWidth = (width / 2);
class ThisWeek extends Component {
static navigationOptions = ({ navigation }) => ({
title: 'ThisWeek',
});
componentWillMount() {
this.props.fetchThisWeek();
}
renderItem({ item }) {
return (
<View>
<Image
source={{ uri: item.photoHref }}
style={{ height: 500, width: '100%' }}
resizeMode="cover"
/>
</View>
);
}
render() {
const movieData = this.props.movieList.movie;
console.log('B component this.props=>');
console.log(this.props);
if (movieData === []) {
return (
<View style={styles.loadingStyle}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{ flex: 1 }}>
<FlatList
data={movieData}
renderItem={this.renderItem}
numColumns={1}
horizontal={false}
keyExtractor={(item, index) => index}
/>
</View>
);
}
}
const styles = {
loadingStyle: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
}
};
const mapStateToProps = (state) => {
const movieList = state.movieList;
return { movieList };
};
export default connect(mapStateToProps, { fetchThisWeek })(ThisWeek);
Here is my MyListReducer.js:
import {
MOVIELIST_MAINACTIVITY,
MOVIELIST_THISWEEK,
MOVIELIST_THEATER
} from '../actions/types';
const INITIAL_STATE = {};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case MOVIELIST_MAINACTIVITY:
return action.payload;
case MOVIELIST_THISWEEK:
return action.payload;
case MOVIELIST_THEATER:
console.log(action.payload);
return action.payload;
default:
return state;
}
};

In your reducer you have added the fetched data into the main object in store, instead, you should have to maintain two different variables to save data of those different components separately. Try by changing the reducer as,
import {
MOVIELIST_MAINACTIVITY,
MOVIELIST_THISWEEK,
MOVIELIST_THEATER
} from '../actions/types';
const INITIAL_STATE = {
weeklyMovies:[],
allMovies:[]
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case MOVIELIST_MAINACTIVITY:
return {
...state,
allMovies:action.payload
};
case MOVIELIST_THISWEEK:
return {
...state,
weeklyMovies:action.payload
};
case MOVIELIST_THEATER:
console.log(action.payload);
return action.payload;
default:
return {...state};
}
};
And in your component A and B you should change your mapStateToProps to read data from corresponding objects in store.
For MainActivity component
const mapStateToProps = (state) => {
const movieList = state.allMovies;
return { movieList };
};
and for ThisWeek component
const mapStateToProps = (state) => {
const movieList = state.weeklyMovies;
return { movieList };
};

Related

Customize Action Sheet Message (Expo)

I'm using #expo/react-native-action-sheet, and i want to show a button in the props message.
e.g
import { useActionSheet } from "#expo/react-native-action-sheet"
const { showActionSheetWithOptions } = useActionSheet()
const onPress = () => {
// Here
**const message = '<TouchableOpacity><Text>fsdf</Text></TouchableOpacity>'**
showActionSheetWithOptions(
{
message
},
(buttonIndex) => {
}
)
}
But it is not showing the button as i want
My purpose is to add a date picker in the action sheet.
Expecting answer:
In this case, you can use another library https://gorhom.github.io/react-native-bottom-sheet/ because Action Sheet is about the list of actions.
You can place any content you need for react-native-bottom-sheet and it also supports Expo
import React, { useCallback, useMemo, useRef } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import BottomSheet from '#gorhom/bottom-sheet';
const App = () => {
// ref
const bottomSheetRef = useRef<BottomSheet>(null);
// variables
const snapPoints = useMemo(() => ['25%', '50%'], []);
// callbacks
const handleSheetChanges = useCallback((index: number) => {
console.log('handleSheetChanges', index);
}, []);
// renders
return (
<View style={styles.container}>
<BottomSheet
ref={bottomSheetRef}
index={1}
snapPoints={snapPoints}
onChange={handleSheetChanges}
>
<View style={styles.contentContainer}>
<Text>Awesome πŸŽ‰</Text>
</View>
</BottomSheet>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 24,
backgroundColor: 'grey',
},
contentContainer: {
flex: 1,
alignItems: 'center',
},
});
export default App;

onPress not working in React Native Flatlist

My onPress handler is not working when someone clicks on Flatlist item.
Video of this issue
https://u.pcloud.link/publink/show?code=XZWGOUkZmDLPeKQOQJJzxnqFB8Q21X3acT7k
Here is the code:
import React, { useState, useEffect } from 'react';
import { View, Text, Image, FlatList, ActivityIndicator } from 'react-native';
import { TouchableNativeFeedback } from 'react-native-gesture-handler';
import axios from 'axios';
export default function _AjaxApp() {
const [postList, setPostList] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [isLoading, setIsLoading] = useState(false);
const loadData = (append = false) => {
let url = "https://edristi.in/wp-json/wp/v2/posts?per_page=20&page=" + currentPage;
setIsLoading(true);
setCurrentPage(currentPage + 1);
axios.get(url).then((r) => {
if (append) {
setPostList(postList.concat(r.data));
} else {
setPostList(r.data);
}
setIsLoading(false);
}).catch((e) => {
console.log(e);
});
}
useEffect(() => {
loadData();
}, [])
let Loader = <></>
if (isLoading) {
Loader = <ActivityIndicator></ActivityIndicator>
}
return (
<View>
<View style={{padding:20, backgroundColor:"#4342fe"}}>
<Text style={{color:"white"}}>Edristi App</Text>
</View>
<FlatList
data={postList}
renderItem={({ item, index, separators }) => <PostCard postList={postList} {...item} index={index} />}
keyExtractor={r => r.id + "-" + Math.random().toString()}
removeClippedSubviews={true}
maxToRenderPerBatch={2}
ListFooterComponent={Loader}
onEndReachedThreshold={0.5}
onEndReached={() => {
loadData(true);
}}
/>
</View>
);
}
class PostCard extends React.PureComponent {
onPressHandler() {
console.log("Clicked");
alert("Clicked");
}
render() {
let image = <></>
if (this.props.jetpack_featured_media_url.trim() !== "") {
image = <Image style={{ flex: 1 }} source={{
//uri: this.props.featuredimage,
uri: this.props.jetpack_featured_media_url,
}} />
}
// console.log(this.props.jetpack_featured_media_url);
return <TouchableNativeFeedback onPress={()=>{
this.onPressHandler();
}}>
<View style={{ margin: 10 }}>
<Text style={{ fontSize: 17, lineHeight: 23, fontWeight: "600" }}>{ this.props.title.rendered}</Text>
</View></TouchableNativeFeedback>
}
}
Try to import 'TouchableNativeFeedback' from 'react-native' instead of 'react-native-gesture-handler'.

Pause video in flatlist when out of view

I have a flatlist showing videos. I want that when a video goes out of the view it should be paused. I am maintaining the pause state in each of the Posts component.
class Posts extends React.PureComponent {
constructor() {
super()
this.state = {
pause: true,
}
return(){
<Video
pause={this.state.pause}
//other props
/>
}
}
I am using react-native-video.
I have tried using onViewableItemsChanged prop of Flatlist but it doesn't change the state.
I tried this .
But it doesn't seem to work for me.
How should I proceed ?
Here is a possible solution using react-native-inviewport. This dependency is only a single index file that contains a component that has a callback when view is in the viewport. It could easily be modified to suit your needs.
I have constructed a very simple app that has a FlatList. The 11th item in the FlatList is a video. That should mean that the video is off the screen when the App renders so the viddeo won't be playing, once the video comes fully into the viewport it should then start playing.
App.js
import * as React from 'react';
import { Text, View, StyleSheet, FlatList } from 'react-native';
import Constants from 'expo-constants';
import VideoPlayer from './VideoPlayer';
export default class App extends React.Component {
state = {
data: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
}
renderItem = ({item, index}) => {
if (index === 10) {
return <VideoPlayer />
} else {
return (
<View style={{height: 100, backgroundColor: '#336699', justifyContent: 'center', alignItems: 'center'}}>
<Text>{index}</Text>
</View>
)
}
}
keyExtractor = (item, index) => `${index}`;
render() {
return (
<View style={styles.container}>
<FlatList
data={this.state.data}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
}
});
VideoPlayer.js
This is a component that contains the Video component. The video is wrapped in the InViewPort component that has a callback function. The callback returns true when the component it surrounds is completely in the viewport and false when it is not fully in the viewport. The callback calls this.handlePlaying which in turn calls either this.playVideo or this.pauseVideo depending on the boolean value.
import React, {Component} from 'react';
import { View, StyleSheet } from 'react-native';
import { Video } from 'expo-av';
import InViewPort from './InViewPort';
//import InViewPort from 'react-native-inviewport; // this wouldn't work in the snack so I just copied the file and added it manually.
export default class VideoPlayer extends React.Component {
pauseVideo = () => {
if(this.video) {
this.video.pauseAsync();
}
}
playVideo = () => {
if(this.video) {
this.video.playAsync();
}
}
handlePlaying = (isVisible) => {
isVisible ? this.playVideo() : this.pauseVideo();
}
render() {
return (
<View style={styles.container}>
<InViewPort onChange={this.handlePlaying}>
<Video
ref={ref => {this.video = ref}}
source={{ uri: 'http://d23dyxeqlo5psv.cloudfront.net/big_buck_bunny.mp4' }}
rate={1.0}
volume={1.0}
isMuted={false}
resizeMode="cover"
shouldPlay
style={{ width: 300, height: 300 }}
/>
</InViewPort>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center'
}
});
Here is a snack showing it working https://snack.expo.io/#andypandy/video-only-playing-when-in-viewport
I should point out that if the video is not fully in the viewport then it will not play. I am sure some tweaking could be done to react-native-inviewport so that it would play the video if it was partially in the viewport if that is what you wanted, perhaps by passing the height of the video to the InViewPort component.
here is how i simply did the trick
inside my card component that have video
<Video ...
paused={currentIndex !== currentVisibleIndex}
/>
both currentIndex and currentVisibleIndex are passed the component from the FlatList parent
my FlatList pass the renderItem index as currentIndex
<FlatList
data={[...]}
renderItem={({ item, index }) => (
<GalleryCard
{...item}
currentIndex={index}
currentVisibleIndex={currentVisibleIndex}
/>
)}
onViewableItemsChanged={this.onViewableItemsChanged}
viewabilityConfig={{
viewAreaCoveragePercentThreshold: 90
}}
finally my this is how to calculate currentVisibleIndex
please make sure to read viewabilityConfig
onViewableItemsChanged = ({ viewableItems, changed }) => {
if (viewableItems && viewableItems.length > 0) {
this.setState({ currentVisibleIndex: viewableItems[0].index });
}
};
please let me know if this is helpful
I finally did this using redux. Not sure whether it is the right way.
Home.js
_renderItem = ({item, index}) => <Posts item={item} />
viewableItemsChanged = (props) => {
let {changed} = props;
let changedPostArray = [];
changed.map((v,i) => {
let {isViewable, item} = v;
if(!isViewable) {
let {post_id, type} = item;
if(type === 1)
changedPostArray.push(post_id);
}
});
if(changedPostArray.length != 0)
this.props.sendPostToPause(changedPostArray);
}
render() {
return(
<View style={{flex: 1}} >
<FlatList
ref={(ref) => this.homeList = ref}
refreshing={this.state.refreshing}
onRefresh={async () => {
await this.setState({refreshing: true, postsId: [0], radiusId: 0, followingId: 0});
this.getPosts();
}}
onEndReached={async () => {
if(!this.state.isEnd) {
await this.setState({isPaginate: true})
this.getPosts()
}
}}
onEndReachedThreshold={0.2}
removeClippedSubviews={true}
contentContainerStyle={{paddingBottom: 80}}
data={this.state.followersRes}
renderItem={this._renderItem}
viewabilityConfig={this.viewabilityConfig}
onViewableItemsChanged={this.viewableItemsChanged}
/>
</View>
<Footer callback={this.scrollToTopAndRefresh} />
</View>
)
}
export default connect(null, {sendPostToPause})(Home);
Posts.js
class Posts extends React.PureComponent {
constructor(){
super()
this.state: {
pause: false
}
}
componentDidUpdate(prevProps) {
if(prevProps != this.props) {
this.props.postIdArray.map((v) => {
if(v === prevProps.item.post_id) {
this.setState({pause: true})
}
})
}
}
render(){
return(
<Video
pause={this.state.pause}
//Other props
/>
)
}
}
const mapStateToProps = (state) => {
const {postId} = state.reducers;
return {
postIdArray: postId
}
}
export default connect(mapStateToProps, {sendPostToPause})(withNavigation(Posts));
Whenever the viewableItemsChanged is trigger I am adding the changed posts id in an array and calling the action with the array of post ids.
In the Posts component I am checking if the post ids match, if so I am setting the pause state to true.

How to send the state of a Text Input variable to a reducer with dispatch and display it on another screen?

I want the state of my variable (with which it is given a value from a textInput) is sent to a reducer and change the state of that reducer by the state of the variable that I sent, so that way I can show it in different screens using mapStateToProps and I get it globally.
Is there any way to do that? I researched and found examples but not the way I want to do it.
I clarify my code is just an example so that you understand what I want to do, do not take it as a guide as I do not know if it works that way
I show you some of my code to give you an idea of ​​what I
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
TextInput,
TouchableOpacity
} from "react-native";
import { connect } from 'react-redux';
class ScreenHome extends Component {
static navigationOptions = {
header: null
}
constructor(props) {
super(props);
this.state = {
Texto: '',
}
}
render() {
this.props.ChangeState({type: 'ACTION_TYPE', Texto: this.state.Texto});
const { navigation } = this.props;
return (
<View style={styles.container}>
<TextInput
placeholder="Enter Text"
value={this.state.Texto}
onChangeText={Texto => this.setState({ Texto })}
/>
<View style={{ marginBottom: 10, marginTop: 10, backgroundColor: 'black', padding: 10 }}>
<TouchableOpacity onPress={this.props.ChangeState}>
<Text style={{ color: 'white' }}>Send Text Input status to the reducer</Text>
</TouchableOpacity>
</View>
<View>
<TouchableOpacity style={{ backgroundColor: 'blue', padding: 10 }} onPress={() => { navigation.navigate('Other') }}>
<Text style={{ color: '#fff' }}>Go</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
const mapStateToProps = (state) => {
return {
// prop: state.prop
}
}
const mapDispatchToProps = (dispatch) => {
return {
ChangeState: () => {
// dispatch({ type: 'ACTION_TYPE', Texto: this.state.Texto });
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ScreenHome)
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}
});
OTHER SCREEN:
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
TouchableOpacity
} from "react-native";
import { connect } from 'react-redux';
class ScreenOther extends Component {
static navigationOptions = {
header: null
}
render() {
const { navigation } = this.props;
return (
<View style={styles.container}>
<Text>{this.props.StateInitial}</Text>
<TouchableOpacity onPress={() => { navigation.navigate('Home') }}>
<Text>Go back</Text>
</TouchableOpacity>
</View>
);
}
}
const mapStateToProps = (state) => {
return {
StateInitial: state.reducerText
}
}
const mapDispatchToProps = (dispatch) => {
return {
// ChangeState: () => {
// dispatch({type: 'CHANGE_TEXT'})
// }
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ScreenOther)
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}
});
Store
import { createStore, combineReducers } from 'redux';
const reducerText = (state = [0], action) => {
switch (action.type) {
case 'ACTION_TYPE':
return {...state, Texto:action.Texto};
default:
return state;
}
};
const Reducers = combineReducers({
reducerText
})
const Store = createStore(Reducers)
export default Store;
dispatch1 should be visible in your props in the homescreen. So if you do
this.props.dispatch1({type: 'YOUR_ACTION_TYPE', Text: this.state.Text});
Your reducer function will be called where you can do:
reducer: (state, action) => {
switch (action.type) {
case 'YOUR_ACTION_TYPE':
return {...state, Text:action.Text};
}
}
Then in the other screen you should get the changed Text prop.
For those who look at this post and want to do something similar, I mean send the status of the textInput variable to a reducer and ask for the status from another screen with redux feel free to see the code that I will leave below since I was investigating and I got it after a while.
This is the code of redux-form
import React, { Component } from "react";
import {
View,
TextInput,
StyleSheet,
Button,
Text
} from "react-native";
import { Field, reduxForm } from 'redux-form';
import { connect } from 'react-redux';
const ScreenFormHome = (props) => (
<View>
<Field name="Text" component={fieldNombre} ph="Enter Text" />
<Button title="Send Dispatch" onPress={props.handleSubmit((values) => props.SendDispatch(values))} />
</View>
);
const fieldNombre = (props) => (
<View style={styles.textInput}>
<TextInput
placeholder={props.ph}
onChangeText={props.input.onChange}
value={props.input.value}
autoCapitalize="none"
onBlur={props.input.onBlur}
/>
<View style={styles.linea} />
</View>
);
const styles = StyleSheet.create({
textInput: {
marginBottom: 16,
},
linea: {
backgroundColor: '#DCDCDC',
height: 2,
},
});
const mapDispatchToProps = (dispatch) => {
return {
SendDispatch: (values) => {
dispatch({ type: 'ACTION_TYPE', Text: values.Text })
}
}
}
const mapStateToProps = (state) => {
return {
// StateInitial: state.reducerText
}
}
export default reduxForm({
form: 'ScreenFormHome',
})(connect(mapStateToProps, mapDispatchToProps)(ScreenFormHome));
and this is the component code
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
TouchableOpacity
} from "react-native";
import ScreenFormHome from "./ScreenFormHome";
class ScreenHome extends Component {
static navigationOptions = {
header: null
}
render() {
const { navigation } = this.props;
return (
<View style={styles.container}>
<TouchableOpacity style={{ backgroundColor: 'blue', padding: 10, marginBottom: 10 }} onPress={() => { navigation.navigate('Other') }}>
<Text style={{ color: '#fff' }}>Go</Text>
</TouchableOpacity>
<ScreenFormHome />
</View>
);
}
}
export default ScreenHome;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}
});
This is the Store code
import { createStore, combineReducers } from 'redux';
import { reducer as form } from 'redux-form'
const reducerText = (state = [], action) => {
switch (action.type) {
case 'ACTION_TYPE':
return (action.Text)
default:
return state;
}
};
const Reducers = combineReducers({
reducerText,
form
})
const Store = createStore(Reducers)
export default Store;

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;