React Native Listview data does not refresh when state changed - android

I know there are 1000 of similar questions like this but even when trying out different examples I can't get this to work, apologies in advance.
When I pull to refresh my data the request is sent and the new response is received (As verified per my logs). However, this data is not reflected in the list itself. I understand the listview is clever enough to refresh the data when there is a change to the datasource but it doesn't seem to be happening here.
constructor(props) {
super(props);
const dataSource = new ListView.DataSource({rowHasChanged: (row1, row2) => row1 !== row2});
this.state = {
dataSource: dataSource.cloneWithRows([]),
refreshing: false
};
this._renderRow = this._renderRow.bind(this);
this._getCoinData = this._getCoinData.bind(this);
}
componentWillMount() {
this._getCoinData();
}
_getCoinData() {
return new Promise((resolve) => {
getCryptocurrencyData()
.then(function (result) {
const ds = new ListView.DataSource({rowHasChanged: (row1, row2) => row1 !== row2});
this.setState({
dataSource: ds.cloneWithRows(result),
jsonData: result
});
resolve();
}.bind(this))
});
}
_renderRow(data) {
return (
<CoinCell
coinName={data.name}
coinPrice={data.price_gbp}
coinPercentageChange={data.percent_change_24h}>
</CoinCell>)
}
_renderHeader() {
return (
<Header />
)
}
_onRefresh() {
this.setState({refreshing: true});
this._getCoinData()
.then(() => {
this.setState({refreshing: false});
});
}
render() {
return (
<View>
<ListView
enableEmptySections
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this._onRefresh.bind(this)}
/>
}
ref={'resultListView'}
dataSource={this.state.dataSource}
renderRow={this._renderRow}
renderSeparator={(sectionId, rowId) => <View key={rowId} style={styles.separator}/>}
renderHeader={() => this._renderHeader()}
/>
</View>
);
}
}

used custom ScroolView is the best solution to rerender list data ti view.
dataSource = [ ['SectionAScene',strings.SectionA,false],
['SectionBScene',strings.SectionB,false],
// ['SectionC1Scene',strings.SectionC1,false],
['SectionD1Scene',strings.SectionD1,false],
// ['SectionEScene',strings.SectionE,false],
['SectionFScene',strings.SectionF,false],
// ['SectionGScene',strings.SectionG,false],
['SectionH1Scene',strings.SectionH,false],
['SectionIScene',strings.SectionI,false],
['SectionJScene',strings.SectionJ,false],
['SectionKScene',strings.SectionK,false],
['SectionLScene',strings.SectionL,false],
['SectionMScene',strings.SectionM,false],
['SectionNScene',strings.SectionN,false],
['SectionOScene',strings.SectionO,false],
['SectionPScene',strings.SectionP,false],
['SectionQScene',strings.SectionQ,false],
['SectionQ1Scene',strings.SectionQ1,false],
['SectionRScene',strings.SectionR,false],
['SectionSScene',strings.SectionS,false],
];
render() {
let data = this.state.dataSource;
return (
{data != [] ?
{this._renderHeader()}
<ScrollView>
{ data.map( (item,index) =>
<TouchableNativeFeedback onPress={this._onPressButton.bind(this,item)}>
<View style={item[2]? styles.itemSelected : styles.item}>
<Text>{item[1]}</Text>
<View style={{flex:1}} />
<Icon name={item[2]?"check":"chevron-right"} size={16} color="#444"/>
</View>
</TouchableNativeFeedback>
)}
</ScrollView>
:<View />}
</View>
);
}

Related

Refresh Control not working on Android -React Native

Here is My Code
<FlatList
refreshControl={
<RefreshControl
enabled={true}
refreshing={loader}
onRefresh={() => getLockerHistory(1)}
tintColor={ThemeColors.primary}
/>
}
ListEmptyComponent={noDataMessage()}
onScroll={(e) => {
if (Platform.OS == 'ios') {
return;
}
let paddingToBottom = 20;
paddingToBottom += e.nativeEvent.layoutMeasurement.height;
if (e.nativeEvent.contentOffset.y >= e.nativeEvent.contentSize.height - paddingToBottom) {
getNextRecordsPage();
}
}}
ListFooterComponent={() => {
return (
<ActivityIndicator color={ThemeColors.black} animating={footerLoader} />
);
}}
ListFooterComponentStyle={footerLoader ? { marginVertical: 20 } : {}}
ListFooterComponentStyle={{ paddingVertical: 20 }}
onEndReached={() => {
if (Platform.OS == 'ios') {
getNextRecordsPage()
}
}}
onEndReachedThreshold={Platform.OS == 'ios' ? 0 : null}
keyExtractor={(item, index) => item.lockerCode + '' + index}
data={lockers}
renderItem={(itemData) => {
return renderItem(itemData.item, props.navigation);
}}
/>
When I have more then 5 records which means there is not empty space left on screen then the refresh control won't work. It only works the seperator space between cells.
And my cell is made up of plain views nothing fancy or touchable.
Note: I tried to debug it with empty view but seems like pull/drag to refresh is not being listen by Flat List.
Any help?
Below code is working for me to achieve Refresh Control
import { View, Text, RefreshControl, FlatList } from 'react-native'
<FlatList
testID="DiscussionView"
initialNumToRender={5}
maxToRenderPerBatch={5}
windowSize={11}
data={posts}
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={this.onRefresh} />}
keyExtractor={this.keyExtractor}
refreshing={refreshing}
renderItem={this.renderPostItem}
onEndReached={this.onEndReached}
scrollEnabled={scrollEnabled}
onEndReachedThreshold={0.3}
contentContainerStyle
listKey="DiscussionViewList"
onMomentumScrollBegin={() => {
this.onEndReachedCalledDuringMomentum = false
}}
/>
onRefresh = () => {
this.setState(
{ refreshing: true },
// Do what you want,
)
}
keyExtractor = (item) => String(item.id)
renderPostItem = ({ item }) => {
// Render Item here
}
onEndReached = () => {
if (!this.onEndReachedCalledDuringMomentum) {
// Call API again if needed by use case
this.onEndReachedCalledDuringMomentum = true
}
}

How can I send images/video and voice messages in React-native-Gifted-chat using render actions?

I want to send images/video in chat application, developed using React-native-gifted-chat and Firebase, How can I create action for them and call that actions to upload in firebase and send images/video?
Here is my code.
handleSendImage = () => {
console.log("handleSendImage");
};
renderActions(props) {
return (
<Actions
{...props}
// containerStyle={{
// width: 70,
// }}
icon={() => (
<Icon
name={"camera"}
size={30}
color={colors.btnBg}
font={"FontAwesome"}
onPress={this.handleSendImage}
/>
)}
onSend={(args) => console.log(args)}
/>
);
}
<GiftedChat
placeholder={"Hey!"}
alwaysShowSend
messages={messages}
onSend={(newMessage) => this.onSend(this.chatID(), newMessage)}
renderInputToolbar={this.renderInputToolbar}
renderActions={this.renderActions}
user={{
_id: senderId,
name: senderName,
}}
/>
How can I click on particular actions and send voice and images/video respectively?
Gifted chat has renderActions property itself so just need to create custom action to upload image/video and voice.
Here, I am attaching code for upload documents like PDF/Doc file.
To upload image/video you just need to change that package instead of I've used document-picker
const renderActions = (props) => {
return (
<Actions
{...props}
options={{
['Document']: async (props) => {
try {
const result = await DocumentPicker.pick({
type: [DocumentPicker.types.pdf],
});
console.log("image file",result)
} catch (e) {
if (DocumentPicker.isCancel(e)) {
console.log("User cancelled!")
} else {
throw e;
}
}
},
Cancel: (props) => { console.log("Cancel") }
}}
onSend={args => console.log(args)}
/>
)
};
Gifted-chat component
<GiftedChat
messages={messages}
showAvatarForEveryMessage={true}
onSend={messages => onSend(messages)}
renderActions={() => renderActions()}
user={{
_id: 2,
name: 'React Native',
avatar: 'https://placeimg.com/140/140/any',
}}
renderCustomView={renderCustomView}
/>

React Native Picker Item Issue

I'm having serious issues with the RN Picker Item, whenever I try to load the picker Items I get the following error.
undefined is not an object (evaluating 'this.inputProps.value')
Here us the screenshot.
This is my code - Component - Basic
import React, { Component } from 'react';
import { Picker } from 'react-native';
export default class Basic extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
var options = this.props.list.map((item, key) => {
return <Picker.Item label={item} value={item} key={key} /> ;
});
return (
<Picker mode="dropdown" selectedValue={this.props.selected} supportedOrientations={['portrait','landscape']} {...this.props}>
{ this.props.default && <Picker label={this.props.default} value=""/> }
{ options }
</Picker>
);
}
}
File - Dynamic OptionSet
This will use the Basic component to display the Picker.
class DynamicOptionSets extends Component {
constructor(props) {
super(props);
this.state = {};
this.ucfirst = this.ucfirst.bind(this);
this._renderMain = this._renderMain.bind(this);
this._renderSpinner = this._renderSpinner.bind(this);
}
componentWillMount() {
InteractionManager.runAfterInteractions(() => {
this.props["get"+this.ucfirst(this.props.option)]();
});
}
ucfirst(string)
{
return string.charAt(0).toUpperCase() + string.slice(1);
}
render() {
return (
<View>
{this._renderSpinner()}
{this._renderMain()}
</View>
);
}
_renderMain(){
if(!this.props[this.props.option]['data']){
return null;
}
return (
<Basic list={this.props[this.props.option]['data']} { ...this.props }/>
)
}
_renderSpinner(){...}
}
const mapDispatchToProps = (dispatch, ownProps) => {
var {getCountries, getStates,
getDepartments, getBranches,
getBusinessSectors, getGenPostingGroup,
getCustPostingGroup, getVatPostingGroup,
getPricelist, getSalesPersons
} = ActionCreators;
return bindActionCreators({
getCountries, getStates,
getDepartments, getBranches,
getBusinessSectors, getGenPostingGroup,
getCustPostingGroup, getVatPostingGroup,
getPricelist, getSalesPersons
}, dispatch);
}
const mapStateToProps = (state) => {
var {
countries, countriesUpdate,
states, statesUpdate,
departments, departmentsUpdate,
branches, branchesUpdate,
businessSectors, businessSectorsUpdate,
genPostingGroup, genPostingGroupUpdate,
ccustPostingGroup, ccustPostingGroupUpdate,
vatPostingGroup, vatPostingGroupUpdate,
pricelist, pricelistUpdate,
salesPersons, salesPersonsUpdate,
} = state;
return {
countries, countriesUpdate,
states, statesUpdate,
departments, departmentsUpdate,
branches, branchesUpdate,
businessSectors, businessSectorsUpdate,
genPostingGroup, genPostingGroupUpdate,
ccustPostingGroup, ccustPostingGroupUpdate,
vatPostingGroup, vatPostingGroupUpdate,
pricelist, pricelistUpdate,
salesPersons, salesPersonsUpdate,
}
}
export default connect(mapStateToProps, mapDispatchToProps)(DynamicOptionSets);
So now I can call the dynamic option set like a regular picker component only and specify the data group (option)
<DynamicOptionSets option="salesPersons" mode="dropdown" onValueChange={this._updateValue.bind(this, 'salesperson')} selectedValue={this.state.form_data.salesperson} />
I don't understand why this is happening as this is the exact way I render Pickers dynamically in RN. I have gone through the doc and followed the instructions as specified.
NB: I'm dynamically loading the picker so it's inside a component I'm calling whenever I need to, display a picker that should explain the {... this.props} on the picker component.
You have a basic mistake in your code.
render() {
var options = this.props.list.map((item, key) => {
return <Picker.Item label={item} value={item} key={key} /> ;
});
return (
<Picker mode="dropdown" selected={this.props.selected} supportedOrientations={['portrait','landscape']}>
{/*_________________^^^^^^^^____ You should place `selectedValue` here instead */}
{ this.props.default && <Picker.Item label={this.props.default} value=""/> }
{ options }
</Picker>
);
}

passProps throws 'Error calling RCTEventEmiter.receiveTouches' (react-native-navigation)

I'm trying to pass some data to a modal screen with react-native-navigation pacakage 1.1.65 (https://github.com/wix/react-native-navigation)
I have two cases :
First one
export default class SearchTab extends Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dicoDataSource: ds.cloneWithRows(realm.objects('User')),
searchText:'',
data:[]
}
}
onPressButton() {
var resultData = this.state.data;
if(resultData.length > 0){
console.log("RESULTDATA", resultData);
this.props.navigator.showModal({
title: "Modal",
screen: "App.SearchResult",
passProps: {
result: resultData,
}
});
}
}
When I clicked the button, it fires me this error :
'Error calling RCTEventEmiter.receiveTouches'
The log "RESULTDATA" is something like that with one or several items :
RESULTDATA', { '0':
{ id: 1,
name: 'Leanne Graham',
username: 'Bret',
email: 'Sincere#april.biz'
} }
Second one
export default class SearchTab extends Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dicoDataSource: ds.cloneWithRows(realm.objects('User')),
searchText:'',
data:[]
}
}
onPressButton() {
var resultData = this.state.data;
if(resultData.length > 0){
console.log("RESULTDATA", resultData);
this.props.navigator.showModal({
title: "Modal",
screen: "App.SearchResult",
passProps: {
result: resultData.name, <== HERE THE ONLY DIFFERENCE
}
});
}
}
With this code, the modal screen shows up but when I log this.props.result it shows undefined.
componentDidMount(){
console.log("PROPS", this.props.result);
}
I would like to use this data to make a ListView in the modal screen which works fine.
No idea what to do with that. I already tested separately some UI elements and with different combinations like described above.
And I want to have the first one to work.
Any suggestion would be highly appreciated.
EDIT
Nobody ?
EDIT 2
Here my SearchResult class:
import React, {Component} from 'react';
import {
TextInput,
View,
TouchableOpacity,
StyleSheet,
TouchableHighlight,
Text,
Button
} from 'react-native';
import realm from '../realmDB/realm';
import { ListView } from 'realm/react-native';
import {Navigation} from 'react-native-navigation';
import EStyleSheet from 'react-native-extended-stylesheet';
export default class SearchResult extends Component {
static navigatorStyle = {
leftButtons: [{
title: 'Close',
id: 'close'
}]
};
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
resultDataSource: ds.cloneWithRows(this.props.result),
searchText:'',
data:[]
}
}
renderRow(rowData, sectionId, rowId, highlightRow){
return(
<View style={styles.row}>
<Text style={styles.rowText}>{rowData.username}</Text>
</View>
)
}
render() {
return (
<View style={styles.container}>
<TextInput style = {styles.searchText}
placeholder="Type your research"
autoCorrect={true}
returnKeyLabel="search"
underlineColorAndroid="black"
placeholderTextColor="black"
value = {this.state.searchText}
onChange={this.setSearchText.bind(this)}
/>
<TouchableOpacity onPress = {() => this.onPressButton(this.state.searchText)}>
<Text style={styles.button}>SEARCH</Text>
</TouchableOpacity>
<ListView
navigator={this.props.navigator}
enableEmptySections={true}
dataSource={this.state.resultDataSource}
renderRow={this.renderRow.bind(this)}
renderSeparator={(sectionId, rowId) => <View key={rowId} style={styles.separator} />}
/>
</View>
);
I also open an issue here: https://github.com/wix/react-native-navigation/issues/1249
Make sure that you are passing the 'result' props from the 'App.SearchResult' to the 'SearchTab' component when you are rendering it in the screen component.
Ok, it was not a context losing problem. It was about the data structure I used. I had to make nested objects in order to pass the data.
I was trying to pass a wrong format/structure of data that react-native-navigation package did not allow. Only an object can be passed

React-Native ListView: cannot read property of undefined

I am using SQLite as the device's database. What I am trying to basically achieve is this:
1- Give a user the ability to star his favorite "data"
2- Once the data gets saved in the db, retrieve it inside another page and insert them into a listView for the user to see at any time.
But no matter how much I try, I am always getting the same error.
Cannot read property of undefined.
The code:
import React, { Component } from 'react'
import {
View,
Text,
ListView
} from 'react-native'
var SQLite = require('react-native-sqlite-storage')
var db = SQLite.openDatabase({ name: "RHPC.db", location: "default"})
var obj;
class Schedules extends Component {
constructor(props) {
super(props)
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
});
this.state = {
datasource: []
}
db.transaction((tx) => {
tx.executeSql("SELECT * FROM schedules", [], (tx, res) => {
let len = res.rows.length;
if(len > 0) {
for(let i = 0; i < len; i++) {
var obj = [{id: res.rows.item(i)["id"], title: res.rows.item(i)["title"]}]
}
this.setState({
datasource: obj
})
} else {
console.log("empty")
}
})
}, (err) => {
console.log("error: " + JSON.stringify(err))
})
}
_renderRow(rowData) {
return(
<View>
<Text key={rowData.id}>
{rowData.title}
</Text>
</View>
)
}
render() {
console.log(this.state.datasource);
return(
<View style={{marginTop: 150}}>
<ListView
dataSource={this.state.datasource}
renderRow={this._renderRow.bind(this)}
/>
</View>
);
}
}
const styles = {
}
export default Schedules;
When I try to console.log the dataSource state:
0: Object
id: 2
title: "Session 1: Transition from Humanitarian Assistance to Rebuilding Health & Health Systems."
So in other words it looks like it's working but not 100%? Because I do have two rows inside that table and it's only retrieving the last one. Is this the cause of the undefined issue?
You use ListView in a wrong way, you create new dataSource in constructor (ds) and not assign it anywhere, checkout example in documentation: https://facebook.github.io/react-native/docs/listview.html
It should be:
constructor(props) {
super(props)
this.state = {
dataSource: new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}),
}
}
And in setState make something like this:
this.setState({
datasource: this.state.dataSource.cloneWithRows(obj)
})
Edit:
And in your for loop you should have:
var obj = [];
for(let i = 0; i < len; i++) {
obj.push({id: res.rows.item(i)["id"], title: res.rows.item(i)["title"]});
}

Categories

Resources