iOS react-native CameraRoll loads too slowly. - ios

With react-native, I implemented IOS CameraRoll that fetches 300 images from 'Camera Roll' Album on first and keep fetching 300 images whenever scroll reaches the end. Below is My code. SalmonCameraRoll.js
import React from 'react'
import {
View,
Text,
TouchableHighlight,
Modal,
StyleSheet,
Button,
CameraRoll,
Image,
Dimensions,
ScrollView,
FlatList,
} from 'react-native'
import Share from 'react-native-share';
import RNFetchBlob from 'react-native-fetch-blob';
let styles
const { width, height } = Dimensions.get('window')
const fetchAmount = 300;
class SalmonCameraRoll extends React.Component {
static navigationOptions = {
title: 'Salmon Camera Roll',
}
constructor(props) {
super(props);
this.state = {
photos: [],
// index: null,
lastCursor: null,
noMorePhotos: false,
loadingMore: false,
refreshing: false,
};
this.tryGetPhotos = this.tryGetPhotos.bind(this);
this.getPhotos = this.getPhotos.bind(this);
this.appendPhotos = this.appendPhotos.bind(this);
this.renderImage = this.renderImage.bind(this);
this.onEndReached = this.onEndReached.bind(this);
this.getPhotos({first: fetchAmount, assetType: 'Photos'});
}
componentDidMount() {
this.subs = [
this.props.navigation.addListener('didFocus', () => {
this.getPhotos({first: fetchAmount, assetType: 'Photos'});
}),
];
}
componentWillUnmount() {
this.subs.forEach(sub => sub.remove());
}
tryGetPhotos = (fetchParams) => {
if (!this.state.loadingMore) {
this.setState({ loadingMore: true }, () => { this.getPhotos(fetchParams)})
}
}
getPhotos = (fetchParams) => {
if (this.state.lastCursor) {
fetchParams.after = this.state.lastCursor;
}
CameraRoll.getPhotos(fetchParams).then(
r => this.appendPhotos(r)
)
}
appendPhotos = (data) => {
const photos = data.edges;
const nextState = {
loadingMore: false,
};
if (!data.page_info.has_next_page) {
nextState.noMorePhotos = true;
}
if (photos.length > 0) {
nextState.lastCursor = data.page_info.end_cursor;
nextState.photos = this.state.photos.concat(photos);
this.setState(nextState);
}
}
onEndReached = () => {
if (!this.state.noMorePhotos) {
this.tryGetPhotos({first: fetchAmount, assetType: 'Photos'});
}
}
renderImage = (photo, index) => {
return (
<TouchableHighlight
style={{borderTopWidth: 1, borderRightWidth: 1, borderColor: 'white'}}
key={index}
underlayColor='transparent'
onPress={() => {
this.props.navigation.navigate('Camera', { backgroundImageUri: photo.node.image.uri })
}
}
>
<Image
style={{
width: width/3,
height: width/3
}}
representation={'thumbnail'}
source={{uri: photo.node.image.uri}}
/>
</TouchableHighlight>
)
}
render() {
return (
<View style={styles.container}>
<View style={styles.modalContainer}>
<FlatList
numColumns={3}
data={this.state.photos}
initialNumToRender={fetchAmount}
onEndReachedThreshold={500}
onEndReached={this.onEndReached}
refreshing={this.state.refreshing}
onRefresh={() => this.tryGetPhotos({first: fetchAmount, assetType: 'Photos'})}
keyExtractor={(item, index) => index}
renderItem={({ item, index }) => (
this.renderImage(item, index)
)}
/>
</View>
</View>
)
}
}
styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
modalContainer: {
// paddingTop: 20,
flex: 1,
},
scrollView: {
flexWrap: 'wrap',
flexDirection: 'row'
},
shareButton: {
position: 'absolute',
width,
padding: 10,
bottom: 0,
left: 0
}
})
export default SalmonCameraRoll
Problem is that in circumstance of a lot of images(about 10000 images) in 'Camera Roll' album, each image component was loaded so slowly that it was also loaded too slowly when scrolling accordingly.
In other famous apps like Facebook or Instagram, it loads all images quickly at once without fetching whenever scroll reaches end.
How can i make my Image component load fast? Or best of all(if possible), how can i make my CameraRoll load all images quickly at once without fetching whenever scroll reaches end?
Thank you.

Related

ReactNative : Getting white space between keyboard and comment-box input field

I am getting white space between the keyboard and input box. I have used KeyBoardAvoiding view in my code.
Here is the piece of code:
const [isKeyboardVisible, setKeyboardVisible] = useState(false);
const [offsetValue, setOffsetValue] = useState(0);
useEffect(() => {
const keyboardDidShowListener = Keyboard.addListener("keyboardDidShow", () => {
setKeyboardVisible(true);
});
const keyboardDidHideListener = Keyboard.addListener("keyboardDidHide", () => {
setKeyboardVisible(false);
});
if (appState === "active") {
isKeyboardVisible && Keyboard.dismiss();
}
return () => {
keyboardDidHideListener.remove();
keyboardDidShowListener.remove();
};
}, []);
useEffect(() => {
if (isKeyboardVisible) {
setOffsetValue(0);
} else {
setOffsetValue(80);
}
}, [isKeyboardVisible]);
<KeyboardAvoidingView
behavior={Platform.OS === "ios" ? "padding" : "height"}
keyboardVerticalOffset={Platform.OS === "ios" ? offsetValue : 0}
>
<ScrollView
contentContainerStyle={styles.scrollViewContainer}
alwaysBounceVertical={false}
keyboardShouldPersistTaps={"handled"}
>
<View style={styles.chatInputBoxWrapper}>
<AvyCommentLinearInput
inputRef={props.setInputRef}
value={props.inputValue}
isVideoCaptureEnabled={true}
imageUrl={""}
onSubmit={(value) => props.onChatSubmit(value)}
/>
</View>
</ScrollView>
</KeyboardAvoidingView>
export default StyleSheet.create({
scrollViewStyle: {
marginHorizontal: 16,
borderRadius: 4,
marginBottom: 8,
marginTop: 8,
},
chatInputBoxWrapper: {
...shadowStyle,
flex: 1,
},
scrollViewContainer: { flexGrow: 1 },
});
Video link of the issue I am facing:
https://www.dropbox.com/s/6ydwxhaq06dpfaf/RPReplay_Final1674132226.MP4?dl=0
Any help would be Appreciated!!!
My recommendation - use React Native Avoid SoftInput https://mateusz1913.github.io/react-native-avoid-softinput/
A lot of examples https://github.com/mateusz1913/react-native-avoid-softinput/tree/main/packages/app/src/screens

ReactNative ble manager is not reading data from peripheral on IOS

I am building a ReactNative application for both IOS and Android platforms. My app needs to read data from another device via BLE communication. I am using this package for implementing BLE communication, https://github.com/innoveit/react-native-ble-manager. I am having a problem with receiving characteristic data on IOS even though it is working as expected on the Android platform.
I have added the following to the info.plist file:
Privacy - Bluetooth Always Usage Description: App needs to use Bluetooth to receive data from WaterRower machine
I have a component that scan and list down the BLE devices as follow. It is called, BleDeviceList.js
import React, {
useState,
useEffect,
} from 'react';
import {
SafeAreaView,
StyleSheet,
ScrollView,
View,
Text,
StatusBar,
NativeModules,
NativeEventEmitter,
Button,
Platform,
PermissionsAndroid,
FlatList,
TouchableHighlight,
} from 'react-native';
import {
Colors,
} from 'react-native/Libraries/NewAppScreen';
import BleManager from 'react-native-ble-manager';
const BleManagerModule = NativeModules.BleManager;
const bleManagerEmitter = new NativeEventEmitter(BleManagerModule);
const BleDeviceList = (props) => {
const [isScanning, setIsScanning] = useState(false);
const peripherals = new Map();
const [list, setList] = useState([]);
const [ connectedDevices, setConnectedDevices ] = useState([ ]);
const [ permissionsAllowed, setPermissionsAllowed ] = useState(false)
const startScan = () => {
if (!isScanning) {
BleManager.scan([], 3, true).then((results) => {
console.log('Scanning...');
setIsScanning(true);
}).catch(err => {
console.error(err);
});
}
}
const handleStopScan = () => {
console.log('Scan is stopped');
setIsScanning(false);
}
const handleDisconnectedPeripheral = (data) => {
let peripheral = peripherals.get(data.peripheral);
if (peripheral) {
peripheral.connected = false;
peripherals.set(peripheral.id, peripheral);
setList(Array.from(peripherals.values()));
}
console.log('Disconnected from ' + data.peripheral);
}
const handleUpdateValueForCharacteristic = (data) => {
console.log('Received data from ' + data.peripheral + ' characteristic ' + data.characteristic, data.value);
}
const retrieveConnected = () => {
BleManager.getConnectedPeripherals([]).then((results) => {
if (results.length == 0) {
console.log('No connected peripherals')
}
console.log(results);
for (var i = 0; i < results.length; i++) {
var peripheral = results[i];
peripheral.connected = true;
peripherals.set(peripheral.id, peripheral);
setList(Array.from(peripherals.values()));
}
});
}
const handleDiscoverPeripheral = (peripheral) => {
console.log('Got ble peripheral', peripheral);
if (!peripheral.name) {
peripheral.name = 'NO NAME';
}
peripherals.set(peripheral.id, peripheral);
setList(Array.from(peripherals.values()));
}
const isConnected = (peripheral) => {
return connectedDevices.filter(cd => cd.id == peripheral.id).length > 0;
}
const toggleConnectPeripheral = (peripheral) => {
if (peripheral){
if (isConnected(peripheral)){
BleManager.disconnect(peripheral.id);
setConnectedDevices(connectedDevices.filter(cd => cd.id != peripheral.id))
}else{
BleManager.connect(peripheral.id).then(() => {
let tempConnnectedDevices = [ ...connectedDevices ]
tempConnnectedDevices.push(peripheral);
setConnectedDevices(tempConnnectedDevices);
props.navigation.push('BleRowingSession', { peripheral: peripheral });
let p = peripherals.get(peripheral.id);
if (p) {
p.connected = true;
peripherals.set(peripheral.id, p);
setList(Array.from(peripherals.values()));
props.navigation.push('BleDeviceServiceList', { peripheral: peripheral });
}
console.log('Connected to ' + peripheral.id);
}).catch((error) => {
console.log('Connection error', error);
});
}
}
}
useEffect(() => {
BleManager.start({showAlert: false});
bleManagerEmitter.addListener('BleManagerDiscoverPeripheral', handleDiscoverPeripheral);
bleManagerEmitter.addListener('BleManagerStopScan', handleStopScan );
bleManagerEmitter.addListener('BleManagerDisconnectPeripheral', handleDisconnectedPeripheral );
bleManagerEmitter.addListener('BleManagerDidUpdateValueForCharacteristic', handleUpdateValueForCharacteristic );
if (Platform.OS === 'android' && Platform.Version >= 23) {
PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION).then((result) => {
if (result) {
console.log("Permission is OK");
setPermissionsAllowed(true);
} else {
PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION).then((result) => {
if (result) {
console.log("User accept");
setPermissionsAllowed(true);
} else {
console.log("User refuse");
setPermissionsAllowed(false);
}
});
}
});
} else {
setPermissionsAllowed(true)
}
return (() => {
console.log('unmount');
bleManagerEmitter.removeListener('BleManagerDiscoverPeripheral', handleDiscoverPeripheral);
bleManagerEmitter.removeListener('BleManagerStopScan', handleStopScan );
bleManagerEmitter.removeListener('BleManagerDisconnectPeripheral', handleDisconnectedPeripheral );
bleManagerEmitter.removeListener('BleManagerDidUpdateValueForCharacteristic', handleUpdateValueForCharacteristic );
})
}, []);
const renderConnectButton = (item) => {
if (isConnected(item)) {
return null
}
return (
<Button
title="Connect"
onPress={() => {
toggleConnectPeripheral(item)
}}
/>
)
}
const renderDisconnectButton = (item) => {
if (! isConnected(item)) {
return null
}
return (
<Button
title="Disconnect"
onPress={() => {
toggleConnectPeripheral(item)
}}
/>
)
}
const renderItem = (item) => {
const color = item.connected ? 'green' : '#fff';
return (
<TouchableHighlight>
<View style={[styles.row, {backgroundColor: color}]}>
<Text style={{fontSize: 12, textAlign: 'center', color: '#333333', padding: 10}}>{item.name}</Text>
<Text style={{fontSize: 10, textAlign: 'center', color: '#333333', padding: 2}}>RSSI: {item.rssi}</Text>
<Text style={{fontSize: 8, textAlign: 'center', color: '#333333', padding: 2, paddingBottom: 20}}>{item.id}</Text>
{renderConnectButton(item)}
{renderDisconnectButton(item)}
</View>
</TouchableHighlight>
);
}
const renderContent = () => {
if (! permissionsAllowed) {
return <Text>Bluetooth and locations permissions are required.</Text>
}
return (
<>
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={styles.scrollView}>
{global.HermesInternal == null ? null : (
<View style={styles.engine}>
<Text style={styles.footer}>Engine: Hermes</Text>
</View>
)}
<View style={styles.body}>
<View style={{margin: 10}}>
<Button
title={'Scan Bluetooth (' + (isScanning ? 'on' : 'off') + ')'}
onPress={() => startScan() }
/>
</View>
<View style={{margin: 10}}>
<Button title="Retrieve connected peripherals" onPress={() => retrieveConnected() } />
</View>
{(list.length == 0) &&
<View style={{flex:1, margin: 20}}>
<Text style={{textAlign: 'center'}}>No peripherals</Text>
</View>
}
</View>
</ScrollView>
<FlatList
data={list}
renderItem={({ item }) => renderItem(item) }
keyExtractor={item => item.id}
/>
</SafeAreaView>
</>
)
}
return (
renderContent()
);
};
const styles = StyleSheet.create({
scrollView: {
backgroundColor: Colors.lighter,
},
engine: {
position: 'absolute',
right: 0,
},
body: {
backgroundColor: Colors.white,
},
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
color: Colors.black,
},
sectionDescription: {
marginTop: 8,
fontSize: 18,
fontWeight: '400',
color: Colors.dark,
},
highlight: {
fontWeight: '700',
},
footer: {
color: Colors.dark,
fontSize: 12,
fontWeight: '600',
padding: 4,
paddingRight: 12,
textAlign: 'right',
},
});
export default BleDeviceList;
As you can see in the code, when the Connect button is clicked, it will redirect the user to another component that reads data from another device. The following is the BleRowingSession.js that reads the data from another device.
import React, { useEffect, useState } from 'react';
import { Text, View, StyleSheet, NativeModules, NativeEventEmitter, ScrollView } from 'react-native';
import BleManager from 'react-native-ble-manager';
const BleManagerModule = NativeModules.BleManager;
const bleManagerEmitter = new NativeEventEmitter(BleManagerModule);
const serviceId = "00001826-0000-1000-8000-00805F9B34FB";
const characteristicId = "00002AD1-0000-1000-8000-00805F9B34FB";
let readDataCache = "";
const BleRowingSession = (props) => {
let peripheral = props.route.params.peripheral;
const [ readData, setReadData ] = useState("");
const setUpBleNotification = () => {
BleManager.retrieveServices(peripheral.id).then((peripheralData) => {
console.log('Retrieved peripheral services', peripheralData);
setTimeout(() => {
BleManager.startNotification(peripheral.id, serviceId, characteristicId).then(() => {
console.log('Started notification on ' + peripheral.id);
bleManagerEmitter.addListener('BleManagerDidUpdateValueForCharacteristic', (data) => {
readDataCache = readDataCache + "\n" + data.value.toString()
setReadData(readDataCache);
});
setTimeout(() => {
}, 500);
}).catch((error) => {
console.log('Notification error', error);
});
}, 500)
});
}
useEffect(() => {
setUpBleNotification()
}, [ ])
return (
<View style={styles.container}>
<ScrollView>
<Text>Ble Rowing Session</Text>
<Text>{readData}</Text>
</ScrollView>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center"
}
})
export default BleRowingSession;
For now, the service id and characteristic id are hardcoded. Of course, I had to choose the right device on the first page that displays the list of BLE devices.
When I run the code, it is working as expected on the Android device, it is receiving the characteristic value. When I run the code on the actual IOS device, it is not receiving any data. But it can scan the devices and connect to them. What is wrong with my code and how can I fix it?

any Scroll events such as onScroll do not work with FlatList omponent

So I have a project using React-Native on iOS using <Flatlist/> component that is being rendered conditionally using a function renderContent() if a cart data is empty under a view like so:
<SafeAreaView style={styles.container}>
<Navigator.Config
onAppear={this.handleGetData}
onDisappear={this.handleOnDisappear}
rightButtons={deleteButton}
onRightPress={this.handleDeleteButton}
>
{this.renderContent()}
</Navigator.Config>
</SafeAreaView>
where the renderContent() function is like so :
renderContent = () => {
const { cart, cart: { cartObject } } = this.props
const { shop_group: shopGroup } = cartObject
if (
(cart.isNoResult && !cart.isLoadingCartList) ||
!this.state.isUserLogin ||
(shopGroup && shopGroup.length < 1 && !cart.isLoadingCartList)
) {
return this.renderNoResult()
}
return this.renderData()
}
the <NoResultView/> component
<View style={{ flex: 1, backgroundColor: 'white' }}>
<View>
<View style={{ paddingTop: noLogin ? 50 : 30, alignItems: 'center' }}>
...
{
cartData.isLoadingRecommended == false ?
<SuggestionsView title="Rekomendasi"
data={cartData.recommended ? cartData.recommended.recommendation : []}
renderItem={renderItemRecommended}
onScroll={handleScroll}
onEndReached={(info)=>{
// if(!cartData.isLoadingRecommended && cartData.recommended){
console.log('----------** END **------------', info, cartData.isLoadingRecommended)
// }
}}
/>
:
<ActivityIndicator animating size="small" />
}
</View>
and the <FlatList/> component is rendered inside `
import { View, Text, Image, FlatList, TouchableOpacity } from 'react-native'
import React, { Component } from 'react'
import Icon from 'react-native-vector-icons/EvilIcons'
import { ReactTPRoutes } from 'NativeModules'
class SuggestionsView extends Component {
constructor(props){
super(props)
this.state = {
isRefreshing: false,
isLoading: false,
isLoadingMorePost: true,
hasNextPage: false,
reachFeedEnd: false,
shouldDisplayErrorToaster: false,
isError: false,
}
}
handlePressViewAll = () => this.props.onPressViewAll()
keyExtractor = (item, index) => index;
render(){
return (
<View style={{
paddingLeft: 16,
paddingRight: 16,
}}>
</View>
<FlatList
style={{ flex: 1 }}
numColumns={2}
data={this.props.data}
keyExtractor={this.keyExtractor}
renderItem={this.props.renderItem}
onEndReachedThreshold={0.3}
scrollEventThrottle={16}
onEndReached={(info)=>{
if(this.props.onEndReached){
this.props.onEndReached(info)
}
}}
ref={x => {
this.flatist = x // tslint:disable-line
}}
onScroll={()=> console.log('test')}
/>
</View>
)}
}
export default SuggestionsView
Although for some reason the onScroll Method is not firing at all. The scrollToEnd method is also not firing for some reason. Any help is appreciated thank you.

react-native: image won't rerender when state changes

I have an icon as an image and I want to change the icon when a state property changes. Here is the relevant code:
<TouchableHighlight underlayColor="rgba(0,0,0,0)" style={styles.playButton} onPress={this._handleStartPress}>
<Image source={(this.state.started) ? require('./Control-pause.png') : require('./Control-play.png')} resizeMode="contain" style={styles.icon}/>
</TouchableHighlight>
The state changes correctly as expected (verifed by some console logs), but somehow the Image won't re render and change when this.state.started changes. The path to the images is also correct.
Any Ideas what's the problem?
EDIT: The whole component:
import React, {
AppRegistry,
Component,
StyleSheet,
Text,
TouchableHighlight,
View,
ScrollView,
Vibration,
AlertIOS,
Image
} from 'react-native'
/*import Icon from 'react-native-vector-icons/FontAwesome';*/
const timer = require('react-native-timer');
const Button = require('./components/Button.js');
const PlayIcon = require('./Control-play.png');
const PauseIcon = require('./Control-pause.png');
class Project extends Component {
constructor(props) {
super(props);
this.state = {
timerValue: 25*60,
count: 0,
started: false,
};
this._tick = this._tick.bind(this);
this._runClock = this._runClock.bind(this);
this._stopClock = this._stopClock.bind(this);
this._handlePomodoroPress = this._handlePomodoroPress.bind(this);
this._handlePausePress = this._handlePausePress.bind(this);
this._getMinsSecs = this._getMinsSecs.bind(this);
this._finishedTimer = this._finishedTimer.bind(this);
this._handleStartPress = this._handleStartPress.bind(this);
}
_tick() {
if (this.state.timerValue > 0) {
this.setState({timerValue: this.state.timerValue - 1});
} else {
this._finishedTimer();
}
}
_finishedTimer() {
this.setState({started: false});
timer.clearInterval('timer');
Vibration.vibrate();
AlertIOS.alert("Time's up!");
}
_runClock() {
this.setState({started: true});
console.log("running: ", this.state.started);
timer.setInterval('timer', this._tick, 1000);
}
_stopClock() {
this.setState({started: false});
console.log("running: ", this.state.started);
timer.clearInterval('timer');
}
_getMinsSecs(seconds) {
let mins = Math.floor(seconds / 60);
let secs = seconds - mins * 60;
return (mins < 10 ? "0" : "") + mins + ":" + (secs <10 ? "0" : "") + secs;
}
_handleStartPress() {
if (!this.state.started) {
this._runClock();
} else {
this._stopClock();
}
}
_handlePomodoroPress() {
if (!this.state.started) {
this.setState({timerValue: 25*60});
}
}
_handlePausePress() {
if(!this.state.started) {
this.setState({ timerValue: 5*60 });
}
}
render() {
return (
<View style={styles.container}>
<View style={styles.timeWrapper}>
<View style={styles.line}/>
<Text style={styles.time}>{this._getMinsSecs(this.state.timerValue)}</Text>
<View style={styles.line}/>
</View>
<TouchableHighlight underlayColor="rgba(0,0,0,0)" style={styles.playButton} onPress={this._handleStartPress}>
<Image source={(this.state.started) ? require('./Control-pause.png') : require('./Control-play.png')} resizeMode="contain" style={styles.icon}/>
</TouchableHighlight>
<View style={styles.buttonWrapper}>
<Button
value="Pomodoro"
onPress={this._handlePomodoroPress}/>
<Button value="Pause" onPress={this._handlePausePress}/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "space-around",
alignItems: 'center',
backgroundColor: "#7CCF9E"
},
time: {
fontSize: 74,
color: '#fff',
fontWeight: '200'
},
buttonWrapper: {
justifyContent: 'center',
alignItems: 'center'
},
playButton: {
width: 79,
height: 79,
borderRadius: 100,
borderWidth: 3,
borderColor: '#fff',
alignItems: 'center',
justifyContent: 'center'
},
line: {
marginTop: 10,
height: 3,
width: 200,
backgroundColor: '#fff'
},
timeWrapper: {
alignItems: 'center'
},
icon: {
height: 42,
}
});
AppRegistry.registerComponent('Project', () => Project);
something like this works easily:
<TouchableHighlight underlayColor="rgba(0,0,0,0)" style={styles.playButton} onPress={this._handleStartPress}>
<Text>{this.state.started ? "started" : "stopped"}</Text>
</TouchableHighlight>
EDIT2:
I found what causes the picture not to rerender!!!!
When I style the size in the StyleSheet it won't rerender ... If it has no size style everything is fine!
require calls are not dynamic. They are statically analyzed and bundled. https://github.com/facebook/react-native/issues/2481 . As Andrew Axton suggested, load them in separate variables outside of render and using that in the conditional should work.

React native listview add item not working

I am new to react native and I was implemented a simple idea in my head. Basically, I am doing a 'todo list' like component which theres a add button below and items can be added. The problem arises after clicking on the add button and the list gets updated and the following xcode warning message appears. And I have realised, after implementing the ListView, that the app in the simulator slows down so much i couldn't even inspect. The alert popup would freeze the UI after some text are entered too, and the entire app needs to be built again since I couldn't do anything. Thanks for all the help!
Main component: SurveyQn
'use strict'
import React, {
Component,
StyleSheet,
Text,
TouchableHighlight,
TextInput,
View,
ListView,
AlertIOS
} from 'react-native';
var LongButton = require('./LongButton.js');
class SurveyQn extends Component {
constructor(props) {
super(props);
this.state = {
options: [{option: 'Pizza'}],
};
}
componentWillMount() {
this.dataSource = new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
})
}
_renderItem(item) {
return (
<LongButton
text={item.option}
onPress={() => {}}
//btnViewStyle={styles.buttonView}
//btnTextStyle={styles.buttonText}
/>
);
}
_addItem() {
AlertIOS.alert(
'Add new option',
null,
[
{
text: 'Add',
onPress: (text) => {
var options = this.state.options;
options.push({option: text})
this.setState({ options: options})
}
},
],
'plain-text'
);
}
render(){
var dataSource = this.dataSource.cloneWithRows(this.state.options);
return (
<View style={styles.container}>
<TextInput
style={styles.question}
placeholder="Question title"
placeholderTextColor="#4B667B"
selectionColor="#4B667B"
onChangeText={(text) => this.setState({text})}/>
<View style={styles.listView}>
<ListView
dataSource={dataSource}
renderRow={this._renderItem.bind(this)}/>
</View>
<TouchableHighlight
onPress={this._addItem.bind(this)}
style={styles.buttonView}
underlayColor='rgba(0,0,0,0)'>
<Text style={styles.buttonText}>
Add option
</Text>
</TouchableHighlight>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
width: 300,
flex :1,
},
listView: {
flex: 1,
},
question: {
height: 30,
fontSize: 20,
fontWeight: "100",
color: '#4B667B',
marginTop: 10,
marginBottom: 10,
},
buttonView: {
width: 300,
paddingVertical: 9,
borderWidth: 1,
borderColor: '#F868AF',
marginBottom: 13,
},
buttonText: {
textAlign: 'center',
fontSize: 25,
color: '#F868AF',
fontWeight: '500'
},
});
ListView item: LongButton
'use strict'
import React, {
Component,
StyleSheet,
Text,
TouchableHighlight,
View,
} from 'react-native';
class LongButton extends Component {
render(){
return (
<TouchableHighlight
onPress={this.props.onPress}
style={this.props.btnViewStyle}
underlayColor='rgba(0,0,0,0)'>
<Text style={this.props.btnTextStyle}>
{this.props.text}
</Text>
</TouchableHighlight>
);
}
}
module.exports = LongButton;
Xcode warning message upon adding item on alert
app[27881:11151280] the behavior of the UICollectionViewFlowLayout is not defined because:
app[27881:11151280] the item height must be less than the height of the UICollectionView minus the section insets top and bottom values, minus the content insets top and bottom values.
app[27881:11151280] The relevant UICollectionViewFlowLayout instance is <_UIAlertControllerCollectionViewFlowLayout: 0x7ff0685b1770>, and it is attached to ; layer = ; contentOffset: {0, 0}; contentSize: {0, 0}> collection view layout: <_UIAlertControllerCollectionViewFlowLayout: 0x7ff0685b1770>.
2016-04-06 07:50:01.545 decisionapp[27881:11151280] Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.
Updates:
I tried this but its not working either. Could it be the alert causing these problems? Its just taking forever to render the alert after clicking on the btn.
class SurveyQn extends Component {
constructor(props) {
super(props);
this.state = {
options: [{option: 'Pizza'}],
dataSource : new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
})
};
}
componentWillMount() {
var data = this.state.options;
this.state.dataSource.cloneWithRows(data);
}
_renderItem(item) {
return (
{item.option}
);
}
_addItem() {
AlertIOS.alert(
'Add new option',
null,
[
{
text: 'Add',
onPress: (text) => {
var options = this.state.options;
options.push({option: text})
this.setState({ options: options})
}
},
],
'plain-text'
);
}
render(){
return (
<View style={styles.listView}>
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderItem.bind(this)}/>
</View>
<TouchableHighlight
onPress={this._addItem.bind(this)}
style={styles.buttonView}
underlayColor='rgba(0,0,0,0)'>
<Text style={styles.buttonText}>
Add option
</Text>
</TouchableHighlight>
</View>
);
}
}
Before diving into complex EcmaScript notation, you can use simple notation. Here is a simple example of ListView. Please go through it and understand how it works.
var API = require('./API');
module.exports = React.createClass({
getInitialState: function(){
return {
rawData: [],
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
}),
loaded: false,
}
},
componentWillMount: function(){
this.loadData();
},
loadData: function(){
API.getItems()
.then((data) => {
this.setState({
rawData: this.state.rawData.concat(data),
dataSource: this.state.dataSource.cloneWithRows(data),
loaded: true,
});
});
},
render: function(){
return(
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderItem}
style={styles.listView}>
</ListView>
);
},
renderItem: function(item){
return (
<View>
<Text>Custom Item</Text>
</View>
);
},
}
In API.js, I am fetching data from an API.
getItems: function(){
var REQUEST_URL = 'http://api.example.org/item/get?api_key=xxxx;
return fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {
return responseData.results.sort(sortByDate);
});
}
The code may not work, since I have not tested. But you can refer my sample project on github. Repo

Resources