I'm trying to use the React Native <ListView /> component with the <List /> and <ListItem /> component from React Native Elements but the <ListItem /> component isn't displaying. Not entirely sure why. Shouldn't my renderRow function be running for every object in my array and returning <Listitem />? My data is coming in fine.
Please let me know what I'm doing wrong. Thanks! Code is below
import React, { PropTypes, Component } from 'react'
import { View, Text, ListView, StyleSheet } from 'react-native'
import { connect } from 'react-redux'
import { List, ListItem } from 'react-native-elements'
import { getMakeData } from '~/redux/modules/data'
class Make extends Component {
static propTypes = {
dispatch: PropTypes.func.isRequired,
makeData: PropTypes.array.isRequired
}
constructor (props) {
super(props)
this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2 })
this.state = {
dataSource: this.ds.cloneWithRows(this.props.makeData)
}
}
componentDidMount () {
this.props.dispatch(getMakeData())
}
renderRow = (item) => {
return (
<ListItem
key={item.id}
title={item.name}
/>
)
}
render () {
console.log(this.props.makeData)
return (
<List style={{flex: 1}}>
<ListView
renderRow={item => this.renderRow(item)}
dataSource={this.state.dataSource}
/>
</List>
)
}
}
function mapStateToProps ({data}) {
return {
makeData: data.makeData
}
}
export default connect(mapStateToProps)(Make)
const styles = StyleSheet.create({
})
It looks like your issue is you are not using renderRow correctly. Based on your description, makeData is an array of objects, so in your render function, you call ListView with that array, but renderRow should only render single row and should be passed in the data for each row. So, change your renderRow and render function like below
renderRow (item) {
return (
<ListItem
key={item.id}
title={item.name}
/>
)
}
render () {
return (
<List style={{flex: 1}}>
<ListView
renderRow={(item) => this.renderRow(item)}
dataSource={this.props.makeData}
/>
</List>
)
}
What is happening now is that you are telling renderRow here is the object you should be using.
What you had before is you are trying to render ListItem using the array makeData, where you should be using a single object to render the row.
fix bug:
renderRow={this.renderRowt} -> renderRow={this.renderRow.bind(this)}
refactor code
function mapStateToProps ({ data }) {
return {
makeData: data.makeData
}
}
->
1-
function mapStateToProps ({ data:{makeData} }) {
return {
makeData,
}
}
export default connect(mapStateToProps)(Make)
2-
const mapStateToProps = ({ data:{makeData} }) => makeData
export default connect(mapStateToProps)(Make)
3-
export default connect(({ data:{makeData} }) => makeData)(Make)
Related
I'm trying to display a flat list (values form json placeholder) filterable with a search bar and it's not rendering for some reason. The values are not visible. Thanks!
The flat list code:
import React, { Component } from "react";
import { View, Text, FlatList, Button } from "react-native";
import { ListItem, SearchBar } from "react-native-elements";
class FlatListDemo extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
temp: [],
error: null,
search: null
};
}
componentDidMount() {
this.getData();
}
getData = async () => {
const url = `https://jsonplaceholder.typicode.com/users`;
this.setState({ loading: true });
try {
const response = await fetch(url);
const json = await response.json();
this.setResult(json);
} catch (e) {
this.setState({ error: 'Error Loading content', loading: false });
}
};
setResult = (res) => {
this.setState({
data: [...this.state.data, ...res],
temp: [...this.state.temp, ...res],
error: res.error || null,
loading: false
});
}
renderHeader = () => {
return <SearchBar placeholder="Search Here..."
lightTheme round editable={true}
value={this.state.search}
onChangeText={this.updateSearch} />;
};
updateSearch = search => {
this.setState({ search }, () => {
if ('' == search) {
this.setState({
data: [...this.state.temp]
});
return;
}
this.state.data = this.state.temp.filter(function(item){
return item.name.includes(search);
}).map(function({id, name, email}){
return {id, name, email};
});
});
};
render() {
return (
this.state.error != null ?
<View style={{flexDirection: 'column',justifyContent: 'center', alignItems: 'center' }}>
<Text>{this.state.error}</Text>
<Button onPress={
() => {
this.getData();
}
} title="Reload" />
</View> :
<FlatList
ListHeaderComponent={this.renderHeader}
data={this.state.data}
keyExtractor={item => item.email}
renderItem={({ item }) => (
<ListItem
roundAvatar
title={`${item.name}`}
subtitle={item.email}
/>
)}
/>
);
}
}
export default FlatListDemo;
Importing this list to:
import React, {useState, useEffect} from 'react'
import { Text, View, StyleSheet, StatusBar, SafeAreaView } from "react-native"
import "firebase/auth";
import 'react-native-gesture-handler';
import "firebase/auth";
import 'firebase/compat/auth';
import 'firebase/compat/firestore';
import FlatListDemo from './FlatListDemo';
const Home: () => React$Node = () => {
return(
<>
<StatusBar barStyle="dark-content"/>
<SafeAreaView style={{flex: 1}}>
<FlatListDemo/>
</SafeAreaView>
</>
);
};
export default Home;
This is what it looks like (values should be there). Using Expo on Android:
photo1
Thanks! I appreciate the help!
It seems to me that you are using a ListElement as it is defined in version 1.2.0 of react-native-elements but the actual version that you are using is > 1.2.0.
You are implementing it similar to the documentation of react-native-elements 1.2.0.
However, the definition of ListItem has changed in newer version. In the newest version, the ListItem component is just a simple view wrapper. Hence, it needs to contain children in order to display the information that you want.
In your case this could be implemented as follows (I did not know what roundAvatar is doing).
renderItem={({ item }) => (
<ListItem>
<ListItem.Content>
<ListItem.Title>{`${item.name}`}</ListItem.Title>
<ListItem.Subtitle>{item.email}</ListItem.Subtitle>
</ListItem.Content>
</ListItem>
Check the documentation of the newest version for the exact features that you want to use.
I'm developing a component to publish it in npm, but I'd like to call my component using a method instead of a tag.
Example:
myComponent.js
import React from 'react'
import { View, Text } from 'react-native'
export const showComponent = () => {
// this would be the function that I user to call my component down
}
const myComponent = (props) => {
return(
<View>
<Text>Oi</Text>
</View>
)
}
App.js
import React from 'react'
import { View, Text, TouchableOpacity } from 'react-native'
import { showComponent } from 'my-component'
const App = () => {
return(
<View>
<TouchableOpacity onPress={() => showComponent()}>
<Text>Home</Text>
</TouchableOpacity>
</View>
)
}
export defaul App
the idea is that when calling the showComponent function I show my component, and when I call, for example, the hide function, I close my component.
You can do it using a single class export:
import * as React from 'react';
export default class MyComponent extends React.Component {
state = {
isOpen: false,
};
open = () => {
this.setState({ isOpen: true });
};
close = () => {
this.setState({ isOpen: true });
};
render() {
const { isOpen } = this.state;
return !isOpen ? null : (
<View>
<Text>Oi</Text>
</View>
);
}
}
And you use it like so:
<MyComponent ref={(x) => this.myComponent = x)} />
And you open it like so:
this.myComponent.open();
I see in a comment above you want to call the component with a redux action, so you should call your redux action in that on click, but the component you want to show/hide needs to be linked to a redux state variable. Then in your jsx you'd have:
<View>
<TouchableOpacity onPress={() => showComponent()}>
<Text>Home</Text>
</TouchableOpacity>
{reduxBoolean && <MyComponent />}
</View>
import React from 'react'
import { View, Text} from 'react-native'
const example = (props) => {
return (
<View>
<Text>Hello</Text>
</View>
)
}
// props
import React from 'react'
import { View, Text} from 'react-native'
const examples = () => {
return(
<View>
<Text><example/></Text>
</View>
)
}
and print is : Hello
I'm working on a very simple react-native app where I type the name of an artist in a searchbox, retrieve a list of artists from the spotify API and I display this list in FlatList component.
I manage to get the list of artists and I want to save it in the local state so that I pass it to the FlatList component.
The list object looks like this : [{...}, {...}, {...}, {...}]
But it doesn't seem to work and I think that my state is not updating and I don't know what I'm doing wrong.
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
FlatList,
StatusBar,
TextInput,
} from 'react-native';
import colors from './utils/colors';
import { List, ListItem, SearchBar } from 'react-native-elements';
import { searchArtist } from './utils/fetcher';
import { debounce } from 'lodash';
export default class spotilist extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
query: '',
artists: [],
error: null,
refreshing: false,
};
}
render() {
return (
<View style={ styles.container }>
<StatusBar barStyle="light-content" />
<TextInput style={ styles.searchBox }
value={this.state.value}
onChangeText={ this.makeQuery }
/>
<List>
<FlatList
data={this.state.artists}
//renderItem={({item}) => <Text>{item.name}</Text>}
/>
</List>
// {
// this.state.artists.map(artist => {
// return (
// <Text key={artist.id}>{artist.name}</Text>
// )
// })
// }
</View>
);
}
makeQuery = debounce(query => {
searchArtist(query)
.then((artists) => {
console.log(artists); // I have the list
this.setState({
artists: this.state.artists,
});
})
.catch((error) => {
throw error;
});
}, 400);
}
Thank you for your help.
UPDATE
I also tried using this without success :
<List>
<FlatList
data={this.state.artists}
renderItem={({ item }) => (
<ListItem
roundAvatar
title={item.name}
avatar={{ uri: item.images[0].url }}
/>
)}
/>
</List>
In the makeQuery function you need to set the response from the server like..
makeQuery = debounce(query => {
searchArtist(query)
.then((artists) => {
console.log(artists); // I have the list
this.setState({
artists: artists, //Here is the change
});
})
.catch((error) => {
throw error;
});
}, 400);
I'm trying to figure out why Match and History aren't showing up whenever I slide my <Drawer/>. I've looked around a lot throughout the web and SO but can't find anything pertaining to this.
Here's SideMenu.js file:
import React, {Component} from 'react';
import { Text, View} from 'react-native';
import {List, ListItem, Header} from 'react-native-elements';
import Container from "native-base/src/theme/components/Container";
export default class SideMenu extends Component {
constructor(props) {
super(props);
}
render() {
let list = [{
title: "Match",
onPress: () => {
this.props.navigator.replace("Match")
}
}, { // 2nd menu item below
title: "History",
onPress: () => {
this.props.navigator.replace("History")
}
}];
return(
<Container theme={this.props.theme}>
<Header/>
<View>
<List dataArray={list} renderRow={(item) =>
<ListItem button onPress={item.onPress.bind(this)}>
<Text>{item.title}</Text>
</ListItem>
}/>
</View>
</Container>
);
}
}
Here's AppContainer.js file:
import React, {Component} from 'react';
import {Navigator} from 'react-native-deprecated-custom-components';
import Drawer from "react-native-drawer-menu";
import SideMenu from './components/sideMenu';
export default class AppContainer extends Component {
constructor(props) {
super(props);
this.state = {
toggled: false,
store: {}, // holds data stores
theme: null
}
}
toggleDrawer() {
this.state.toggled ? this._drawer.close() : this._drawer.open();
}
openDrawer() {
this.setState({toggled: true});
}
closeDrawer() {
this.setState({toggled: false});
}
renderScene(route, navigator) { // current route you want to change to, instance of the navigator
switch(route) {
default: {
return null;
}
}
}
// handles how our scenes are brought into view
configureScene(route, routeStack) {
return Navigator.SceneConfigs.PushFromLeft; // pushes new scene from RHS
}
render() {
return(
<Drawer
ref = {(ref) => this._drawer = ref}
type = 'default' // controls how menu appears on screen, pushes content to the side
content = {<SideMenu navigator={this._navigator} theme={this.state.theme}
/>}
onClose={this.closeDrawer.bind(this)}
onOpen={this.openDrawer.bind(this)}
openDrawerOffset={0.9}
>
<Navigator
ref={(ref) => this._navigator = ref}
configureScene={this.configureScene.bind(this)}
renderScene={this.renderScene.bind(this)}
/>
</Drawer>
);
}
}
First of all there's no such property as dataArray in ListView component. You need to create data source first and pass it to dataSource property. Look at the example in the DOCUMENTATION
Looking at the Lists API for react-native-elements https://react-native-training.github.io/react-native-elements/API/lists/
Examples using ListItem are using title prop for setting the title. Maybe try returning this instead from SideMenu render
return(
<Container theme={this.props.theme}>
<Header/>
<View>
<List>
{
list.map((item, i) => (
<ListItem onPress={item.onPress} key={i} title={item.title}/>
))
}
</List>
</View>
</Container>
);
i am using Flatlist from react-native and ListItem from react-native-elements,
i want to initially limit the number of list-items that are loaded on the screen.Otherwise it loads all the items that i have initially .
Suppose i have 300 list items but initially i only want to load 10 items ,instead of 300.
MY CODE:
import React, { Component } from 'react'
import {
FlatList
} from 'react-native'
import {Avatar,Tile,ListItem} from 'react-native-elements'
export default class Login extends Component{
constructor(props) {
super(props);
this.state = {
data:[],
dataSource: []
};
}
renderList(item,i){
return(
<View>
<ListItem
subtitle={
<Avatar
small
rounded
source={{uri: "https://s3.amazonaws.com/uifaces/faces/twitter/ladylexy/128.jpg"}}
/>
{<Text>{item.body}</Text>}
}
/>
<View>
)
}
render(){
return(
<View>
<List>
<FlatList
data={this.state.data}
keyExtractor={item => item.id}
renderItem ={({item,index}) => this.renderList(item,index)}
/>
</List>
</View>
)
}
}
Basically, what you need is to implement sort of pagination. You can do it by using onEndReached and onEndReachedThreshold(for more details look here) of FlatList to load more data when user reaches the end of list.
You can change your code like so:
import React, { Component } from 'react';
import { FlatList } from 'react-native';
import { Avatar, Tile, ListItem } from 'react-native-elements';
const initialData = [0,...,299]; //all 300. Usually you receive this from server or is stored as one batch somewhere
const ITEMS_PER_PAGE = 10; // what is the batch size you want to load.
export default class Login extends Component {
constructor(props) {
super(props);
this.state = {
data: [0,..., 9], // you can do something like initialData.slice(0, 10) to populate from initialData.
dataSource: [],
page: 1,
};
}
renderList(item, i) {
return (
<View>
<ListItem />
</View>
);
}
loadMore() {
const { page, data } = this.state;
const start = page*ITEMS_PER_PAGE;
const end = (page+1)*ITEMS_PER_PAGE-1;
const newData = initialData.slice(start, end); // here, we will receive next batch of the items
this.setState({data: [...data, ...newData]}); // here we are appending new batch to existing batch
}
render() {
return (
<View>
<FlatList
data={this.state.data}
keyExtractor={item => item.id}
renderItem={({ item, index }) => this.renderList(item, index)}
onEndReached={this.loadMore}
/>
</View>
);
}
}