Unable to render flat list while searching - android

I have a list view in which list item component is designed by me.
If I render listItem alone it is working fine but after integrating ith with SearchBar I am not able to render them even though am able to get object matching a particular search
SearchListItem
import React from 'react';
import {Image, Text, View} from "react-native";
export default class SearchListItem extends React.Component
{
render()
{
return (
<View style={{flexDirection: 'row'}}>
<Image source={{uri: this.props.src}}
style={{width: 50, height: 50, backgroundColor: '#fff'}} />
<Text style={{height: 50, lineHeight: 50, width: '100%',textAlign: 'center', flex:1, backgroundColor: '#FFF'}}>{this.props.text}</Text>
</View>
)
}
}
App.js
import React from 'react';
import {FlatList, StyleSheet, Text, TextInput, View} from 'react-native';
import SearchListItem from "./components/SearchListItem";
import { SearchBar } from 'react-native-elements';
import { includes } from 'lodash';
export default class App extends React.Component {
constructor(props)
{
super(props);
this.all_categories = [
{
"id": "1",
"text": "abc",
"src": "https://cdn-images-1.medium.com/max/1200/1*dIocy2HvI_BIpziOypf8ig.jpeg"
},
{
"id": "2",
"text": "dbef",
"src": "https://cdn-images-1.medium.com/max/1200/1*dIocy2HvI_BIpziOypf8ig.jpeg"
},
{
"id": "3",
"text":"bghi",
"src":"https://cdn-images-1.medium.com/max/1200/1*dIocy2HvI_BIpziOypf8ig.jpeg"
},
{
"id": "4",
"text":"jbkl",
"src":"https://cdn-images-1.medium.com/max/1200/1*dIocy2HvI_BIpziOypf8ig.jpeg"
}
];
this.state = {text:"",
categories: []
}
}
search(text)
{
var searchResults = [];
var categories = this.all_categories;
for (var i = categories.length - 1; i >= 0; i--)
{
includes(categories[i]['text'], text) && searchResults.push(categories[i])
}
console.log("text searched is " + text);
console.log(searchResults);
return searchResults;
}
_keyExtractor = (item, index) => item.id;
_renderItem(item)
{
console.log("item to render is");
console.log(item);
return <SearchListItem text={item.text} src={item.src}/>
}
render() {
console.log("rendered");
console.log("categories to display are");
console.log(this.state.categories);
return (
<View>
<View style={{height:30,width:"100%"}}/>
<SearchBar
round
lightTheme
containerStyle={{
backgroundColor:'transparent',
borderTopWidth: 0,
borderBottomWidth: 0,
}}
placeholder="Search!"
inputStyle = {{backgroundColor:'white'}}
onChangeText={ (text) =>
{
let result = this.search(text);
console.log("changing state");
this.setState({categories:result, text:text})
}}
value={this.state.text}
/>
<FlatList style={{flex:1}}
removeClippedSubviews={false}
data={this.state.categories}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
/>
</View>
)
}
}
Searching gives all the valid results but not able to show the list corresponding to the results.What I have done wrong?

Running your code I found two little mistakes:
First one is in _renderItem param, which has to be like
_renderItem({item})
{
console.log("item to render is");
console.log(item);
return <SearchListItem text={item.text} src={item.src}/>
}
as destructuring suggest (see doc).
Second one causes your list not rendering:
try remove that style={{flex:1}} in FlatList props.
I've just created a snack if you want to check it:

Related

React Native - Encountered two children with the same key, `221`

I am trying to fetch data through API. The data is returned in array, i parsed it to JSON and it shows perfectly in Console Log. But i can't show it on screen, the console shows following warning,
Warning: Encountered two children with the same key, 221. Keys should be unique so that components maintain
their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
This is my code:
import React from 'react';
import { Container, Header, Title, Drawer, Content, Button, Left, Right, Body, Text} from 'native-base';
import { Alert, View, TouchableOpacity, SafeAreaView } from 'react-native';
import { MaterialIcons } from '#expo/vector-icons';
import { Ionicons } from '#expo/vector-icons';
import SideBar from './components/SideBar';
import { FlatList } from 'react-native-gesture-handler';
export default class QrScan extends React.Component{
constructor(props) {
super(props)
this.state = {
resourcedata:'',
};
this.resourceAllocationList = this.resourceAllocationList.bind(this);
}
closeDrawer = () => {
this.drawer._root.close();
}
openDrawer = () => {
this.drawer._root.open();
}
resourceAllocationList() {
fetch('https://api.idepoz.com/ncl/api/getResource', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
}).then((response) => response.json())
.then((responseJson) => {
if(responseJson)
{
var jsonData = JSON.stringify(responseJson.data);
var resultdata = JSON.parse(jsonData);
//console.log(resultdata);
this.setState({resourcedata:resultdata});
}
}).catch((error) => {
console.error(error);
});
}
render()
{
const getHeader = () => {
return <Text>{'Resource Allocation'}</Text>;
};
const getFooter = () => {
if (this.state.loading) {
return null;
}
return <Text>{'Loading...'}</Text>;
};
return(
<Drawer
ref={(ref) => { this.drawer = ref; }}
content={<SideBar navigator={this.navigator} closeDrawer={this.closeDrawer} usertoken=
{this.props.navigation.state.params.usertoken} />}
onClose={() => this.closeDrawer()} >
<Container>
<Header>
<Left>
<Button transparent onPress={this.openDrawer.bind(this)}>
<MaterialIcons name="list" size={40} color="#FFFFFF" />
</Button>
</Left>
<Body>
</Body>
<Right>
<Button transparent>
<Ionicons name="search" size={40} color="#FFFFFF" onPress={() =>
Alert.alert('Search Button pressed')} />
</Button>
</Right>
</Header>
<Content>
<SafeAreaView style={{
flexDirection:"row",
justifyContent:'center',
marginTop: 20,
alignItems: 'center',
marginHorizontal: 20,
}}>
<TouchableOpacity onPress={this.resourceAllocationList}>
<Text>Press Here</Text>
<FlatList data={this.state.resourcedata}
renderItem={({ item }) => {
<Text>{ item.id }</Text>
}}
ListHeaderComponent={getHeader}
ListFooterComponent={getFooter}/>
</TouchableOpacity>
</SafeAreaView>
</Content>
</Container>
</Drawer>
);
}
}
Return Data in Console shows like below:
Array [
Object {
"allocation_space": 1,
"created_at": "2021-03-26 15:49:55",
"created_by": 1,
"date": "2021-04-19",
"deleted_at": null,
"deleted_by": null,
"duration": "01:00:00",
"employee_id": 2,
"end_time": "01:05:00",
"id": 73,
"is_active": 1,
"is_payable": 1,
"order_plan_id": 1,
"price": 13,
"resources_allocation_id": 73,
"serviceuser_id": 1,
"start_time": "00:05:00",
"status": "Approved",
"updated_at": "2021-04-19 07:56:08",
"updated_by": 1,
}.........
Can anyone help how to return above data on screen ?
Try adding extraData prop to Flatlist.
"By passing extraData={selected} to FlatList we make sure FlatList itself will re-render when the state changes. Without setting this prop, FlatList would not know it needs to re-render any items because it is a PureComponent and the prop comparison will not show any changes."
https://docs.expo.dev/versions/latest/react-native/flatlist/
Also adding "key" prop to your Flatlist Text element will take away the error if you don't have multiple same ids in your data. In that case you could use index as key, but this is not the best practice.
So changing your flatlist to...
<FlatList
data={this.state.resourcedata}
extraData={this.state.resourcedata}
renderItem={({ item }) => {
<Text key={item.id}>{ item.id }</Text>
}}
ListHeaderComponent={getHeader}
ListFooterComponent={getFooter
/>
Also I have used props like: initialNumToRender={8} windowSize={16} to reduce memory consumption but this depends on your use case and list size
More information about Flatlist props: https://docs.expo.dev/versions/latest/react-native/flatlist/
(This is my first answer to question so be merciful to me)
EDIT
If braces {} are used in renderItem={} there needs to be return ()
keyExtractor={(item, index) => index.toString()}
renderItem={({ item, index }) => {
return (
<Text key={index }>{ item.id }</Text>
);
}}
Otherwise you could use normal braces () in renderItem which would look like...
keyExtractor={(item, index) => index.toString()}
renderItem={({ item, index }) => (
<Text key={index}>{ item.id }</Text>
)
}
I usually use {} and return as then I can even put logic before return () statement like console.log()

React native maps showsMyLocationButton position

I am using Expo react-native-app. I have some local data which I want show in maps. For maps I have used React-native-maps. I successfully pull the data and display in maps by using Marker. For user location I have used expo-location and expo-permission. That works fine. I have used React-native-maps one props called showsMyLocationButton, by default props boolean false. when I made it true I can able to see in IOS Emulator and i think by default position bottom-right. Android emulator I don't see the button at all. This is ios image and this android image. I want to display some data under the maps like a Bottom-Sheet, for that I have used this package. As a result it hide the ios' showsMyLocationButton.
I want to display the user current location Button like Uber. I tried lots of way to do that but could not able to achieve the goal. I want change the button icon as well but also failed. Really appreciate if someone Help me out.
I shared my code in Snack Ps. it only works in ios and Android, does not work in web View.
This is my code
import React, { useState, useEffect, useCallback } from 'react';
import {
Dimensions, StatusBar, StyleSheet, Text,
TextInput, TouchableOpacity, View, Button
} from 'react-native';
import Constants from 'expo-constants';
import Mapview, { Marker, Callout, PROVIDER_GOOGLE } from 'react-native-maps'
import * as Location from 'expo-location';
import * as Permissions from 'expo-permissions';
import datas from './datas.json'
// You can import from local files
import Animated from 'react-native-reanimated';
import BottomSheet from 'reanimated-bottom-sheet';
// or any pure javascript modules available in npm
export default function App() {
const sheetRef = React.useRef(null);
const renderContent = () => (
<View
style={{
backgroundColor: 'white',
padding: 16,
height: 450,
}}
>
<Text>Swipe down to close</Text>
<Button
title="Open Bottom Sheet"
onPress={() => sheetRef.current.snapTo(0)}
/>
</View>
);
const [state, setstate] = useState({
"latitude": 60.1098678,
"longitude": 24.7385084,
"latitudeDelta": 1,
"longitudeDelta": 1
});
useEffect(() => {
_onMapReady();
}, [_onMapReady]);
const _onMapReady = useCallback(async () => {
const { status } = await Permissions.askAsync(Permissions.LOCATION);
if (status !== `granted`) {
// console.log(`Permisson Denied`);
}
const location = await Location.getCurrentPositionAsync({ "accuracy": Location.Accuracy.High });
setstate({
...state,
"latitude": location.coords.latitude,
"longitude": location.coords.longitude
});
}, [state]);
return (
<View style={styles.container}>
<Mapview
provider={PROVIDER_GOOGLE}
initialRegion={state}
showsIndoors={true}
showsMyLocationButton={true}
zoomControlEnabled={true}
zoomEnabled={true}
zoomTapEnabled={true}
showsScale={true}
showsBuildings={true}
showsUserLocation={true}
showsCompass={true}
onMapReady={_onMapReady}
style={styles.mapStyle}>
{
datas.data?.map((i) => {
return (
<Marker
coordinate={{
"latitude": i.location.lat,
"longitude": i.location.lng
}}
animation={true}
key={i.id}
>
<Callout
style={{ "width": 100, "height": 50 }}>
<View>
<Text>{i.Property}</Text>
</View>
</Callout>
</Marker>
);
})
}
</Mapview>
<BottomSheet
ref={sheetRef}
snapPoints={[450, 300, 0]}
borderRadius={10}
renderContent={renderContent}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
"flex": 1,
"alignItems": `center`,
"justifyContent": `center`
// position: 'absolute',
},
mapStyle: {
"height": Dimensions.get(`window`).height,
"width": Dimensions.get(`window`).width
},
});

How to call function in another component from Drawer in React Native

We have the following code within I have the method createDrawerNavigator in my App.js file
const RootDrawer = createDrawerNavigator({
Home: { screen: HomeScreen },
Detail: { screen: DetailScreen },
Result: { screen: ResultScreen },
Section : { screen: SectionScreen }
},{
contentComponent : ({ navigation }) => (<SideBar navigation={navigation} />),
initialRouteName: 'Home',
navigationOptions: {
headerStyle: {
backgroundColor: '#26272d',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
},
transitionConfig: () => ({
transitionSpec: {
duration: 500,
easing: Easing.out(Easing.poly(4)),
timing: Animated.timing,
},
screenInterpolator: sceneProps => {
const { layout, position, scene } = sceneProps;
const { index } = scene;
const height = layout.initHeight;
const translateY = position.interpolate({
inputRange: [index - 1, index, index + 1],
outputRange: [height, 0, 0],
});
const opacity = position.interpolate({
inputRange: [index - 1, index - 0.99, index],
outputRange: [0, 1, 1],
});
return { opacity, transform: [{ translateY }] };
},
}),
});
And I have the screen SideBar that acts as Drawer:
import React, { Component, PureComponent } from 'react';
import { connect } from 'react-redux';
import { Image, StyleSheet, View, TouchableOpacity, Text, Linking } from 'react-native';
import { Icon } from 'native-base';
import {StackActions, NavigationActions, DrawerActions} from 'react-navigation';
import Ionicons from 'react-native-vector-icons/Ionicons';
export default class SideBar extends React.Component {
goTo = (section) => {
const resetAction = StackActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: 'Section' })
]
})
return () => this.props.navigation.dispatch(resetAction);
}
render() {
return(
<View style={styles.container}>
<View>
<View style={styles.logo}><Image source={require('./images/ln-header-bg.jpg')} style={styles.ln_logo} resizeMode="contain" /></View>
<TouchableOpacity style={styles.link_menu} onPress={() => { this.goTo('all'); }}><Text style={styles.link_menu_text}>Últimas noticias</Text></TouchableOpacity>
<TouchableOpacity style={styles.link_menu} onPress={() => { this.goTo(68); }}><Text style={styles.link_menu_text}>La Nación</Text></TouchableOpacity>
<TouchableOpacity style={styles.link_menu} onPress={() => { this.goTo(69); }}><Text style={styles.link_menu_text}>El Mundo</Text></TouchableOpacity>
<TouchableOpacity style={styles.link_menu} onPress={() => { this.goTo(70); }}><Text style={styles.link_menu_text}>Gente</Text></TouchableOpacity>
<TouchableOpacity style={styles.link_menu} onPress={() => { this.goTo(97); }}><Text style={styles.link_menu_text}>#YoParticipo</Text></TouchableOpacity>
</View>
<View>
<Text style={styles.follow_social}>Síguenos en las redes</Text>
<View style={styles.follow_social_links}>
</View>
</View>
</View>
)
}
}
In the SideBar I want to call an function located in Home Component, I tried with react navigation dispacth method but doesn't working.
What I have to call the function or navigate to another screen? Can some help me please?
Thanks!
I never used drawers from react-navigation, but I would assume that the way they work is similar to stackNavigators. So, assuming that, what you could do was to set a navigation parameter in the Home screen, for example, inside the componentDidMount() method, like so:
this.props.navigation.setParams({ 'paramName': paramValue });
and then, in the drawer, in the componentWillMount() method, you could do something like:
const var_x = this.props.navigation.getParam('paramName', null);
This way, you can either send the function itself as a parameter, or send a reference to the Home screen, and then access its methods from the drawer.
ps: on both calls, paramName needs to be a string.
ps2: in the getParam method call, the second argument, in the example, null, is the default value in case there is not a value for the requested parameter.
Again, I use this method for stackNavigators, so you might take a look at the react-navigation documentation to double check if there is any difference for drawer: https://reactnavigation.org/docs/en/drawer-navigator.html#docsNav

I want to use the data from a JSON file as the label for my radio button (react native)

I have an app that reads a JSON file. In one tab I have it coming out as a list, on another tab, I want it to show the items selected from the file as the labels for a radio button I found here:
https://www.npmjs.com/package/react-native-radio-buttons-group
Here's the code that I have for the tab of my app that pulls names of medications from a JSON file:
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
TouchableHighlight,
FlatList
} from "react-native";
import { Icon } from 'native-base'
import RadioGroup from 'react-native-radio-buttons-group';
//import MultipleChoice from 'react-native-multiple-choice'
class LikesTab extends Component {
_onSelect = ( item ) => {
console.log(item);
};
onPress = data => this.setState({ data });
constructor(props){
super(props);
this.state = {
data:[]
}
}
//SETTING THE STATE MAKING AN EMPTY ARRAY WHICH WE FIL
// state = {
// data: []
//};
componentWillMount() {
this.fetchData();
}
//Getting the data
fetchData = async () => {
const response = await fetch("https://api.myjson.com/bins/s5iii");
const json = await response.json();
this.setState({ data: json.results });
};
//var customData = require('./customData.json');
//Setting what is shown
render() {
return (
<View style={{ marginVertical: 10, backgroundColor: "#E7E7E7" }} >
<FlatList
data={this.state.data}
keyExtractor={(x, i) => i}
renderItem={({ item }) =>
<Text>
{`${item.name.first} ${item.name.last}`}
</Text>}
/>
</View>
);
}
}
export default LikesTab;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}
});
Here's my tab for the radio button
import React, { Component } from 'react';
import { Text, View, StyleSheet } from 'react-native';
import RadioGroup from 'react-native-radio-buttons-group';
export default class AddMediaTab extends Component {
componentWillMount() {
this.fetchData();
}
//Getting the data
fetchData = async () => {
const response = await fetch("https://api.myjson.com/bins/s5iii");
const json = await response.json();
this.setState({ data: json.results });
};
state = {
data: [
{
label:' ' ,
}
]
};
// update state
onPress = data => this.setState({ data });
render() {
let selectedButton = this.state.data.find(e => e.selected == true);
selectedButton = selectedButton ? selectedButton.value : this.state.data[0].label;
return (
<View style={styles.container}>
<Text style={styles.valueText}>
Value = {selectedButton}
</Text>
<RadioGroup radioButtons={this.state.data} onPress={this.onPress}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
valueText: {
fontSize: 18,
marginBottom: 50,
},
});
What is your selectedButton variable? I think Value = {selectedButton} is the issue. If selectedButton evaluates to a string, fine but it looks like it is an object in your case. Array.find() returns the first element that satisfies the condition, or if none satisfy it it returns undefined. If that variable undefined during the time it's waiting for your api call to return something that could cause an issue as well.

React Native FlatList scrollToBottom not working

Greeting everyone,
I am working on react-native FlatList and my problem is that I am unable to scroll to bottom. Here is my FlatList code:
<FlatList style = { styles.list }
ref="flatList1"
data = {
this.state.conversation_arr
}
onContentSizeChange={(contentWidth, contentHeight) => {
this.flat_list_height = contentHeight;
// this.scrollNow();
}}
onLayout={this.scrollNow()}
renderItem = { this._renderItem }
keyExtractor = { item => item.message_id }
/>
scrollNow() {
console.log("******",this.refs);
if(this.refs.flatList1){
this.refs.flatList1.scrollToEnd();
}
}
When the code reaches to
this.refs.flatList1.scrollToEnd();
Error appears on the screen saying
Type Error, cannot read property -1 of undefined.
here is the screenshot.
cannot read property -1 of undefined
I am basically trying to implement chat feature for my application, and therefor i need to scroll to bottom of the list once chat is loaded.
Any kind of help is appreciated.
Thanks
COMPLETE CODE
import React, {
Component
} from 'react';
import {
StyleSheet,
View,
Text,
TextInput,
ActivityIndicator,
TouchableOpacity,
FlatList,
} from 'react-native';
export default class Conversation extends Component {
state = {
conversation_arr: undefined,
last_message_loaded: false,
show_activty_indicator: true,
message_text : undefined,
message_text_length : 0
};
params = this.props.navigation.state;
last_evaluated_key = {
message_id: undefined,
timestamp: undefined
}
conversation = undefined;
thread_info = undefined;
flat_list_height = 0;
static navigationOptions = ({ navigation }) => ({
title: navigation.state.params.header_title,
});
constructor(props) {
super(props);
this._renderItem = this._renderItem.bind(this);
this._fetchConversation();
this.scrollNow = this.scrollNow.bind(this);
this.handleInputTextContent = this.handleInputTextContent.bind(this);
this.sendMessage = this.sendMessage.bind(this);
console.disableYellowBox = true;
this.thread_info = this.params.params.thread_info;
}
// scrollNow() {
// console.log("******",this.refs);
// if(this.refs.flatList1){
// // debugger;
// this.refs.flatList1.scrollToEnd();
// }
// console.log("******", this.flatlist1);
// if(this.flatlist1){
// this.flatlist1.scrollToEnd();
// }
// console.log("###", this.flat_list_height);
// }
scrollNow = () => {
console.log("******", this.flatList1);
if(this.state.conversation_arr && this.state.conversation_arr.length > 0 && this.flatList1){
// debugger;
// setTimeout(() => {
// console.log("timer over now");
this.flatList1.scrollToEnd();
// }, 1000)
// this.flatList1.scrollToEnd();
console.log("Scrolling now at length", this.state.conversation_arr.length);
}
// !!this.refs["myFlatList"] && this.refs["myFlatList"].scrollToEnd()
}
componentDidUpdate(){
console.log("componentDidUpdatecomponentDidUpdate")
}
componentWillUnmount(){
console.log("componentWillUnmountv")
}
render() {
return (
<View style={ styles.container }>
{
this.state.show_activty_indicator &&
<View style={[styles.activity_indicator_container]}>
<ActivityIndicator
animating = { this.state.show_activty_indicator }
size="large"
color="#444444"
/>
</View>
}
<FlatList style = { styles.list }
// inverted
// ref="myFlatList"
// ref={(ref) => this.flatList1 = ref}
ref={(ref) => { this.flatList1 = ref; }}
data = {
this.state.conversation_arr
}
onContentSizeChange={(contentWidth, contentHeight) => {
this.flat_list_height = contentHeight;
// this.scrollNow();
}}
onLayout={this.scrollNow}
renderItem = { this._renderItem }
keyExtractor = { item => item.message_id }
/>
<View style={ styles.bottom_box }>
<Text style={ styles.message_length }> {this.state.message_text_length}/160 </Text>
<View style={ styles.message_box }>
<View style={{flex: 3}} >
<TextInput
style={styles.input_text}
value={this.state.message_text}
onChangeText={this.handleInputTextContent}
placeholder='Type a message here...'
underlineColorAndroid='transparent'
/>
</View>
<View style={{flex: 1}} >
<TouchableOpacity
style={styles.send_button}
onPress={this.sendMessage} >
<Text style={[styles.label_send]}>SEND</Text>
</TouchableOpacity>
</View>
</View>
</View>
</View>
);
}
handleInputTextContent(text){
if(text.length > 160){
text = text.substring(0,159)
this.setState({ message_text : text });
} else {
this.setState({ message_text : text });
this.setState({ message_text_length : text.length });
}
}
sendMessage(){
console.log(this.state.message_text);
if(this.state.message_text && this.state.message_text.length > 0) {
console.log("Send message now");
}
}
_renderItem(item, index){
// console.log(item.item);
item = item.item;
let view = (
<Text style = { [styles.msg_common] }> { item.message_content } </Text>
)
return view ;
}
processPaymentMessages(dataArr) {
return dataArr;
}
_fetchConversation() {
new WebApi().fetchThreadConversation().then(result => {
console.log("resutlresult", result);
if (result && result.data && result.data.LastEvaluatedKey) {
this.last_evaluated_key.message_id = result.data.LastEvaluatedKey.message_id;
this.last_evaluated_key.timestamp = result.data.LastEvaluatedKey.timestamp;
} else {
this.setState({
last_message_loaded: true
});
this.last_evaluated_key = null;
}
if (!this.conversation) {
this.conversation = [];
}
for (let i = 0; i < result.data.Items.length; i++) {
item = result.data.Items[i];
this.conversation.push(item);
}
// console.log(this.conversation);
this.setState({ conversation_arr : this.conversation });
this.setState({ show_activty_indicator : false });
console.log(this.state.conversation_arr.length);
}, error => {
console.log("web api fetch data error", error);
})
}
}
const styles = StyleSheet.create({
activity_indicator_container: {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff'
},
container: {
flex: 1,
paddingTop: 8,
backgroundColor:"#fff"
},
list : {
paddingLeft: 8,
paddingRight:8,
marginBottom : 85,
},
gray_item: {
padding: 10,
height: 44,
backgroundColor:"#fff"
},
white_item : {
padding: 10,
height: 44,
backgroundColor:"#f5f5f5"
},
msg_common : {
flex: 1,
justifyContent: 'flex-end',
alignItems: 'flex-end',
},
colored_item_common : {
padding : 12,
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
item_insider_common : {
backgroundColor:"#fff",
fontSize : 16,
padding : 12,
borderWidth : 1,
borderRadius : 8,
width : "90%",
fontWeight : "bold",
textAlign: 'center',
},
purple_item_insider : {
color : "#7986cb",
borderColor: "#7986cb",
},
green_item_insider : {
color : "#66bb6a",
borderColor: "#66bb6a",
},
bottom_box:{
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
flex: 1,
},
message_length: {
height : 20,
textAlign: 'right', alignSelf: 'stretch'
},
message_box: {
// position: 'absolute',
// left: 0,
// right: 0,
// bottom: 0,
flex: 1,
flexDirection: "row"
},
input_text: {
width: '100%',
height: 60,
color: '#000',
backgroundColor:"#eee",
padding: 16,
},
send_button: {
alignItems: 'center',
flex: 1,
height: 60,
padding: 16,
backgroundColor : '#f1592b'
},
label_send: {
color: '#fff'
}
})
You need to give a callback to onLayout like this:
onLayout={this.scrollNow}
The updated code is: I tried it. It works.
import React, { Component } from 'react';
import { FlatList, Text } from 'react-native';
export default class App extends Component {
render() {
return (
<FlatList
ref={(ref) => { this.flatList1 = ref; }}
data={[0, 0, 0, 0, 0, 0, 0, 0]}
onContentSizeChange={(contentWidth, contentHeight) => { this.flat_list_height = contentHeight; }}
onLayout={this.scrollNow}
renderItem={this.renderItem}
keyExtractor={item => item.message_id}
/>
);
}
renderItem =() => <Text style={{marginTop: 100}}>Just Some Text</Text>
scrollNow =() => {
if (this.flatList1) {
this.flatList1.scrollToEnd();
}
}
}
Have you tried passing an empty array when "this.state.conversation_arr" is undefined or null. Checking this._renderItem can be helpful also, weather it is trying to access -1 of any argument.
There are 2 parts wrong in your code.
the ref in FlatList is a function, not a string.
If you don't use arrow function, the FlatList cannot access the "this" in scrollNow() function.
I tested with similar code. The code below should work.
import * as React from 'react';
import { StyleSheet, View, Text, FlatList } from 'react-native';
export default class Conversation extends React.Component {
flatList1;
_interval;
state = { conversation_arr: [] };
componentDidMount() {
let buffer = [];
for (let i = 1; i < 40; i++) {
buffer.push({ message_id: i, message_content: `I'm the message ${i}` });
}
this._interval = setInterval(() => {
this.setState(() => {
return { conversation_arr: buffer };
});
this.flatList1.scrollToIndex({
index: buffer.length - 1,
viewPosition: 1,
viewOffset: 0,
});
}, 1000);
}
componentWillUnmount() {
clearInterval(this._interval);
}
render() {
return (
<View style={styles.container}>
<FlatList
ref={ref => {
this.flatList1 = ref;
}}
data={this.state.conversation_arr}
renderItem={this._renderItem}
keyExtractor={item => item.message_id}
getItemLayout={(data, index) => ({
length: 40,
offset: 40 * index,
index,
})}
/>
</View>
);
}
_renderItem = ({ item }) => {
return (
<View>
<Text style={styles.msg_common}>{item.message_content}</Text>
</View>
);
};
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
msg_common: {
height: 40,
fontSize: 18,
paddingHorizontal: 10,
},
});
PS: If you have a long list, let's say more than 100. Using onLayout to scroll down might take some time for initial rendering, which provides a really bad user experience. The much better approach may be
to reverse the list before rendering
provide a sort button and set onRefresh to true during sorting
provide a scroll to bottom button list's footer
Update:
I noticed the onContentSizeChange property is NOT in the documentation.
https://facebook.github.io/react-native/docs/flatlist.html#scrolltoindex
Which version of React-Native are you using?
Update2:
The complete code in TypeScript based on yours. I use setInterval to simulate the delay from API call.
Any async function should be in componentDidMount, because constructor doesn't wait.
getItemLayout is used to help FlatList to figure out height. Notice that this is set before the data arrived.

Categories

Resources