show message if listview is empty react native - react-native

If List View is empty, I would like to show the header(Which is happening) and also show a alternate Text saying "List is Empty".
I tried doing getRowCount(), which returns 0. But how can I insert the text in the same view.
`
<Image source={require('./img/background.png')} style={GlobalStyles.bgImageContainer}>
<ListView style={{margin: 5}}
initialListSize={10}
dataSource={this.state.familydataSource}
renderRow={this.renderRow}
renderSeparator={::this._renderSeparator}
renderSectionHeader={this.renderFamilySectionHeader}
enableEmptySections={true}
/>
<ListView style={{margin: 5}}
dataSource={this.state.friendsdataSource}
renderRow={this.renderRow}
renderSeparator={::this._renderSeparator}
renderSectionHeader={this.renderFriendsSectionHeader}
enableEmptySections={true}
/>
</Image>
`

I initially misunderstood what you meant by header, thinking it was a component separate from the ListView. To show a separate message in addition to the ListView (instead of as a replacement to it), I would use the flex style to determine whether the ListView should take up the full height or just a percentage. In that later case you can render your message below the ListView so that both appear.
You can separate the rendering of the ListView and message into two functions like so:
_renderMessage() {
return (
<View style={{ flex:0.5 }}>
<Text>List is Empty</Text>
</View>
)
}
render() {
const listViewProportion = this.state.dataSource.getRowCount() == 0 ? 0.5 : 1
return (
<View style={{ flex:1 }}>
<ListView
style={{ flex: listViewProportion }}
...
/>
{if (listViewProportion != 1) {
this._renderMessage()
}}
</View>
)
}

Maybe this could help in order to give you an idea for what you want. Have you tried something like this?
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows([]),
}
}
renderIf(condition){
if(condition){
return (<ListView
dataSource={this.state.dataSource}
renderRow={(rowData) => <Text>{rowData}</Text>} />)
} else{
return (<Text> There is no data </Text>)
}
}
componentDidMount(){
var datos = ['John', 'Joel', 'James', 'Jimmy', 'Jackson', 'Jillian', 'Julie', 'Devin']
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.setState({dataSource: ds.cloneWithRows(datos)})
}
render(){
return(
{this.renderIf(this.state.dataSource.getRowCount())}
);
}
In this example, I imagine that we start with 0 elements in the array. So the constructor runs first, and then I declare my dataSource has 0 elements. After this, render methods is executed. As there are 0 elements, when we call to renderIf it will return the second part of the if statement. Once render method is finished, then componentDidMount method will be called to and in this method we will do all the operations in order to retrieve our data from our server o whatever. Once this method ends, our dataSource will have information, so it will render the ListView with all our information.

In the ListViewDataSource there is a property _cachedRowCount which holds a number with total rows of a ListView
Here is an example of how you could handle an empty ListView message:
render() {
return (
<View style={styles.listViewContainer}>
{this.state.dataSource._cachedRowCount > 0 // condition
? // if true
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow.bind(this)}
>
</ListView>
: // if false
<Text>Nothing found</Text>
}
</View>
)
}

Related

FlatList not rendering style dynamically

I'm currently struggling in making my FlatList applying the changes I do to it. What I am wanting right now is that when I click an item in my flatlist, that it highlights in a certain color. I followed an approach done by a guy but I am having the problem that to me is not working the update once I click.
I can see through console that all I am doing performs a modification but I think that I am missing some point with extraData parameter since it is not re-rendering with the backgroundColor that I would like to apply.
The code I have is as following, I know that the style I am applying is correct since if i substitute in the map styles.list per styles.selected, everything gets the background I would like to be applied to the elements I click.
So summarizing, the issue I think I have is that the flatlist is not re-rendering so it doesn't show the modifications I perform on it. Any idea of what I am doing wrong? Any tip?
render() {
const { students, studentsDataSource, loading, userProfile } = this.props.navigation.state.params.store;
this.state.dataSource = studentsDataSource._dataBlob.s1.map(item => {
item.isSelect = false;
item.selectedClass = styles.list;
return item;
})
const itemNumber = this.state.dataSource.filter(item => item.isSelect).length;
return (
<View style={styles.container}>
<Item rounded style={styles.searchBar}>
<Input placeholder='Group Name'/>
</Item>
<FlatList
style={{
flex: 1,
width: "100%",
}}
data={this.state.dataSource}
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={ ({ item }) => (
<ListItem avatar style={[styles.list, item.selectedClass]}
onPress={() => this.selectItem(item)}>
<Left>
{!item.voteCount && <Avatar unseen={true} /> }
{!!item.voteCount > 0 && <Avatar />}
</Left>
<Body>
<Text>{item.name}</Text>
<Text note>{item.group}</Text>
</Body>
</ListItem>
)
}
listKey={item => item.key}
extraData={this.state}
/>
</View>
);
}
Here we can find the state and SelectItem functions:
constructor(props) {
super(props)
this.state = {
dataSource : [],
}
}
//FlatListItemSeparator = () => <View style={styles.line} />;
selectItem = data => {
//{console.log("inside SelectItem=", data)}
data.isSelect = !data.isSelect;
data.selectedClass = data.isSelect? styles.selected: styles.list;
const index = this.state.dataSource.findIndex( item => data.key === item.key);
this.state.dataSource[index] = data;
this.setState({
dataSource: this.state.dataSource,
});
console.log("This state has the changes:=",this.state.dataSource)
};
Well the main issue was that I was not using the .setState and instead I was doing assignations which killed the listeners.

ReactNative - FlatList not updated until scroll

I have a problem with FlatList component which does not update until scrolled.
I tried add log to renderItem and keyExtractor both methods called with correct data but list didn't update.
Here is a render method:
render() {
const messages = this.props.messages
const message = this.props.message
return (
<View style={[styles.container]}>
<FlatList
ref={"flatList"}
contentContainerStyle={styles.list}
data={messages}
renderItem={(listItem) => {
return <MessageBuble message={listItem.item}/>
}}
keyExtractor={(item: Message) => {
return item.id
}}
/>
<View style={[styles.textInputContainer]}>
<TextInput
style={styles.textInput}
value={message}
multiline={true}
onChangeText={this.props.messageChanged}
/>
<Button title={"Odeslat"} onPress={() => {
if (this.props.sendMessage) {
this.props.sendMessage(this.props.message)
}
}}/>
</View>
</View>
)
}
Add extraData in FlatList and retry
<FlatList
extraData={this.props}
....
Tried the extraData, but that does not work.
There was an issue on Android where content was not visible when I returned back from another page to home screen (where the flatlist was present). The content was visible when I scrolled it a bit.
I assigned the main list to the extraData attribute, and could see that it changed in size via console logs. But the content remained invisible. Finally, used
onContentSizeChange={() => {
if (list.length > 0) {
ref.current.scrollToOffset({ animated: true, x: 0 });
}
}}
and it worked.

How to get scrollY point in React Native

I'm building a header and I want to hide the header content UNTIL the user scrolls down to the certain point.
(My Approach)
Get scrollY position (How much did user scroll vertically from top?)
if scrollY is greater than 50
show the header name.
Here is render() method. I manually built header. I'm getting scrollY value and set it to this.state.scrollY
<View style={{flex:1}}>
<View style={styles.header}>
{this._renderHeader(profile.username)}
</View>
<ScrollView style={styles.root}
onScroll={(event)=>{this.setState({scrollY: event.nativeEvent.contentOffset.y})}}>
// setting this.state.scrollY for scroll Y position
...
Here is _renderHeader function. (rendering header)
_renderHeader = (username) => {
return (
<View style={styles.headerLayout}>
<View rkCardHeader style={styles.left}>
...
<View style={{justifyContent: 'center'}}>
{this._renderUsername(username)}
// I want to show this string ONLY IF user
// scrolls down the window to the certain point.
</View>
....
</View>
);
}
(Here is the problem) This doesn't render only or it doesn't trigger _renderUsername.
_renderUsername = (username) => {
let show = (this.state.scrollY > 50) ? true : false;
if(show) {
return (
<RkText rkType='header3'>{username}</RkText>
);
} else {
return <View />
}
}
You can keep a track of scrollY in redux store and map it to the header component.
While rendering the header component use ternary expression to decide whether to render or not.
For example -
(From the store you will get the value this.props.scrollY)
render() {
return (
{this.props.scrollY > 50 ? <AppHeader /> : ""}
);
}

Raw text "" must be wrapped in explicit text component

I am using a react native component listview to render nested items as follows:
constructor() {
super();
this.renderRow = this.renderRow.bind(this);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows([
[
{'main':'q', 'sub': 'y'}
],
[
{'main':'x', 'sub': 'f'},
{'main':'c', 'sub': 'b'}
]
]),
};
}
renderRow(rowData, section, row) {
const total = this.state.dataSource.getRowCount();
let rowView = rowData.map( x => {
return <View> <Text>{x['main']}</Text> <Text>{x['sub']}</Text> </View>
})
return (
<View>
{rowView}
</View>
);
}
render() {
return (
<View style={styles.container}>
<ListView style={styles.listView}
dataSource={this.state.dataSource}
renderRow={this.renderRow}
/>
</View>
);
}
But I am getting following error:
Raw text "" must be wrapped in explicit text component.
I am unable to track where I am getting this error from.
let rowView = rowData.map( x => {
return <View> <Text>{x['main']}</Text> <Text>{x['sub']}</Text> </View>
})
Remove spaces between View And Text Components. Use tab and enter instead of space character.
let rowView = rowData.map( x => {
return <View>
<Text>{x['main']}</Text>
<Text>{x['sub']}</Text>
</View>
})
To solve this, I used regex search & replace in entire file, and replacing >(\s)+<Text with ><Text works.
Mainly this issue arise due to space between <View> <Text> etc
A trick fix for this issue could be execute Format Document (I am using VScode (IDE), you can try with any IDE which have code styling capability). By Style fixing spaces between tags gets automatically removed.

React Native list view data parsing from JSON

This is the JSON im trying to parse and show in my list view.
The data I would like to show on my list view is ZoneInfo["Name"] as a section header. For the list view, there would be 3 text showing the Name,QueueTime or ShowTime.
I have my JSON saved in my state variable.
This is the code I've been trying to retrieve the data from the JSON.
{this.state.loading? <Spinner /> : <List dataArray={this.state.results.items} renderRow={(item) =>
<ListItem>
<Text>{item}</Text>
</ListItem>
} />}
Anyone can guide me on how I can parse the JSON and show it on my listview?
There are for sure multiple ways of achieving this, up till now I'm pretty happy using it this way:
I'm sure you will find your way around ;-)
export default class extends React.Component {
constructor(props) {
super(props);
this.renderRow = this.renderRow.bind(this);
this.renderSectionHeader = this.renderSectionHeader.bind(this);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2,
sectionHeaderHasChanged: (s1, s2) => s1 !== s2});
this.state = {
dataSource: ds.cloneWithRowsAndSections({}),
};
}
componentDidMount(){
... load your json and assign data to the state
this.setState({
dataSource: this.state.dataSource.cloneWithRowsAndSections(spots)
});
}
renderRow(rowData: string, sectionID: number, rowID: number) {
return (
<TouchableOpacity onPress={()=>this.onRowPress(rowData)}>
... your row content
</TouchableOpacity>
)
}
renderSectionHeader(sectionData, category) {
return (
<View style={styles.rowHeaderContainer}>
... your header content
</View>
)
}
render(){
return (
<View style={styles.container}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
enableEmptySections={true}
renderSectionHeader={this.renderSectionHeader}
/>
</View>
);
}
}