I have created 5 tabs using createMaterialTopTabNavigator and implemented search bar on each of tabs. However, if I am switching between tabs, components will persist the data and did not refresh. Our componentWillUnmount() method is not invoked.
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
SafeAreaView,
FlatList,
TouchableOpacity,
TextInput
} from "react-native";
import SegmentedControlTab from "react-native-segmented-control-tab";
let context;
import MachineInformationCard from './MachineInformationCard';
export default class AllMachine extends Component {
constructor(props) {
super(props);
context = this;
this.state = {
selectedIndex: 0,
machineData: [],
tempMachineData: []
}
}
componentDidMount = () => {
const fetchData = [{
'Status': 'ON',
'MachineName': '1',
'OperatorName': 'Abhishek',
'Target': 500,
'Achieved': 200,
'Health': '60%',
'Shift': 1,
'Date': '29-10-2019'
},
{
'Status': 'Maintenance',
'MachineName': '12',
'OperatorName': 'Vicky',
'Target': 600,
'Achieved': 100,
'Health': '20%',
'Shift': 2,
'Date': '30-10-2019'
},
{
'Status': 'OFF',
'MachineName': '3',
'OperatorName': 'Prasad',
'Target': 700,
'Achieved': 700,
'Health': '80%',
'Shift': 3,
'Date': '28-10-2019'
},
{
'Status': 'OFF',
'MachineName': '34',
'OperatorName': 'Prasad',
'Target': 700,
'Achieved': 700,
'Health': '80%',
'Shift': 3,
'Date': '28-10-2019'
}];
this.setState({
machineData: fetchData,
tempMachineData: fetchData,
})
}
getSearchValue = (data) => {
const filteredMachineNames = this.state.tempMachineData.filter(
macName => {
let machineNameLowercase = macName.MachineName.toLowerCase()
let searchTermLowercase = data.toLowerCase()
return machineNameLowercase.indexOf(searchTermLowercase) > -1;
}
)
this.setState({
machineData: filteredMachineNames
})
}
_renderItem({ item, index }) {
return (
<TouchableOpacity
onPress={() => context.props.navigation.navigate('MachineProfile')}>
<MachineInformationCard {...item} />
</TouchableOpacity>
)
}
render() {
return (
<SafeAreaView style={styles.container}>
<View style={{
backgroundColor: '#E1E1E1', borderRadius: 30,
flexDirection: 'row', marginTop: 5
}}>
<TextInput style={{
color: 'black', fontSize: 17, height: 40, padding: 10
}}
placeholder={'Search Machines'}
placeholderTextColor={'grey'}
onChangeText={(value) => this.getSearchValue(value)} />
</View>
<FlatList
data={this.state.machineData}
renderItem={this._renderItem}
keyExtractor={({ id }, index) => id} />
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
CardItem: {
flex: 1,
flexDirection: 'row',
height: 120,
backgroundColor: '#e7e7e7',
borderRadius: 5,
overflow: 'hidden',
margin: 5,
shadowColor: 'black',
shadowOpacity: 0.8,
elevation: 8
}
});
[[screen 1 with search][when I switch to ON tab and come back to All machine, I want All machine to be display entire list instead of filtered list]][same]
components will not be unmounted in a tabbed layout. you must use navigation events to determine if the screen comes into or goes out of focus - documentation
example -
const willFocusSubscription = this.props.navigation.addListener(
'willFocus',
payload => {
// callback when component comes into view
console.debug('willFocus', payload);
}
);
// Remove the listener when you are done (usually in componentWillUnmount)
// willFocusSubscription.remove();
events -
willFocus - the screen will focus
didFocus - the screen focused (if there was a transition, the transition completed)
willBlur - the screen will be unfocused
didBlur - the screen unfocused (if there was a transition, the transition completed)
Related
I am looking for assistance on how to replace a default profile picture with the one a user would select from their media library.
I have managed to create an onPress function that allows the user to select the image from their media library. The image is returned and displayed also in the prescribed layout.
My problem is that I cannot see the default profile picture, but I can see and click on a pencil icon to prompt the image-picker for iOS and Android as I am using Expo.
Here is my custom component code:
import React, { useState, useEffect } from "react";
import {
StyleSheet,
View,
Text,
Image,
TouchableOpacity,
useWindowDimensions,
Platform,
} from "react-native";
import { Controller } from "react-hook-form";
import * as ImagePicker from "expo-image-picker";
import { Ionicons } from "#expo/vector-icons";
//import dependencies
import { COLORS, SIZES, images } from "../constants";
const CustomImagePicker = ({ control, name, rules = {} }) => {
const { height } = useWindowDimensions();
const [hasGalleryPermission, setHasGalleryPermission] = useState("false");
const [profilePicture, setProfilePicture] = useState(name);
useEffect(() => {
async () => {
const galleryStatus =
await ImagePicker.requestMediaLibraryPermissionsAsync();
setHasGalleryPermission(galleryStatus.status === "granted");
};
}, []);
const pickImage = async () => {
let chosenImage = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
console.log(JSON.stringify(chosenImage));
if (!chosenImage.cancelled) {
setProfilePicture(chosenImage.uri);
}
};
if (hasGalleryPermission === false) {
return <Text>❌ No access to Internal Storage</Text>;
}
return (
<Controller
name={name}
control={control}
rules={rules}
render={({ field: { onChange, value }, fieldState: { error } }) => (
<>
<View
style={[
styles.container,
{ borderColor: error ? COLORS.red : COLORS.gray },
]}
>
<TouchableOpacity
style={styles.touchPicture}
onPress={() => pickImage()}
>
<Ionicons name="pencil-outline" size={24} color={COLORS.white} />
</TouchableOpacity>
<Image
onChange={onChange}
value={value}
source={{
uri: profilePicture ? profilePicture : images.defaultRounded,
}}
style={[
styles.logo,
styles.profileImage,
{ height: height * 0.19 },
]}
resizeMode={Platform.OS === "android" ? "contain" : "cover"}
/>
</View>
{error && (
<Text
style={{
color: COLORS.red,
alignSelf: "stretch",
fontSize: SIZES.body5,
padding: SIZES.padding - 22,
marginTop: 15,
marginHorizontal: SIZES.padding * 3,
}}
>
{error.message || "❌ Oops, something went wrong!"}
</Text>
)}
</>
)}
/>
);
};
export default CustomImagePicker;
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
touchPicture: {
zIndex: 10,
marginBottom: -50,
marginLeft: 100,
resizeMode: "contain",
},
logo: {
width: Platform.OS == "android" ? 155 : 164,
maxWidth: 300,
maxHeight: 200,
},
profileImage: {
marginTop: SIZES.padding * 2,
borderRadius: 100,
},
});
I have a code that saves and reads the value of a state through AsyncStorage, however when closing the app or changing the screen the value returns to the original state value, which in my case is zero. How do I change the screen or close the app so the value remains the last changed? What alternative to state variables?
My code is:
import React, { Component } from 'react'
import { Text, View, AsyncStorage, TextInput, StyleSheet, TouchableOpacity } from 'react-native'
export default class App extends Component {
constructor(props){
super(props);
this.state = {
txtInputData: '',
getValue: '',
}
}
saveValue = () => {
if(this.state.txtInputData){
AsyncStorage.setItem('key_default', this.state.txtInputData)
this.setState({txtInputData: ''})
alert('Data salved!')
}else{
alert('Please, fill the data!')
}
}
getValue = () => {
AsyncStorage.getItem('key_default').then(value => this.setState({getValue: value}))
}
render() {
return (
<View style={styles.mainContainer}>
<Text>Using AsyncStorage</Text>
<TextInput
style={styles.textInput}
placeholder = 'type here'
value = {this.state.txtInputData}
onChangeText={data => this.setState({txtInputData: data})}
/>
<TouchableOpacity
onPress={this.saveValue}
style={styles.TouchableOpacity}
>
<Text>Save value</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={this.getValue}
style={styles.TouchableOpacity}
>
<Text> Read value</Text>
</TouchableOpacity>
<Text>Value read:{this.state.getValue}</Text>
</View>
)
}
}
const styles = StyleSheet.create({
mainContainer: {
flex: 1,
padding: 20,
},
textInput: {
marginTop: 10,
borderWidth: 1,
borderColor: '#fa19',
fontSize: 15
},
TouchableOpacity: {
width: '50%',
marginTop: 10,
borderWidth: 1,
borderColor: '#fa19',
padding: 10,
alignSelf: 'center',
alignItems: 'center'
}
})
Just setStat of txtInputData instead of getValue like this
getValue = () => {
AsyncStorage.getItem('key_default').then(value => this.setState({txtInputData: value}))
}
I am using react-native-radio-button to implement a flatlist with radio button as shown in the image.
[![enter image description here][1]][1]
But i am unable to select a single radio button at a time in flatlist.
Here is my code
import React from 'react';
import { StyleSheet, View, Text, FlatList, TouchableOpacity, Image, LayoutAnimation, UIManager, Platform } from 'react-native';
import RadioButton from 'react-native-radio-button';
import PHARMACY from './api/mockPharmacyList';
import { Colors, Fonts } from '../../styles';
interface IState {
selected: number;
selectedIndex: number;
data: any;
expanded: boolean;
checked: boolean;
}
export class TransferToPharmacy extends React.Component<{}, IState> {
constructor(props) {
super(props);
this.state = {
data: PHARMACY,
selected: 0,
selectedIndex: 0,
expanded: true,
checked: false,
};
this.onPress = this.onPress.bind(this);
this.renderItem = this.renderItem.bind(this);
this.renderSeparator = this.renderSeparator.bind(this);
this._keyExtractor = this._keyExtractor.bind(this);
this.changeLayout = this.changeLayout.bind(this);
this.onCheck = this.onCheck.bind(this);
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
}
changeLayout = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
this.setState({ expanded: false });
}
getInitialState() {
return {
value: 0,
};
}
onPress(value) {
this.setState({ selected: value });
}
_keyExtractor = (item, index) => item.id;
onCheck = () => {
const { checked } = this.state;
if (checked === true) {
this.setState({ checked: false });
} else {
this.setState({ checked: true });
}
}
renderItem(data) {
const { item } = data;
return (
<View style={styles.itemBlock}>
<TouchableOpacity >
<View style={styles.arrowIconStyle}>
<Text style={styles.pharmacyText}>{item.name}</Text>
<RadioButton
innerColor={Colors.darkBlue}
outerColor={Colors.lightGray}
animation={'bounceIn'}
isSelected={this.state.selectedIndex === 0}
onPress={this.onPress}
/>
</View>
</TouchableOpacity>
<Text>{item.key1}</Text>
<View style={{ height: this.state.expanded === true ? 90 : 0, overflow: 'hidden' }}>
<View style={{ flexDirection: 'row', marginTop: 5, padding: 10 }}>
<Image style={[styles.itemImage, { width: 15, height: 15, margin: 1 }]} source={require('./images/map_pic_icon.png')} />
<Text style={styles.itemText}>{item.location}</Text>
</View>
<View style={{ flexDirection: 'row', marginTop: 5, padding: 10 }}>
<Image style={[styles.itemImage, { width: 15, height: 15, margin: 1 }]} source={require('./images/phone_icon.png')} />
<Text style={styles.itemText}>{item.phone}</Text>
</View>
</View>
</View>
);
}
renderSeparator() {
return <View style={styles.separator} />;
}
render() {
return (
<View style={styles.container} >
<Text style={styles.textStyle}>Transfer to Pharmacy</Text>
<View style={styles.childViewStyle}>
<FlatList
keyExtractor={this._keyExtractor}
data={this.state.data}
renderItem={this.renderItem}
ItemSeparatorComponent={this.renderSeparator}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
textStyle: {
fontSize: 18,
color: Colors.black,
marginTop: 30,
marginLeft: 20
},
childViewStyle: {
margin: 20,
},
itemBlock: {
paddingVertical: 15,
},
itemImage: {
width: 50,
height: 50,
borderRadius: 25,
margin: 10
},
itemText: {
fontSize: 16,
justifyContent: 'center',
color: Colors.darkySkyBlue
},
itemName: {
fontSize: 20,
color: Colors.black,
},
separator: {
borderRadius: 4,
borderWidth: 1,
borderColor: Colors.lightGray,
},
pharmacyText: {
fontSize: 16,
fontFamily: Fonts.bold,
color: Colors.black
},
radioStyle: {
marginTop: 10,
marginRight: 50,
justifyContent: 'space-between'
},
arrowIconStyle: {
flexDirection: 'row',
justifyContent: 'space-between',
padding: 10
// flex: 1
}
});
with this code i am not able to select the radio button
Please let me know where it is going wrong as i am unable to select radio button in the flatlist separately.
i have changed onPress() like this
onPress( index) {
this.setState({ selected: index, });
}
and in renderItem method i have changed it like this
const { item, index } = data;
<RadioButton
innerColor={Colors.darkBlue}
outerColor={Colors.lightGray}
animation={'bounceIn'}
isSelected={this.state.selected === index}
onPress={() => this.onPress(index)}
/>
Now the output is like this
Now the first radio button is selected but when i tried to select other radio buttons they are not getting selected.
Here is my mock data file which is used for Flatlist.
export const PHARMACY = [
{
key: 0,
name: 'Plaza Pharmacy',
type: 'Urgent Care Center - 4.01 mi',
location: '3417 Gaston Avenue, Suite 195\n Dallas, TX 75202',
phone: '469.764.7100',
},
{
key: 1,
name: 'Courtyard Pharmacy',
type: 'Urgent Care Center - 4.09 mi',
location: '5236 West University Drive, Suite 1900\n MCKINNEY, TX 75071',
phone: '969.264.7190',
},
{
key: 2,
name: 'Outptaient Pharmacy at the Cancer Center',
type: 'Urgent Care Center - 4.66 mi',
location: '3800 Gaylord Parkway, Ste 110\n FRISCO, TX 75034',
phone: '890.664.8130',
},
{
key: 3,
name: 'Carrolton Pharmacy',
type: 'Urgent Care Center - 4.08 mi',
location: '5236 West University Drive, Suite 1900\n MCKINNEY, TX 75071',
phone: '969.264.7190',
},
{
key: 4,
name: 'Garland Pharmacy',
type: 'Urgent Care Center - 22.11 mi',
location: '3417 Gaston Avenue, Suite 195\n Dallas, TX 75202',
phone: '469.764.7100',
},
];
I am exporting this const PHARMACY and importing it in my class and set this to a state variable "data".
you can do something like this in renderItem
renderItem = ({item, index}) => {
.....
<RadioButton
innerColor={Colors.darkBlue}
outerColor={Colors.lightGray}
animation={'bounceIn'}
isSelected={this.state.selectedIndex === index}
onPress={() => {this.onPress(index)}}
/>
}
and replace onPress with
onPress = (index) => {
this.setState({ selectedIndex: index });
}
and update FlatList with extraData props as FlatList needs to be re-rendered as
<FlatList
keyExtractor={this._keyExtractor}
data={this.state.data}
renderItem={this.renderItem}
ItemSeparatorComponent={this.renderSeparator}
extraData={this.state}
/>
You can refer in snack here
First, you need to get the index of the current item like so:
const { item, index } = data;
After you get the index of the current item you can then check if the current radio button is selected or not and for changing the radio button you need to pass the value of the current index to the onPress function.
<RadioButton
innerColor={Colors.darkBlue}
outerColor={Colors.lightGray}
animation={'bounceIn'}
isSelected={this.state.selected === index}
onPress={() => this.onPress(index)}
/>
I have created a game on React and I am trying to adapt my code to React Native. One of the things that is troubling me is how to translate these three lines, since in RN there are no DOM solutions to rely on:
handleClick(e) {
this.props.change(e.currentTarget.id);
}
What is happening here is that a stateless child is harvesting a clicked elements id (the currentTarget's) and is using it to call with it a method defined inside the parent. This kind of formulation e.currentTarget.id however does not work in RN.
Is there an eloquent way to re-write this one liner in RN?
Note: there are two questions vaguely resembling this one, here and here, however the answers look like more like patches than a structural elegant solution. If you are aware of something pls post an answer.
Edit: It seems that one cannot go around ReactNativeComponentTree.
I have that much so far but this does not work yet:
handlePress(event) {
let number = ReactNativeComponentTree.getInstanceFromNode(event.currentTarget)._currentElement.id;
this.props.change(number);
}
Second Edit: Ok maybe I should add a simplistic example of what I am trying to achieve. When I click on any of the flatlist's elements its id should be displayed on the Child's bottom . Clicking on reset will restore default state.
Code of simplistic example below:
import React, { Component } from 'react';
import { AppRegistry, FlatList, StyleSheet, Text, View, Button } from 'react-native';
import ReactNativeComponentTree from 'react-native';
export default class Parent extends Component {
constructor(props) {
super(props);
this.state = {
quotes: ["a","bnaskdkahhahskkdk","c","d","e","a","b","c","d"],
size: [true, true, true, true, true, true, true, true, true],
color: [false, false, false, false, false, false, false, false, false],
progress: "me"
};
this.change = this.change.bind(this);
this.reset = this.reset.bind(this);
}
change(number) {
this.setState({color: [true, true, true, true, true, true, true, true, true], progress: number});
}
reset() {
this.setState({color: [false, false, false, false, false, false, false, false, false],
progress: "me"
});
}
render() {
return (
<View style={styles.container}>
<Child change={this.change} reset={this.reset} quotes={this.state.quotes}
size={this.state.size} color={this.state.color}
progress={this.state.progress} />
</View>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.handlePress = this.handlePress.bind(this);
this.handleReset = this.handleReset.bind(this);
}
/*handlePress(e) {
let number = e.currentTarget.id;
this.props.change(number);
}*/
handlePress(event) {
let number = ReactNativeComponentTree.getInstanceFromNode(event.currentTarget)._currentElement.id;
this.props.change(number);
}
handleReset() {
this.props.reset();
}
render() {
let ar = [];
for (let i=0; i<this.props.quotes.length; i++) {
let b = {key: `${i}`, id: i,
classSize: this.props.size[i] ? (i%2===0 ? styles.size : styles.oddsize) : "",
classColor: this.props.color[i] ? (i%2===0 ? styles.color : styles.oddcolor) : ""}
ar.push(b);
}
return (
<View style={styles.container}>
<Button onPress={this.handleReset} title="Reset" />
<FlatList
data={
ar
}
renderItem={({item}) => <Text onPress={this.handlePress}
style={[item.classSize, item.classColor]}> {item.id+1}
{this.props.quotes[item.id]} </Text> }
/>
<Text style={styles.size}>{this.props.progress}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "column",
//justifyContent: "center",
alignItems: "center",
paddingTop: 22,
//backgroundColor: "purple"
},
size: {
flex: 1,
padding: 10,
fontSize: 18,
backgroundColor: "grey",
margin: 1,
height: 44,
color: 'gold',
borderColor: "white",
borderWidth: "1",
textAlign: "center"
},
oddsize: {
flex: 1,
padding: 10,
fontSize: 18,
backgroundColor: "white",
margin: 1,
height: 44,
color: 'gold',
borderColor: "white",
borderWidth: "1",
textAlign: "center"
},
color: {
flex: 1,
padding: 10,
backgroundColor: 'grey',
//borderRadius: "25%",
margin: 1,
fontSize: 18,
height: 44,
color: 'pink',
borderColor: "red",
borderWidth: "1"
},
oddcolor: {
flex: 1,
padding: 10,
backgroundColor: 'white',
//borderRadius: "25%",
margin: 1,
fontSize: 18,
height: 44,
color: 'pink',
borderColor: "red",
borderWidth: "1"
}
})
// skip this line if using Create React Native App
AppRegistry.registerComponent('AwesomeProject', () => Parent);
A better way (avoiding event callbacks creation at every render)
to get the current pressed element properties (id in this example)
is by wrapping it in a parent component, passing data
and binding all events only once (in constructor)
First declare your component wrapper:
It needs to be a class and not a stateless functional component otherwise you can't avoid the callback creation at every render
to see the difference, here's a more advanced example in action:
https://snack.expo.io/ByTEKgEsZ (example source code)
class TouchableText extends React.PureComponent {
constructor(props) {
super(props);
this.textPressed = this.textPressed.bind(this);
}
textPressed(){
this.props.onPressItem(this.props.id);
}
render() {
return (
<Text style={styles.item} onPress={this.textPressed}>
{this.props.children}
</Text>
);
}
}
If you use a const JSX object (stateless functional component), it works but it's not optimal, the event callback will be created every time the component is rendered as the arrow function is actually a shortcut to the render function
const TouchableText = props => {
const textPressed = () => {
props.onPressItem(props.id);
};
return <Text onPress={textPressed} />;
};
Then use this wrapper instead of your component as following:
class Test extends React.Component {
constructor(props) {
super(props);
//event binding in constructor for performance (happens only once)
//see facebook advice:
//https://facebook.github.io/react/docs/handling-events.html
this.handlePress = this.handlePress.bind(this);
}
handlePress(id) {
//Do what you want with the id
}
render() {
return (
<View>
<FlatList
data={ar}
renderItem={({ item }) => (
<TouchableText
id={item.id}
onPressItem={this.handlePress}
>
{`Component with id ${item.id}`}
</TouchableText>
)}
/>
</View>
);
}
}
As written in React Native Handling Events Doc, there's another possible syntax to avoid binding in the constructor (experimental though: i.e. which may or may not be supported in the future!):
If calling bind annoys you, there are two ways you can get around this. If you are using the experimental property initializer syntax, you can use property initializers to correctly bind callbacks
this means we could only write
handlePress = (id) => {
//`this` is already bound!
}
instead of
constructor(props) {
super(props);
//manually bind `this` in the constructor
this.handlePress = this.handlePress.bind(this);
}
handlePress(id) {
}
Some references:
Event handlers and Functional Stateless Components
React Binding Patterns: 5 Approaches for Handling this
After 8 hours of searching I ve found the solution on my own, thanks to the fantastic calculator tutorial of Kyle Banks.
https://kylewbanks.com/blog/react-native-tutorial-part-3-developing-a-calculator
Well basically the solution is in binding item.id along this in the assignment of the onPress event listener, like so:
renderItem={({item}) => <Text
onPress={this.handlePress.bind(this, item.id)}
style={[item.classSize, item.classColor]}>
{item.id+1}
{this.props.quotes[item.id]}
</Text> }
After doing so, the only thing you have to do is to define handlePress like so:
handlePress(e) {
this.props.change(e);
}
Where e is the item.id, on the Child and change() on the Parent:
change(number) {
this.setState({color: [true, true, true, true, true, true, true, true, true], progress: number});
}
The complete code works as intended.
import React, { Component } from 'react';
import { AppRegistry, FlatList, StyleSheet, Text, View, Button } from 'react-native';
import ReactNativeComponentTree from 'react-native';
export default class Parent extends Component {
constructor(props) {
super(props);
this.state = {
quotes: ["a","bnaskdkahhahskkdk","c","d","e","a","b","c","d"],
size: [true, true, true, true, true, true, true, true, true],
color: [false, false, false, false, false, false, false, false, false],
progress: "me"
};
this.change = this.change.bind(this);
this.reset = this.reset.bind(this);
}
change(number) {
this.setState({color: [true, true, true, true, true, true, true, true, true], progress: number});
}
reset() {
this.setState({color: [false, false, false, false, false, false, false, false, false],
progress: "me"
});
}
render() {
return (
<View style={styles.container}>
<Child change={this.change} reset={this.reset} quotes={this.state.quotes}
size={this.state.size} color={this.state.color}
progress={this.state.progress} />
</View>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.handlePress = this.handlePress.bind(this);
this.handleReset = this.handleReset.bind(this);
}
handlePress(e) {
this.props.change(e);
}
/* handlePress(event) {
let number = ReactNativeComponentTree.getInstanceFromNode(event.currentTarget)._currentElement.id;
this.props.change(number);
} */
handleReset() {
this.props.reset();
}
render() {
let ar = [];
for (let i=0; i<this.props.quotes.length; i++) {
let b = {key: `${i}`, id: i,
classSize: this.props.size[i] ? (i%2===0 ? styles.size : styles.oddsize) : "",
classColor: this.props.color[i] ? (i%2===0 ? styles.color : styles.oddcolor) : ""}
ar.push(b);
}
return (
<View style={styles.container}>
<Button onPress={this.handleReset} title="Reset" />
<FlatList
data={
ar
}
renderItem={({item}) => <Text onPress={this.handlePress.bind(this, item.id)}
style={[item.classSize, item.classColor]}> {item.id+1}
{this.props.quotes[item.id]} </Text> }
/>
<Text style={styles.size}>{this.props.progress}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "column",
//justifyContent: "center",
alignItems: "center",
paddingTop: 22,
//backgroundColor: "purple"
},
size: {
flex: 1,
padding: 10,
fontSize: 18,
backgroundColor: "grey",
margin: 1,
height: 44,
color: 'gold',
borderColor: "white",
borderWidth: "1",
textAlign: "center"
},
oddsize: {
flex: 1,
padding: 10,
fontSize: 18,
backgroundColor: "white",
margin: 1,
height: 44,
color: 'gold',
borderColor: "white",
borderWidth: "1",
textAlign: "center"
},
color: {
flex: 1,
padding: 10,
backgroundColor: 'grey',
//borderRadius: "25%",
margin: 1,
fontSize: 18,
height: 44,
color: 'pink',
borderColor: "red",
borderWidth: "1"
},
oddcolor: {
flex: 1,
padding: 10,
backgroundColor: 'white',
//borderRadius: "25%",
margin: 1,
fontSize: 18,
height: 44,
color: 'pink',
borderColor: "red",
borderWidth: "1"
}
})
// skip this line if using Create React Native App
AppRegistry.registerComponent('AwesomeProject', () => Parent);
I have a horizontal ListView (shown in the image with list items a,b,c) that will not scroll, or rather, it does scroll - but one out of 10 or so swipes seems to make it scroll. I have also used a FlatList - same result. The ListView is on a Interactable.View, however I got rid of the Interactable.View and it still didn't scroll. I've tested on a real ios device and a genymotion emulator and both had the same result. There are more items in the list than just a,b,c. There are 6 items in the list.
import { StyleSheet, View, Text, FlatList, ListView } from 'react-native'
import React, { Component } from 'react'
import MapView from 'react-native-maps'
import { connect } from 'react-redux'
import {
Button,
Container
} from 'native-base'
import { updateRegion } from './map.action'
import { OptimizedFlatList } from 'react-native-optimized-flatlist'
import Icon from 'react-native-vector-icons/FontAwesome'
import { toggleMenu } from '../search-page/searchPage.action'
import mapStyle from './style'
import Interactable from 'react-native-interactable'
import { setSelectedShop } from '../search-results/searchResults.action'
import { updateHeight } from '../search-results/searchResultsPresenter.action'
import { getSelectedProduct } from './markers.selector'
const mapStateToProps = (state) => ({
region: state.get('map').get('region'),
markers: state.get('searchResults').get('products'),
selectedProduct: getSelectedProduct(state),
height: state.get('searchResultsPresenter').get('height')
})
const mapDispatchToProps = (dispatch) => ({
onRegionChange: (region) => {
dispatch(updateRegion(region))
},
onToggleMenuClick: () => {
dispatch(toggleMenu())
},
setSelectedShop: id => {
dispatch(setSelectedShop(id))
},
updateHeight: height => {
dispatch(updateHeight(height))
}
})
class Map extends Component {
componentDidMount() {
const { store } = this.context
this.unsubscribe = store.subscribe(() => { })
}
componentWillUnmount() {
this.unsubscribe()
}
componentWillReceiveProps(newProps) {
if (newProps.selectedProduct) {
let products = newProps.selectedProduct.products
this.setState({
dataSource: this.state.dataSource.cloneWithRows(products)
})
}
}
interactableView;
constructor(props) {
super(props)
this.state = { dataSource: new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 }) }
}
render() {
console.log(this.props.height)
return (
<Container>
<MapView
style={styles.map}
region={this.props.region}
onRegionChangeComplete={this.props.onRegionChange}>
{
this.props.markers.map(marker => {
return (
<MapView.Marker
coordinate={marker.shop.coordinate}
title={marker.shop.name}
identifier={marker.shop.id.toString()}
onPress={e => {
console.log(e.nativeEvent)
this.interactableView.snapTo({ index: 0 })
this.props.setSelectedShop(marker.shop)
console.log(this.props.selectedProduct)
}}
/>
)
})
}
</MapView>
<Button
small
icon
style={mapStyle.toggleMenuButton}
onPress={() => this.props.onToggleMenuClick()}>
<Icon name="sliders" size={20} color="#FFFFFF" />
</Button>
<Interactable.View
style={{
flex: 1,
flexDirection: 'row',
zIndex: 20,
borderRadius: 10,
backgroundColor: '#222222',
padding: 30,
paddingTop: 50
}}
verticalOnly={true}
snapPoints={[{ y: this.props.height - 225 }, { y: this.props.height - 50 }]}
initialPosition={{ y: this.props.height - 50 }}
ref={view => this.interactableView = view}
onLayout={(event) => {
this.props.updateHeight(event.nativeEvent.layout.height)
}} >
<View style={{ flex: 1, flexDirection: 'row', height: 50 }}>
<Text
style={{
color: 'white',
position: 'absolute',
top: -40,
marginBottom: 20,
textAlign: 'center',
width: '100%'
}}>
{this.props.selectedProduct ? this.props.selectedProduct.shop.name : ''}
</Text>
<ListView
dataSource={this.state.dataSource}
horizontal={true}
style={{
height: 200
}}
renderRow={(rowData)=> {
console.log(rowData)
return (
<View style={{
backgroundColor: 'blue',
width: 100,
borderWidth: 1,
borderColor: 'black',
margin: 0
}}>
<Text style={{ color: 'white' }}>{rowData.name}</Text>
<View style={{
zIndex: 15,
width: '100%',
height: '100%',
backgroundColor: 'red'
}}>
</View>
</View>
)
}}
/>
</View>
</Interactable.View>
</Container>
)
}
}
Map.contextTypes = {
store: React.PropTypes.object
}
Map.propTypes = {
region: React.PropTypes.shape({
latitude: React.PropTypes.number,
longitude: React.PropTypes.number,
latitudeDelta: React.PropTypes.number,
longitudeDelta: React.PropTypes.number
}).isRequired,
height: React.PropTypes.number,
updateHeight: React.PropTypes.func,
setSelectedShop: React.PropTypes.func,
selectedProduct: React.PropTypes.string,
onRegionChange: React.PropTypes.func.isRequired,
onToggleMenuClick: React.PropTypes.func.isRequired,
markers: React.PropTypes.array
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Map)
const styles = StyleSheet.create({
map: {
...StyleSheet.absoluteFillObject,
zIndex: 3
}
})
How do I get the listview to scroll with every swipe, rather than about 1 out of 10 swipes?
Same thing happens when I use a ScrollView. So nothing can be swiped horizontally there. Even when removing the interactable.view
Making renderRow return a TouchableHighlight instead of a View caused the list to be draggable with every touch and swipe. And finally, unessential to the answer, I switched from a ListView to a FlatList because ListView is going to be deprecated in the future. I felt it good to state that as at this point in time it isn't clear from the official documentation that ListView is becoming deprecated.