I'm trying to navigate to another screen (Artist) by clicking on an element in a FlatList. This FlatList contains Artist instances, as set in the _renderListItem method. So far I've tried three different approaches (two of them commented out at the moment), but none of them seem to work:
Method 1: NavigationActions
Method 2: this.props.navigation.navigate
Method 3: Navigator.push
I managed to pass the params to the other screen, but unfortunately the navigation itself doesn't seem to work; I'm getting the expected values in my logs, but nothing happens and the app stays at LinksScreen (doesn't change screens).
LinksScreen.js
import React, { Component } from 'react';
import { PropTypes } from 'prop-types';
import Artist from './Artist';
import { createStackNavigator, withNavigation, NavigationActions } from 'react-navigation';
import {
ScrollView,
StyleSheet,
View,
Text,
Image,
FlatList,
ActivityIndicator,
TouchableOpacity,
TouchableHighlight,
} from 'react-native';
export default class LinksScreen extends React.Component {
constructor(props) {
super(props);
this._onAlertTypePressed = this._onAlertTypePressed.bind(this);
}
_onAlertTypePressed(typeId: string, typeName: string, imageUrl: string) {
console.log(typeId)
console.log(typeName)
console.log(imageUrl)
// NavigationActions
// NavigationActions.navigate({
// routeName: 'Artist',
// params: { itemId: typeId, itemName: typeName, itemImageUrl: imageUrl,},
// });
// NAVIGATE
this.props.navigation.navigate('HomeStack',{},
{
type: "Navigate",
routeName: "Artist",
params: {
itemId: typeId,
itemName: typeName,
itemImageUrl: imageUrl}
}
);
// PUSH
// this.props.navigator.push({
// screen: 'Artist',
// title: 'Artist',
// passProps: {
// itemId: typeId,
// itemName: typeName,
// itemImageUrl: imageUrl,
// },
// });
}
_renderListItem = ({item}) => (
<Artist
itemId={item.id}
itemName={item.title.rendered}
itemImageUrl={
item.better_featured_image
? item.better_featured_image.source_url
: 'http://54.168.73.151/wp-content/uploads/2018/04/brand-logo.jpg'
}
onPressItem={this._onAlertTypePressed}
/>
);
static navigationOptions = {
title: 'Links',
};
state = {
data: [],
isLoading: true,
isError: false,
};
// static propTypes = {
// navigation: PropTypes.shape({
// navigate: PropTypes.func.isRequired,
// }).isRequired,
// };
componentWillMount() {
fetch('http://54.168.73.151/wp-json/wp/v2/pages?parent=38&per_page=100')
.then(response => response.json())
.then(responseJson => {
responseJson.sort(
(a, b) => (a.title.rendered < b.title.rendered ? -1 : 1)
);
this.setState({
data: responseJson,
isLoading: false,
isError: false,
});
})
.catch(error => {
this.setState({
isLoading: false,
isError: true,
});
console.error(error);
});
}
// Not used anymore.
renderRow = item => (
<View style={styles.grid}>
<Image
style={styles.thumb}
source={{
uri: item.better_featured_image
? item.better_featured_image.source_url
: 'http://54.168.73.151/wp-content/uploads/2018/04/brand-logo.jpg',
}}
/>
<Text style={styles.title}>{item.title.rendered}</Text>
</View>
);
getKey = item => String(item.id);
renderComponent() {
if (this.state.isLoading) {
return <ActivityIndicator />;
} else if (this.state.isError) {
return <Text>Error loading data</Text>;
} else {
return (
<FlatList
numColumns={3}
contentContainerStyle={styles.elementsContainer}
data={this.state.data}
renderItem={this._renderListItem}
keyExtractor={this.getKey}
/>
);
}
}
render() {
return (
<View style={styles.container}>
<Text
style={{
fontSize: 20,
color: '#FFFFFF',
marginLeft: 4,
marginTop: 10,
}}>
RESIDENTS
</Text>
{this.renderComponent()}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#000000',
},
elementsContainer: {
backgroundColor: '#000000',
},
grid: {
marginTop: 15,
marginBottom: 15,
marginLeft: 5,
height: 125,
width: 115,
borderBottomWidth: 1,
borderBottomColor: '#191970',
},
title: {
color: '#FFFFFF',
textAlign: 'left',
fontSize: 12,
},
thumb: {
height: 110,
width: 110,
resizeMode: 'cover',
},
});
Artist.js
The console.log in the beginning of the _onPress() method seem to be working (the expected params have the correct values here), but I'm unable to navigate to this screen.
import React, { Component } from 'react';
import { createStackNavigator } from 'react-navigation';
import {
ScrollView,
StyleSheet,
View,
Text,
TouchableOpacity,
Image,
} from 'react-native';
class Artist extends React.PureComponent {
_onPress = () => {
// console.log(this.props)
const itemId = this.props.itemId
const itemName = this.props.itemName
const itemImageUrl = this.props.itemImageUrl
console.log(itemId)
console.log(itemName)
console.log(itemImageUrl)
// FOR PUSH
// this.props.onPressItem(
// this.props.itemid,
// this.props.itemName,
// this.props.itemImageUrl,
// );
// };
}
static navigationOptions = {
title: 'Artist',
};
render() {
return (
<TouchableOpacity
{...this.props}
style={styles.grid}
onPress={this._onPress}>
<Image
style={styles.image}
source={{uri: this.props.itemImageUrl}}
/>
<Text style={styles.title}>{this.props.itemName}</Text>
</TouchableOpacity>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#000000',
},
grid: {
marginTop: 15,
marginBottom: 15,
marginLeft: 5,
height: 125,
width: 115,
borderBottomWidth: 1,
borderBottomColor: '#191970',
},
title: {
color: '#FFFFFF',
textAlign: 'left',
fontSize: 12,
},
image: {
height: 110,
width: 110,
resizeMode: 'cover',
},
});
export default Artist;
MainTabNavigator.js
Perhaps there is something wrong regarding the routing, so here is how it's done in my case.
import React from 'react';
import { Platform, AppRegistry } from 'react-native';
import { createStackNavigator, createBottomTabNavigator } from 'react-navigation';
import TabBarIcon from '../components/TabBarIcon';
import HomeScreen from '../screens/HomeScreen';
import LinksScreen from '../screens/LinksScreen';
import SettingsScreen from '../screens/SettingsScreen';
import Artist from '../screens/Artist';
const HomeStack = createStackNavigator({
Home: {
screen: HomeScreen,
},
Links: {
screen: LinksScreen,
},
Artist: {
screen: Artist,
},
});
AppRegistry.registerComponent('ParamsRepo', () => HomeStack);
export default HomeStack;
Try simplyfying your code like this:
this.props.navigation.navigate('Artist',
{
itemId: typeId,
itemName: typeName,
itemImageUrl: imageUrl
}
});
Related
here is image
can we make this kind of bottom navigation in react native specially the shape on navigation? can I make this kind of bottom navigation specially the wave on it
You can make your custom bottom bar
Working example: https://snack.expo.io/#msbot01/happy-waffle
(You can adjust code as per required UI)
Example code
import React, { Component } from 'react';
import {View, Text, StyleSheet, Image, TouchableOpacity, Dimensions} from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome5';
import Dashboard from './Dashboard';
import Gift from './Gift';
import Like from './Like';
var width = Dimensions.get('window').width
var tabs = [
{
key: 'home',
icon: 'home',
label: 'Home',
barColor: '#388E3C',
pressColor: 'rgba(255, 255, 255, 0.16)',
badgeCount: 0
},
{
key: 'heart',
icon: 'calendar-check',
label: 'Heart',
barColor: '#B71C1C',
pressColor: 'rgba(255, 255, 255, 0.16)',
badgeCount: 2
},
{
key: 'location',
icon: 'users',
label: 'Location',
barColor: '#E64A19',
pressColor: 'rgba(255, 255, 255, 0.16)',
badgeCount: 2
}
]
export default class SupervisorDashboard extends Component<Props> {
constructor(props) {
super(props);
this.state = {
userData: global.userData,
selectedTab: 'home',
}
}
componentDidMount(){
}
dateSelected(e){
this.setState({
selectedDate: e
})
}
homePressed(){
console.log('Home____')
this.setState({
selectedTab: 'home'
})
}
heartPressed(){
console.log('heart____')
this.setState({
selectedTab: 'heart'
})
}
locationPressed(){
console.log('Location____')
this.setState({
selectedTab: 'location'
})
}
renderScene(){
switch(this.state.selectedTab) {
case ('home'):{
return <Dashboard/>
}
break;
case ('heart'):{
return <Like/>
}
break;
case ('location'):{
return <Gift/>
}
break;
}
}
renderMenuIcons(){
var tempArray = []
for (var i =0; i < tabs.length; i++) {
var a = tabs[i].key
console.log(a)
var eachElement;
switch(tabs[i].key) {
case ('home'):{
console.log('I am in home++++++++++')
eachElement = <TouchableOpacity key={i} style={{alignItems:'center', justifyContent:'center'}} onPress={()=>{this.homePressed()}}>
<Icon name = {tabs[i].icon} size={20} light color={this.state.selectedTab==tabs[i].key? 'red' : 'grey'}/>
</TouchableOpacity>
}
break;
case ('heart'):{
console.log('I am in approvals+++++++++++')
eachElement = <TouchableOpacity key={i} style={{alignItems:'center', justifyContent:'center', marginBottom:30, backgroundColor:'green', height:50, width:50, borderRadius:30}} onPress={()=>{this.heartPressed()}}>
<Icon name={tabs[i].icon} size={25} light color={'white'}/>
</TouchableOpacity>
}
break;
case ('location'):{
console.log('I am in location+++++++++++++')
eachElement = <TouchableOpacity key={i} style={{alignItems:'center', justifyContent:'center'}} onPress={()=>{this.locationPressed()}}>
<Icon name={tabs[i].icon} size={20} light color={this.state.selectedTab==tabs[i].key? 'red' : 'grey'}/>
</TouchableOpacity>
}
break;
default:{
console.log('+++++++++++++++++'+tabs[i].key)
}
}
tempArray.push(eachElement)
}
return tempArray;
}
render(){
return(
<View style={{flex:1, backgroundColor: global.backGroundColor}}>
{this.renderScene()}
<View style={styles.oval}>
</View>
<View style={{width:'90%', alignItems:'center', height:70, flexDirection:'row', justifyContent:'space-around', marginLeft:'5%', marginRight:'5%', position:'absolute', bottom:10, borderRadius:30, borderColor: global.borderColor, backgroundColor:global.backGroundColor}}>
{this.renderMenuIcons()}
</View>
</View>
);
}
}
const styles = StyleSheet.create({
background: {
backgroundColor: 'red'
},
oval: {
width: (0.78*width),
height: (0.78*width),
borderRadius: 150,
//backgroundColor: 'red',
borderWidth:1,
borderColor:'#e4e4e4',
transform: [
{scaleX: 1.5}
],
marginLeft:45,
marginTop:150,
position:'absolute',
bottom:-200
}
});
So, I'm trying to add the user avatar and username to my addPost. I've tried everything I can think of but nothing is working.
I've Tried;
firebase.auth().currentUser.avatar;
But that doesn't work, I'm assuming it's because "avatar" or "username" isn't firebase specific properties for auth.
I've also tried;
get uid() {
return firebase.auth().currentUser;
}
and then adding to addPost
avatar: this.uid.avatar,
username: this.uid.username
That doesn't work either.
I just need the user avatar and username to be available when called on the home screen.
import React from "react";
import { View, StyleSheet, FlatList, Platform, TouchableNativeFeedback, TouchableOpacity, Image, Button } from "react-native";
import Fire from '../../Fire';
import UsernameText from '../../components/Text/DefaultUsernameText';
import CreatedAtText from '../../components/Text/CreatedAtText';
import ThoughtTitleText from '../../components/Text/DefaultThoughtTitle';
import Colors from '../../constants/Colors';
import moment from 'moment';
let TouchableCmp = TouchableOpacity;
if (Platform.OS === 'android' && Platform.Version >= 21) {
TouchableCmp = TouchableNativeFeedback;
}
export default class HomeScreen extends React.Component {
state = {
latestPost: [],
}
displayLatestPost = (latestPost) => {
this.setState({latestPost: latestPost});
console.log("latest Post " + this.state.latestPost);
}
componentDidMount(){
Fire.shared.getPosts(this.displayLatestPost);
console.log("This is the displayLatestPost " + this.state.latestPost);
}
renderLatestPost = (post) => {
return (
<View>
<TouchableCmp onPress={() => {}} style={{flex: 1}}>
<View style={styles.container}>
<View style={styles.infoText}>
<Image style={styles.userAvatar}>{post.avatar}</Image>
<UsernameText style={styles.name} >{post.username}</UsernameText>
<CreatedAtText style={styles.timestamp}>{moment(post.timestamp).fromNow()}</CreatedAtText>
</View>
<View style={styles.container} >
<ThoughtTitleText style={styles.feedItem}>{post.thoughtTitle}</ThoughtTitleText>
</View>
</View>
</TouchableCmp>
</View>
);
};
render() {
return (
<View style={styles.container}>
<Button
onPress={() => {
Fire.shared.signOut();
}}
title="Log out"
/>
<FlatList
showsVerticalScrollIndicator={false}
keyExtractor={item => item.id}
style={styles.feed}
data={this.state.latestPost}
renderItem={( {item, index }) =>
this.renderLatestPost(item, index)
}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.background
},
feed: {
marginHorizontal: 16
},
feedItem: {
borderRadius: 5,
padding: 2,
flexDirection: "row",
marginVertical: 2
},
name: {
fontSize: 15,
fontWeight: "500",
color: "#454D65"
},
timestamp: {
fontSize: 11,
color: "#C4C6CE",
marginTop: 4
},
post: {
marginTop: 16,
fontSize: 14,
color: "#838899"
},
postImage: {
width: undefined,
height: 150,
borderRadius: 5,
marginVertical: 16
},
container: {
flex:1,
borderRadius: 10,
padding:15,
justifyContent: 'flex-end',
alignItems: 'flex-end'
},
thought: {
color: '#666',
fontSize: 18,
marginBottom: 5,
alignItems: 'center'
},
infoText:{
flexDirection: "row"
},
// icons: {
// flexDirection: 'row'
// },
userAvatar: {
backgroundColor: Colors.subheadings,
borderColor: Colors.accent,
borderWidth:3.5,
backgroundColor: Colors.maintext,
marginEnd: 15,
width: 35,
height: 35,
borderRadius: 20,
}
});
Here's my Fire.js
import FirebaseKeys from "./config/FirebaseKeys";
import firebase from "firebase/app";
import '#firebase/auth';
import 'firebase/database';
import '#firebase/firestore';
import "firebase/storage";
require("firebase/firestore");
class Fire {
constructor() {
firebase.initializeApp(FirebaseKeys);
}
getPosts = async (displayLatestPost) => {
const post = await
this.firestore.collection('thoughts').orderBy('timestamp', ' desc').limit(10).get()
let postArray =[]
post.forEach((post) => {
postArray.push({id: post.id, ...post.data()})
})
displayLatestPost(postArray)
}
addPost = async ({ thoughtTitle, thoughtText, localUri, avatar, username}) => {
const remoteUri = await this.uploadPhotoAsync(localUri, `photos/${this.uid}/${Date.now()}`);
return new Promise((res, rej) => {
this.firestore
.collection("thoughts")
.add({
uid: this.uid,
thoughtTitle,
thoughtText,
image: remoteUri,
timestamp: this.timestamp,
})
.then(ref => {
res(ref);
})
.catch(error => {
rej(error);
});
});
};
uploadPhotoAsync = (uri, filename) => {
return new Promise(async (res, rej) => {
const response = await fetch(uri);
const file = await response.blob();
let upload = firebase
.storage()
.ref(filename)
.put(file);
upload.on(
"state_changed",
snapshot => {},
err => {
rej(err);
},
async () => {
const url = await upload.snapshot.ref.getDownloadURL();
res(url);
}
);
});
};
createUser = async user => {
let remoteUri = null;
try {
await firebase.auth().createUserWithEmailAndPassword(user.email, user.password);
let db = this.firestore.collection("users").doc(this.uid);
db.set({
username: user.username,
email: user.email,
avatar: null
});
if (user.avatar) {
remoteUri = await this.uploadPhotoAsync(user.avatar, `avatars/${this.uid}`);
db.set({ avatar: remoteUri }, { merge: true });
}
} catch (error) {
alert("Error: ", error);
}
};
updateProfile = async user => {
let remoteUri = null;
try {
let db =
this.firestore.collection("users").doc(this.uid);
db.update({
username: user.username,
email: user.email,
avatar: null
});
if (user.avatar) {
remoteUri = await this.uploadPhotoAsync(user.avatar, `avatars/${this.uid}`);
db.set({ avatar: remoteUri }, { merge: true });
}
} catch (error) {
alert("Error: ", error);
}
}
signOut = () => {
firebase.auth().signOut();
};
get firestore() {
return firebase.firestore();
}
/* get username(){
return firebase.auth().currentUser.username;
} */
get uid() {
return (firebase.auth().currentUser || {}).uid;
}
/* get avatar(){
return firebase.auth().currentUser.avatar;
} */
get timestamp() {
return Date.now();
}
}
Fire.shared = new Fire();
export default Fire;
I hope the following information helps.
You can store Name & Profile Picture URL like this:
var user = firebase.auth().currentUser;
// Update User Profile
user.updateProfile({
displayName: "Jane Doe",
photoURL: "https://example.com/1hdfabSesfE/profile.jpg"
}).then(function() {
// Update successful.
}).catch(function(error) {
// An error happened.
});
// Use these parameters inside the add post function
var profilePicture = user.photoURL;
var name = user.displayName;
So, I changed my createUser and updateUser
createUser = async user => {
let remoteUri = null;
try {
await firebase.auth().createUserWithEmailAndPassword(user.email, user.password);
let db = this.firestore.collection("users").doc(this.uid);
db.set({
displayName: user.displayName,
email: user.email,
photoURL: null
});
if (user.photoURL) {
remoteUri = await this.uploadPhotoAsync(user.photoURL, `avatars/${this.uid}`);
db.set({ photoURL: remoteUri }, { merge: true });
}
} catch (error) {
alert("Error: ", error);
}
};
updateProfile = async user => {
let remoteUri = null;
try {
let db = this.firestore.collection("users").doc(this.uid);
db.update({
displayName: user.displayName,
photoURL: user.photoURL
});
if (user.photoURL) {
remoteUri = await this.uploadPhotoAsync(user.photoURL, `avatars/${this.uid}`);
db.set({ photoURL: remoteUri }, { merge: true });
}
} catch (error) {
alert("Error: ", error);
}
}
Then I changed my addPost
addPost = async ({ thoughtTitle, thoughtText, localUri, photoURL, displayName}) => {
const remoteUri = await this.uploadPhotoAsync(localUri, `photos/${this.uid}/${Date.now()}`);
return new Promise((res, rej) => {
this.firestore
.collection("thoughts")
.add({
uid: this.uid,
displayName,
photoURL,
thoughtTitle,
thoughtText,
image: remoteUri,
timestamp: this.timestamp,
})
.then(ref => {
res(ref);
})
.catch(error => {
rej(error);
});
});
};
Thanks for all the help - It's working!.
Here's my account screen where I update the displayName and photoURL
import React from "react";
import { View, Text, StyleSheet, Button, Image, TextInput, TouchableOpacity} from
"react-native";
import Fire from "../../Fire";
import { MaterialIcons } from "#expo/vector-icons"
import * as ImagePicker from "expo-image-picker";
import UserPermissions from "../../utilities/UserPermissions";
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
//import Avatar from '../../components/User/Avatar';
export default class AccountScreen extends React.Component {
state = {
user: {},
updatedUser: {
photoURL: null,
displayName: ""
}
};
unsubscribe = null;
componentDidMount() {
const user = this.props.uid || Fire.shared.uid;
this.unsubscribe = Fire.shared.firestore
.collection("users")
.doc(user)
.onSnapshot(doc => {
this.setState({ user: doc.data() });
});
}
componentWillUnmount() {
this.unsubscribe();
}
handleUpdate = () => {
Fire.shared.updateProfile(this.state.updatedUser);
};
handlePickAvatar = async () => {
UserPermissions.getCameraPermission();
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [1, 1],
quality: 0.1
});
if (!result.cancelled) {
this.setState({ updatedUser: { ...this.state.updatedUser, photoURL: result.uri
} });
}
};
render() {
return (
<View style={styles.container}>
<KeyboardAwareScrollView
style={{ flex: 1, width: '100%' }}
keyboardShouldPersistTaps="always">
<View style={styles.container}>
<TouchableOpacity style={styles.avatarContainer} onPress=.
{this.handlePickAvatar}>
<Image
source={
this.state.updatedUser.photoURL
? { uri: this.state.user.photoURL }
: require("../../assets/tempAvatar.jpg")
}
style={styles.avatar}
/>
<MaterialIcons
name="photo-camera"
size={40} color="grey"
style={{ marginTop: 6, marginLeft: 2 }}
/>
</TouchableOpacity>
<View>
<TextInput
style={styles.border}
placeholder= "change username"
onChangeText={displayName => this.setState({ updatedUser: { ...this.state.updatedUser, displayName } })}
value={this.state.updatedUser.displayName}
></TextInput>
</View>
<TouchableOpacity onPress={this.handleUpdate}>
<MaterialIcons name="check" size={24} color="black" />
</TouchableOpacity>
</View>
</KeyboardAwareScrollView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center'
},
profile: {
marginTop: 64,
alignItems: "center"
},
avatarContainer: {
shadowColor: "#151734",
shadowRadius: 30,
shadowOpacity: 0.4
},
avatar: {
width: 100,
height: 100,
borderRadius: 68
},
name: {
marginTop: 24,
fontSize: 16,
fontWeight: "600"
},
statsContainer: {
flexDirection: "row",
justifyContent: "space-between",
margin: 32
},
stat: {
alignItems: "center",
flex: 1
},
statAmount: {
color: "#4F566D",
fontSize: 18,
fontWeight: "300"
},
statTitle: {
color: "#C3C5CD",
fontSize: 12,
fontWeight: "500",
marginTop: 4
},
border: {
width: 200,
margin: 10,
padding: 15,
fontSize: 16,
borderColor: '#d3d3d3',
borderBottomWidth: 1,
textAlign: 'center'
},
});
Here's my Home.js
import React from "react";
import { View, StyleSheet, FlatList, Platform,
TouchableNativeFeedback,
TouchableOpacity, Image, Button } from "react-native";
import Fire from '../../Fire';
import UsernameText from '
../../components/Text/DefaultUsernameText';
import CreatedAtText from '../../components/Text/CreatedAtText';
import ThoughtTitleText from '
../../components/Text/DefaultThoughtTitle';
import Colors from '../../constants/Colors';
import moment from 'moment';
let TouchableCmp = TouchableOpacity;
if (Platform.OS === 'android' && Platform.Version >= 21) {
TouchableCmp = TouchableNativeFeedback;
}
export default class HomeScreen extends React.Component {
state = {
latestPost: [],
}
displayLatestPost = (latestPost) => {
this.setState({latestPost: latestPost});
console.log("latest Post " + this.state.latestPost);
}
componentDidMount(){
Fire.shared.getPosts(this.displayLatestPost);
console.log("This is the displayLatestPost " + this.state.latestPost);
}
renderLatestPost = (post) => {
return (
<View>
<TouchableCmp onPress={() => {}} style={{flex: 1}}>
<View style={styles.container}>
<View style={styles.infoText}>
<Image style={styles.userAvatar} source={{uri:
post.photoURL}} />
<UsernameText style={styles.name} >{post.displayName}.
</UsernameText>
<CreatedAtText style={styles.timestamp}>.
{moment(post.timestamp).fromNow()}</CreatedAtText>
</View>
<View style={styles.container} >
<ThoughtTitleText style={styles.feedItem}>{post.thoughtTitle}.
</ThoughtTitleText>
</View>
</View>
</TouchableCmp>
</View>
);
};
render() {
return (
<View style={styles.container}>
<Button
onPress={() => {
Fire.shared.signOut();
}}
title="Log out"
/>
<FlatList
showsVerticalScrollIndicator={false}
keyExtractor={item => item.id}
style={styles.feed}
data={this.state.latestPost}
renderItem={( {item, index }) =>
this.renderLatestPost(item, index)
}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.background
},
feed: {
marginHorizontal: 16
},
feedItem: {
borderRadius: 5,
padding: 2,
flexDirection: "row",
marginVertical: 2
},
name: {
fontSize: 15,
fontWeight: "500",
color: "#454D65"
},
timestamp: {
fontSize: 11,
color: "#C4C6CE",
marginTop: 4
},
post: {
marginTop: 16,
fontSize: 14,
color: "#838899"
},
postImage: {
width: undefined,
height: 150,
borderRadius: 5,
marginVertical: 16
},
container: {
flex:1,
borderRadius: 10,
padding:15,
justifyContent: 'flex-end',
alignItems: 'flex-end'
},
thought: {
color: '#666',
fontSize: 18,
marginBottom: 5,
alignItems: 'center'
},
infoText:{
flexDirection: "row"
},
// icons: {
// flexDirection: 'row'
// },
userAvatar: {
backgroundColor: Colors.subheadings,
borderColor: Colors.accent,
borderWidth:3.5,
backgroundColor: Colors.maintext,
marginEnd: 15,
width: 35,
height: 35,
borderRadius: 20,
}
});
I am trying to render a View through renderCustomView={this.displayEmojis} for an individual message once a user has activated the onLongPress={} function. However, the view im trying to display repeats for every single message on the screen, rather than the one pressed. I have attached what happens when I try to press on a message with multiple messages showing:
//EventPost
import React from 'react'
import { Platform, View, StatusBar, TouchableOpacity, Text, Image, StyleSheet } from 'react-native'
import { getStatusBarHeight } from 'react-native-status-bar-height';
import { GiftedChat } from 'react-native-gifted-chat'
import emojiUtils from 'emoji-utils'
import KeyboardSpacer from 'react-native-keyboard-spacer';
import SlackMessage from './SlackMessage'
import Keyboard from 'react-native';
export default class EventPosts extends React.Component {
constructor(props) {
super(props);
this.state = {
messages: [],
toggle: false,
reactedMessage: 0,
reacted: false,
images: [
require("../assets/emojis/beer_photo.png"),
require("../assets/emojis/party_photo.png"),
require("../assets/emojis/laughing-emoji_photo.png"),
require("../assets/emojis/happy_photo.png"),
require("../assets/emojis/crying_photo.png"),
],
id: 0,
}
this.displayEmojis = this.displayEmojis.bind(this);
}
componentDidMount() {
this.setState({
messages: [
{
_id: 1,
text: 'I cant wait for this birthday party',
createdAt: new Date(),
user: {
_id: 2,
name: 'Andrew Garrett',
avatar: 'https://placeimg.com/140/140/any',
},
},
],
});
}
onSend(messages = []) {
this.setState(previousState => ({
messages: GiftedChat.append(previousState.messages, messages),
}))
}
renderMessage(props) {
const {
currentMessage: { text: currText },
} = props
let messageTextStyle
if (currText && emojiUtils.isPureEmojiString(currText)) {
messageTextStyle = {
fontSize: 28,
lineHeight: Platform.OS === 'android' ? 34 : 30,
}
}
return (<SlackMessage {...props} messageTextStyle={messageTextStyle} />)
}
dismiss = () =>
Keyboard.dismiss();
updateEmojiCount(number) {
switch (number) {
case 1:
this.setState({ reactedMessage: 0 });
break;
case 2:
this.setState({ reactedMessage: 1 });
break;
case 3:
this.setState({ reactedMessage: 2 });
break;
case 4:
this.setState({ reactedMessage: 3 });
break;
case 5:
this.setState({ reactedMessage: 4 });
break;
default:
this.forceUpdate();
}
this.setState({ reacted: true });
this.setState({ toggle: false })
}
beerEmoji() {
return (
<View>
<TouchableOpacity style={styles.emojiContainer}
onPress={() => this.updateEmojiCount(1)}>
<Image style={styles.emoji} source={require("../assets/emojis/beer.gif")} />
</TouchableOpacity>
</View>
)
}
partyEmoji() {
return (
<View>
<TouchableOpacity style={styles.emojiContainer}
onPress={() => this.updateEmojiCount(2)}>
<Image style={styles.emoji} source={require("../assets/emojis/party.gif")} />
</TouchableOpacity>
</View>
)
}
laughingEmoji() {
return (
<View>
<TouchableOpacity onPress={() => this.updateEmojiCount(3)} style={styles.emojiContainer}>
<Image style={[styles.emoji, styles.emojiResize]} source={require("../assets/emojis/laughing-emoji.gif")} />
</TouchableOpacity>
</View>
)
}
happyEmoji() {
return (
<View>
<TouchableOpacity onPress={() => this.updateEmojiCount(4)} style={styles.emojiContainer}>
<Image style={[styles.emoji, styles.emojiResize]} source={require("../assets/emojis/happy.gif")} />
</TouchableOpacity>
</View>
)
}
cryingEmoji() {
return (
<View>
<TouchableOpacity onPress={() => this.updateEmojiCount(5)} style={styles.emojiContainer}>
<Image style={[styles.emoji, styles.emojiResize]} source={require("../assets/emojis/crying.gif")} />
</TouchableOpacity>
</View>
)
}
displayEmojis() {
if (this.state.toggle) {
return (
<View
style={{
flexDirection: 'row',
right: '35%',
backgroundColor: 'white',
zIndex: 3,
overlayColor: 'white',
borderRadius: 30,
paddingHorizontal: '10%',
bottom: '2%'
}}>
{this.beerEmoji()}
{this.partyEmoji()}
{this.laughingEmoji()}
{this.happyEmoji()}
{this.cryingEmoji()}
</View>
)
}
}
toggleEmojis = (context, message) => {
let temp = this.state.messages.filter(temp => message._id == temp._id);
let id = temp[0]._id;
this.setState({ id: temp[0]._id })
if (this.state.toggle) { this.setState({ toggle: false }) }
else { this.setState({ toggle: true }) }
}
render() {
return (
<View style={{ flex: 1 }}>
<View
style={{ paddingTop: Platform.OS === "android" ? StatusBar.currentHeight : getStatusBarHeight() }}>
<View style={{ backgroundColor: '#2c3e50', paddingVertical: '4%' }}>
<TouchableOpacity style={{ left: "89%", top: '0%' }} onPress={() => this.props.navigation.goBack()}>
<Text style={{ color: 'white' }}>Done</Text>
</TouchableOpacity>
</View>
</View>
<GiftedChat
keyboardShouldPersistTaps={'handled'}
messages={this.state.messages}
onSend={messages => this.onSend(messages)}
placeholder={"Post something..."}
isKeyboardInternallyHandled={false}
multiline={true}
extraData={this.state}
alwaysShowSend={true}
onLongPress={this.toggleEmojis}
user={{ _id: 1 }}
renderCustomView={this.displayEmojis}
renderMessage={this.renderMessage} />
<KeyboardSpacer />
</View>
)
}
}
const styles = StyleSheet.create({
emojiContainer: {
width: 55,
height: 55,
paddingHorizontal: '13%',
right: '15%'
},
emoji: {
width: 55,
height: 55,
borderRadius: 20,
},
emojiResize: {
transform: [{ scale: 0.75 }],
},
emojiReacted: {
transform: [{ scale: 0.4 }],
},
})
//SlackMessage
import PropTypes from 'prop-types';
import React from 'react';
import {
View,
ViewPropTypes,
StyleSheet,
Image
} from 'react-native';
import { Avatar, Day, utils } from 'react-native-gifted-chat';
import Bubble from './SlackBubble';
const { isSameUser, isSameDay } = utils;
export default class Message extends React.Component {
state = {
images: [
require("../assets/emojis/beer_photo.png"),
require("../assets/emojis/party_photo.png"),
require("../assets/emojis/laughing-emoji_photo.png"),
require("../assets/emojis/happy_photo.png"),
require("../assets/emojis/crying_photo.png"),
]
}
getInnerComponentProps() {
const { containerStyle, ...props } = this.props;
return {
...props,
position: 'left',
isSameUser,
isSameDay,
};
}
renderDay() {
if (this.props.currentMessage.createdAt) {
const dayProps = this.getInnerComponentProps();
if (this.props.renderDay) {
return this.props.renderDay(dayProps);
}
return <Day {...dayProps} />;
}
return null;
}
renderBubble() {
const bubbleProps = this.getInnerComponentProps();
if (this.props.renderBubble) {
return this.props.renderBubble(bubbleProps);
}
return <Bubble {...bubbleProps} />;
}
renderAvatar() {
let extraStyle;
if (
isSameUser(this.props.currentMessage, this.props.previousMessage)
&& isSameDay(this.props.currentMessage, this.props.previousMessage)
) {
extraStyle = { height: 0 };
}
const avatarProps = this.getInnerComponentProps();
return (
<Avatar
{...avatarProps}
imageStyle={{ left: [styles.slackAvatar, avatarProps.imageStyle, extraStyle] }}
/>
);
}
render() {
const marginBottom = isSameUser(this.props.currentMessage, this.props.nextMessage) ? 2 : 10;
const { images } = this.state
const index = (this.props.extraData.reactedMessage);
return (
<View>
{this.renderDay()}
<View
style={[
styles.container,
{ marginBottom },
this.props.containerStyle,
]}>
{this.renderAvatar()}
{this.renderBubble()}
{<Image source={images[index]} style={{ width: 20, height: 20, right: '8%' }} />}
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'flex-end',
justifyContent: 'flex-start',
marginLeft: 8,
marginRight: 8,
backgroundColor: 'white',
borderWidth: 2,
borderColor: '#BEBEBE',
paddingBottom: '3%'
},
slackAvatar: {
height: 40,
width: 40,
borderRadius: 3,
left: '10%',
bottom: '12%'
},
});
Message.defaultProps = {
renderAvatar: undefined,
renderBubble: null,
renderDay: null,
currentMessage: {},
nextMessage: {},
previousMessage: {},
user: {},
containerStyle: {},
};
Message.propTypes = {
renderAvatar: PropTypes.func,
renderBubble: PropTypes.func,
renderDay: PropTypes.func,
currentMessage: PropTypes.object,
nextMessage: PropTypes.object,
previousMessage: PropTypes.object,
user: PropTypes.object,
containerStyle: PropTypes.shape({
left: ViewPropTypes.style,
right: ViewPropTypes.style,
}),
};
//Bubble
import PropTypes from 'prop-types';
import React from 'react';
import {
Text,
Clipboard,
StyleSheet,
TouchableOpacity,
View,
ViewPropTypes,
Platform,
} from 'react-native';
import { MessageText, MessageImage, Time, utils } from 'react-native-gifted-chat';
const { isSameUser, isSameDay } = utils;
export default class Bubble extends React.Component {
constructor(props) {
super(props);
this.onLongPress = this.onLongPress.bind(this);
}
onLongPress() {
if (this.props.onLongPress) {
this.props.onLongPress(this.context, this.props.currentMessage);
} else {
if (this.props.currentMessage.text) {
const options = [
'Copy Text',
];
const cancelButtonIndex = options.length - 1;
this.context.actionSheet().showActionSheetWithOptions({
options,
cancelButtonIndex,
},
(buttonIndex) => {
switch (buttonIndex) {
case 0:
Clipboard.setString(this.props.currentMessage.text);
break;
}
});
}
}
}
renderMessageText() {
if (this.props.currentMessage.text) {
const { containerStyle, wrapperStyle, messageTextStyle, ...messageTextProps } = this.props;
if (this.props.renderMessageText) {
return this.props.renderMessageText(messageTextProps);
}
return (
<MessageText
{...messageTextProps}
textStyle={{
left: [styles.standardFont, styles.slackMessageText, messageTextProps.textStyle, messageTextStyle],
}}
/>
);
}
return null;
}
renderMessageImage() {
if (this.props.currentMessage.image) {
const { containerStyle, wrapperStyle, ...messageImageProps } = this.props;
if (this.props.renderMessageImage) {
return this.props.renderMessageImage(messageImageProps);
}
return <MessageImage {...messageImageProps} imageStyle={[styles.slackImage, messageImageProps.imageStyle]} />;
}
return null;
}
renderTicks() {
const { currentMessage } = this.props;
if (this.props.renderTicks) {
return this.props.renderTicks(currentMessage);
}
if (currentMessage.user._id !== this.props.user._id) {
return null;
}
if (currentMessage.sent || currentMessage.received) {
return (
<View style={[styles.headerItem, styles.tickView]}>
{currentMessage.sent && <Text style={[styles.standardFont, styles.tick, this.props.tickStyle]}>✓</Text>}
{currentMessage.received && <Text style={[styles.standardFont, styles.tick, this.props.tickStyle]}>✓</Text>}
</View>
);
}
return null;
}
renderUsername() {
const username = this.props.currentMessage.user.name;
if (username) {
const { containerStyle, wrapperStyle, ...usernameProps } = this.props;
if (this.props.renderUsername) {
return this.props.renderUsername(usernameProps);
}
return (
<Text style={[styles.standardFont, styles.headerItem, styles.username, this.props.usernameStyle]}>
{username}
</Text>
);
}
return null;
}
renderTime() {
if (this.props.currentMessage.createdAt) {
const { containerStyle, wrapperStyle, ...timeProps } = this.props;
if (this.props.renderTime) {
return this.props.renderTime(timeProps);
}
return (
<Time
{...timeProps}
containerStyle={{ left: [styles.timeContainer] }}
textStyle={{ left: [styles.standardFont, styles.headerItem, styles.time, timeProps.textStyle] }}
/>
);
}
return null;
}
renderCustomView() {
if (this.props.renderCustomView) {
return this.props.renderCustomView(this.props);
}
return null;
}
render() {
const isSameThread = isSameUser(this.props.currentMessage, this.props.previousMessage)
&& isSameDay(this.props.currentMessage, this.props.previousMessage);
const messageHeader = isSameThread ? null : (
<View style={styles.headerView}>
{this.renderUsername()}
{this.renderTime()}
{this.renderTicks()}
</View>
);
return (
<View style={[styles.container, this.props.containerStyle]}>
<TouchableOpacity
onLongPress={this.onLongPress}
accessibilityTraits="text"
{...this.props.touchableProps}
>
<View
style={[
styles.wrapper,
this.props.wrapperStyle,
]}
>
<View>
{this.renderCustomView()}
{messageHeader}
{this.renderMessageImage()}
{this.renderMessageText()}
</View>
</View>
</TouchableOpacity>
</View>
);
}
}
// Note: Everything is forced to be "left" positioned with this component.
// The "right" position is only used in the default Bubble.
const styles = StyleSheet.create({
standardFont: {
fontSize: 15,
},
slackMessageText: {
marginLeft: 0,
marginRight: 0,
},
container: {
flex: 1,
alignItems: 'flex-start',
},
wrapper: {
marginRight: 60,
minHeight: 20,
justifyContent: 'flex-end',
},
username: {
fontWeight: 'bold',
},
time: {
textAlign: 'left',
fontSize: 12,
},
timeContainer: {
marginLeft: 0,
marginRight: 0,
marginBottom: 0,
},
headerItem: {
marginRight: 10,
},
headerView: {
// Try to align it better with the avatar on Android.
marginTop: Platform.OS === 'android' ? -2 : 0,
flexDirection: 'row',
alignItems: 'baseline',
},
/* eslint-disable react-native/no-color-literals */
tick: {
backgroundColor: 'transparent',
color: 'white',
},
/* eslint-enable react-native/no-color-literals */
tickView: {
flexDirection: 'row',
},
slackImage: {
borderRadius: 3,
marginLeft: 0,
marginRight: 0,
},
});
Bubble.contextTypes = {
actionSheet: PropTypes.func,
};
Bubble.defaultProps = {
touchableProps: {},
onLongPress: null,
renderMessageImage: null,
renderMessageText: null,
renderCustomView: null,
renderTime: null,
currentMessage: {
text: null,
createdAt: null,
image: null,
},
nextMessage: {},
previousMessage: {},
containerStyle: {},
wrapperStyle: {},
tickStyle: {},
containerToNextStyle: {},
containerToPreviousStyle: {},
};
Bubble.propTypes = {
touchableProps: PropTypes.object,
onLongPress: PropTypes.func,
renderMessageImage: PropTypes.func,
renderMessageText: PropTypes.func,
renderCustomView: PropTypes.func,
renderUsername: PropTypes.func,
renderTime: PropTypes.func,
renderTicks: PropTypes.func,
currentMessage: PropTypes.object,
nextMessage: PropTypes.object,
previousMessage: PropTypes.object,
user: PropTypes.object,
containerStyle: PropTypes.shape({
left: ViewPropTypes.style,
right: ViewPropTypes.style,
}),
wrapperStyle: PropTypes.shape({
left: ViewPropTypes.style,
right: ViewPropTypes.style,
}),
messageTextStyle: Text.propTypes.style,
usernameStyle: Text.propTypes.style,
tickStyle: Text.propTypes.style,
containerToNextStyle: PropTypes.shape({
left: ViewPropTypes.style,
right: ViewPropTypes.style,
}),
containerToPreviousStyle: PropTypes.shape({
left: ViewPropTypes.style,
right: ViewPropTypes.style,
}),
};
Goal: create an OptionFan button that when pressed, rotates on its Z axis, and FanItems release from behind the main button and travel along their own respective vectors.
OptionFan.js:
import React, { useState, useEffect } from 'react';
import { Image, View, Animated, StyleSheet, TouchableOpacity, Dimensions } from 'react-native';
import EStyleSheet from 'react-native-extended-stylesheet';
import FanItem from './FanItem';
const { height, width } = Dimensions.get('window');
export default class OptionFan extends React.Component {
constructor (props) {
super(props);
this.state = {
animatedRotate: new Animated.Value(0),
expanded: false
};
}
handlePress = () => {
if (this.state.expanded) {
// button is opened
Animated.spring(this.state.animatedRotate, {
toValue: 0
}).start();
this.refs.option.collapse();
this.setState({ expanded: !this.state.expanded });
} else {
// button is collapsed
Animated.spring(this.state.animatedRotate, {
toValue: 1
}).start();
this.refs.option.expand();
this.setState({ expanded: !this.state.expanded });
}
};
render () {
const animatedRotation = this.state.animatedRotate.interpolate({
inputRange: [ 0, 0.5, 1 ],
outputRange: [ '0deg', '90deg', '180deg' ]
});
return (
<View>
<View style={{ position: 'absolute', left: 2, top: 2 }}>
{this.props.options.map((item, index) => (
<FanItem ref={'option'} icon={item.icon} onPress={item.onPress} index={index} />
))}
</View>
<TouchableOpacity style={styles.container} onPress={() => this.handlePress()}>
<Animated.Image
resizeMode={'contain'}
source={require('./src/assets/img/arrow-up.png')}
style={{ transform: [ { rotateZ: animatedRotation } ], ...styles.icon }}
/>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center',
borderRadius: 30,
backgroundColor: '#E06363',
elevation: 15,
shadowOffset: {
height: 3,
width: 3
},
shadowColor: '#333',
shadowOpacity: 0.5,
shadowRadius: 5,
height: width * 0.155,
width: width * 0.155
},
icon: {
height: width * 0.06,
width: width * 0.06
},
optContainer: {
justifyContent: 'center',
alignItems: 'center',
borderRadius: 30,
backgroundColor: '#219F75',
elevation: 5,
shadowOffset: {
height: 3,
width: 3
},
shadowColor: '#333',
shadowOpacity: 0.5,
shadowRadius: 5,
height: width * 0.13,
width: width * 0.13,
position: 'absolute'
}
});
FanItem.js:
import React, { useState } from 'react';
import { Image, Animated, StyleSheet, TouchableOpacity, Dimensions } from 'react-native';
import EStyleSheet from 'react-native-extended-stylesheet';
const { width } = Dimensions.get('window');
export default class FanItem extends React.Component {
constructor (props) {
super(props);
this.state = {
animatedOffset: new Animated.ValueXY(0),
animatedOpacity: new Animated.Value(0)
};
}
expand () {
let offset = { x: 0, y: 0 };
switch (this.props.index) {
case 0:
offset = { x: -50, y: 20 };
break;
case 1:
offset = { x: -20, y: 50 };
break;
case 2:
offset = { x: 20, y: 50 };
break;
case 3:
offset = { x: 75, y: -20 };
break;
}
Animated.parallel([
Animated.spring(this.state.animatedOffset, { toValue: offset }),
Animated.timing(this.state.animatedOpacity, { toValue: 1, duration: 600 })
]).start();
}
collapse () {
Animated.parallel([
Animated.spring(this.state.animatedOffset, { toValue: 0 }),
Animated.timing(this.state.animatedOpacity, { toValue: 0, duration: 600 })
]).start();
}
render () {
return (
<Animated.View
style={
(this.props.style,
{
left: this.state.animatedOffset.x,
top: this.state.animatedOffset.y,
opacity: this.state.animatedOpacity
})
}
>
<TouchableOpacity style={styles.container} onPress={this.props.onPress}>
<Image resizeMode={'contain'} source={this.props.icon} style={styles.icon} />
</TouchableOpacity>
</Animated.View>
);
}
}
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center',
borderRadius: 30,
backgroundColor: '#219F75',
elevation: 5,
shadowOffset: {
height: 3,
width: 3
},
shadowColor: '#333',
shadowOpacity: 0.5,
shadowRadius: 5,
height: width * 0.13,
width: width * 0.13,
position: 'absolute'
},
icon: {
height: width * 0.08,
width: width * 0.08
}
});
Implementation:
import React from 'react';
import { StyleSheet, View, Dimensions } from 'react-native';
import Component from './Component';
const { height, width } = Dimensions.get('window');
const testArr = [
{
icon: require('./src/assets/img/chat.png'),
onPress: () => alert('start chat')
},
{
icon: require('./src/assets/img/white_video.png'),
onPress: () => alert('video chat')
},
{
icon: require('./src/assets/img/white_voice.png'),
onPress: () => alert('voice chat')
},
{
icon: require('./src/assets/img/camera.png'),
onPress: () => alert('request selfie')
}
];
const App = () => {
return (
<View style={styles.screen}>
<Component options={testArr} />
</View>
);
};
const styles = StyleSheet.create({
screen: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#E6E6E6'
}
});
export default App;
Problem: The issue is, only the last FanItem item runs its animation. (opacity, and vector translation). before implementing the opacity animation I could tell the first three FanItems did in fact render behind the main button, because I could see them when pressing the main button, as the opacity temporarily changes for the duration of the button click.
My question is 1) why are the first three mapped items not animating? and 2) how to resolve this?
You are storing ref of FanItem in option. but, ref gets overridden in each iteration of map. so, at the end it only stores ref of last FanItem in option. So, first declare one array in constructor to store ref of each FanItem:
constructor(props) {
super(props);
// your other code
this.refOptions = [];
}
Store ref of each FanItem separately like this:
{this.props.options.map((item, index) => (
<FanItem ref={(ref) => this.refOptions[index] = ref} icon={item.icon} onPress={item.onPress} index={index} />
))}
and then to animate each FanItem:
for(var i = 0; i < this.refOptions.length; i++){
this.refOptions[i].expand(); //call 'expand' or 'collapse' as required
}
This is expo snack link for your reference:
https://snack.expo.io/BygobuL3JL
This is my only component:
import React from 'react';
import { View, ListView, StyleSheet, Text } from 'react-native';
import Row from './row'
import TimerMixin from 'react-timer-mixin';
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 20,
},
});
class ListViewDemo extends React.Component {
updateState(){
console.log("stae updation")
}
componentDidMount() {
console.log("mount");
//timer.setTimeout(this, this.updateState, 1000);
}
componentWillUnmount() {
console.log("unmount");
//timer.clearTimeout(this);
}
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
let data = [{
"name": {
"symbol": "Hello",
"tick": 1
}},
{
"name": {
"symbol": "World",
"tick": 0
}}]
this.state = {
dataSource: ds.cloneWithRows(data),
};
}
render() {
return (
<ListView
style={styles.container}
dataSource={this.state.dataSource}
renderRow={(data) => <Row {...data} />}
/>
);
}
}
export default ListViewDemo;
Now, I want to update each of the row's ticker by incrementing it by 1 inside a timer every second. Couple of issues I'm facing is binding a function that can update the state, and getting a mixin library working for es6. Any help, please?
This is a working example where info is updated in every row after pressing Start and that uses TimerMixin.
I used some extra vars on state to make this work quickly.
It might be possible to extract data directly from dataSource (without using the updatedData var) but I haven't discovered that yet.
import React from 'react';
import { View, ListView, StyleSheet, Text, TouchableHighlight } from 'react-native';
import TimerMixin from 'react-timer-mixin';
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 20,
},
button: {
justifyContent: 'center',
alignItems: 'center',
borderWidth: 1,
borderRadius: 5,
padding: 5,
borderColor: 'black',
marginTop: 10,
height: 40
},
buttonText: {
flex: 1,
alignSelf: 'center',
fontSize: 20
}
});
class ListViewDemo extends React.Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
let data = [{
"name": {
"symbol": "Hello",
"tick": 1
}},
{
"name": {
"symbol": "World",
"tick": 0
}}]
this.state = {
timerStarted: false,
updatedData: data,
dataSource: ds.cloneWithRows(data),
};
}
onTimerStart() {
if (this.state.timerStarted) {
this.setState({timerStarted: false});
return TimerMixin.clearInterval(this.state.timer);
}
var timer = TimerMixin.setInterval(
() => {
data = [{
"name": {
"symbol": "Hello",
"tick": this.state.updatedData[0].name.tick+1
}},
{
"name": {
"symbol": "World",
"tick": this.state.updatedData[1].name.tick+1
}}]
this.setState({
timerStarted: true,
updatedData: data,
dataSource: this.state.dataSource.cloneWithRows(data)
})
}, 1000
)
this.setState({timer: timer});
}
render() {
return (
<View>
<ListView
dataSource={this.state.dataSource}
renderRow={(rowData) => <Text> {rowData.name.symbol} {rowData.name.tick}</Text> }
/>
<TouchableHighlight style={styles.button}
underlayColor={'gray'}
onPress={this.onTimerStart.bind(this)}>
<Text style={styles.buttonText}>Start</Text>
</TouchableHighlight>
</View>
);
}
}
export default ListViewDemo;