I have a question, I'm doing an app with React Native and Redux, similar to what Twitter is. In the Home a timeline of tweets is loaded of which when I play in one of them, I have to show that particular tweet. The problem is that when I click or take a particular tweet I get the following error:
The sequence is as follows (below I show you images): From the home.js file I pass to Timeline.js the data of the tweets, in this file by means of a TouchableOpacity, to select a special tweet, it is executed an action by passing the tweet data to navReducer.js. This file filters the route and passes the data to TweetScreen.js which in turn through getParam I want to pass it to the Tweet.js component. The error seems to be in TweetScreen when I call navigation.state.getParam.
//AppNavigator.js
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createStackNavigator, createMaterialTopTabNavigator } from 'react-navigation';
import {
reduxifyNavigator,
createReactNavigationReduxMiddleware,
} from 'react-navigation-redux-helpers';
import { Ionicons } from '@expo/vector-icons';
import Routes from '../config/routes';
import HomeScreen from '../screens/HomeScreen';
import SearchScreen from '../screens/SearchScreen';
import SettingsScreen from '../screens/SettingsScreen';
import Home from './home/home';
import TweetScreen from '../screens/TweetScreen';
const middleware = createReactNavigationReduxMiddleware(
'root',
state => state.nav
);
const RootNavigator = createMaterialTopTabNavigator({
Home: HomeScreen,
Search: SearchScreen,
Settings: SettingsScreen,
}, {
navigationOptions: ({ navigation }) => ({
tabBarIcon: ({ horizontal, tintColor }) => {
const { routeName } = navigation.state;
let iconName;
switch(routeName) {
case 'Home':
iconName = 'md-home';
break;
case 'Search':
iconName = 'md-search';
break;
case 'Settings':
iconName = 'md-settings';
break;
}
return <Ionicons name={iconName} size={30} color={tintColor} />;
},
}),
tabBarOptions: {
activeTintColor: '#000',
showIcon: true,
showLabel: false,
inactiveTintColor: 'gray',
style: {
backgroundColor: '#fff',
padding:20,
},
iconStyle: {
justifyContent: 'center',
alignItems: 'center',
},
indicatorStyle: {
backgroundColor: '#000',
},
}
});
const TweetStack = createStackNavigator({
Home: {
screen: RootNavigator,
navigationOptions: ({ navigation }) => ({
title: 'Gwitter',
}),
},
Tweet: TweetScreen
});
const AppWithNavigationState = reduxifyNavigator(TweetStack, 'root');
const mapStateToProps = state => ({
state: state.nav,
});
const AppNavigator = connect(mapStateToProps)(AppWithNavigationState);
export { TweetStack, AppNavigator, middleware };
//home.js
'use strict';
import React, { Component } from 'react';
import {
StyleSheet,
FlatList,
View,
ActivityIndicator
} from 'react-native';
import {bindActionCreators} from 'redux';
import { connect } from 'react-redux';
import * as Actions from '../../actions/homeActions'; //Import your actions
import Timeline from './Timeline';
class Home extends Component {
constructor(props) {
super(props);
this.state = {
};
this.renderItem = this.renderItem.bind(this);
}
componentDidMount() {
this.props.getTweetsTimeline(0); //call our action
}
render() {
if (this.props.loading) {
return (
<View style={styles.activityIndicatorContainer}>
<ActivityIndicator animating={true}/>
</View>
);
} else {
return (
<View style={{flex:1, backgroundColor: '#F5F5F5', paddingTop:20}}>
<FlatList
ref='listRef'
data={this.props.data}
onEndReached={this.handleEnd}
onEndReachedThreshold={0}
renderItem={this.renderItem}
keyExtractor={(item, index) => index.toString()}/>
</View>
);
}
}
handleEnd() {
this.props.getTweetsTimeline(this.props.page);
}
renderItem({item}) {
return (
<Timeline
tweet={item}
navigation={this.props.navigation}
/>
)
}
};
// The function takes data from the app current state,
// and insert/links it into the props of our component.
// This function makes Redux know that this component needs to be passed a piece of the state
function mapStateToProps(state, props) {
return {
loading: state.homeReducer.loading,
data: state.homeReducer.data
}
}
// Doing this merges our actions into the component’s props,
// while wrapping them in dispatch() so that they immediately dispatch an Action.
// Just by doing this, we will have access to the actions defined in out actions file (action/home.js)
function mapDispatchToProps(dispatch) {
return bindActionCreators(Actions, dispatch);
}
//Connect everything
export default connect(mapStateToProps, mapDispatchToProps)(Home);
const styles = StyleSheet.create({
activityIndicatorContainer:{
backgroundColor: "#fff",
alignItems: 'center',
justifyContent: 'center',
flex: 1,
},
});
//Timeline.js
'use strict';
import React, { Component } from 'react';
import {
StyleSheet,
FlatList,
View,
Text,
ActivityIndicator,
Image,
TouchableOpacity
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import {StackNavigator} from 'react-navigation';
const Timeline = props => {
let tweetMedia;
//Some cases tweets doesnt have pictures
if(props.tweet.entities.media) {
//If there is a picture save it in tweetMedia, to render it later on
tweetMedia = props.tweet.entities.media.map(picture => {
return (
<Image
source={{uri: picture.media_url}}
key={picture.id}
style={styles.tweetPicture}
/>
)
})
}
return (
<View style={styles.row}>
<View style={styles.userPictureContainer}>
<Image
source={{uri: props.tweet.user.profile_image_url}}
style={styles.userProfileImage}
/>
</View>
<View style={styles.tweetContainer}>
<TouchableOpacity
onPress={() => props.navigation.dispatch({type: 'Tweet', payload: props.tweet})}
>
<View>
<Text style={styles.tweetUsername}>
{props.tweet.user.name} <Text style={styles.tweetUsernameInfo}>@{props.tweet.user.screen_name} </Text>
</Text>
</View>
<View>
<Text>
{props.tweet.text}
</Text>
</View>
</TouchableOpacity>
<View style={styles.tweetMedia}>
{tweetMedia}
</View>
<View style={styles.socialIconsContainer}>
<View style={styles.socialIcon}>
<Ionicons name='md-heart' size={25} color='#000' />
<Text style={styles.socialIconCounter}>{props.tweet.favorite_count}</Text>
</View>
<View style={styles.socialIcon}>
<Ionicons name='md-refresh'size={25} color='#000' />
<Text style={styles.socialIconCounter}>{props.tweet.retweet_count}</Text>
</View>
<View style={styles.socialIcon}>
<Ionicons name='md-send' size={25} color='#000' />
<Text></Text>
</View>
</View>
</View>
</View>
)
}
export default Timeline;