I like to know how i can use React Navigation on FlatList property, where the name of the Stack.Screen comes from a .json file.
And with that, when the user click on that Item, they goes to another page of the application.
Data
{
Data: [
{
"key": "0",
"label": "Test",
"goTo": "Test", <--- Here goes the name of Stack.Screen from routes.js
}
]
}
FlatList structure
function Item({ label, goTo }) {
return (
<Ripple rippleCentered onPressIn={goTo}> // (react-native-material-ripple)
<Option>
<Icon name={onIcon} size={28} color={onColor} /> // (react-native-vector-icons)
<OptionLabel color={onColor}>{label}</OptionLabel>
</Option>
</Ripple>
);
}
I've already tried to use navigation.navigate({goTo}) on onPressIn property from Ripple, but a ReferenceError appears: Can't find variable: navigation
Final exported component
export default class Menu extends Component {
render() {
return (
<Container color={this.props.color}>
<FlatList
data={Data}
showsVerticalScrollIndicator={false}
keyExtractor={item => item.key}
numColumns={5}
columnWrapperStyle={Styles.Row}
renderItem={({ item }) =>
<Item
goTo={item.goTo}
label={item.label}
/>
}
/>
</Container>
);
}
}
Read from json file
import json from './myfile.json'; // reading from json file
export default class Menu extends Component {
render() {
return (
<Container color={this.props.color}>
<FlatList
data={json.Data} // accessing Data from json
showsVerticalScrollIndicator={false}
keyExtractor={item => item.key}
numColumns={5}
columnWrapperStyle={Styles.Row}
renderItem={({ item }) =>
<Item
goTo={item.goTo}
label={item.label}
/>
}
/>
</Container>
);
}
}
Navigating
You could use useNavigation hook to call navigation.navigate(goTo)
e.g.
import { useNavigation } from '#react-navigation/native';
function Item({ label, goTo }) {
const navigation = useNavigation(); // navigation hook
return (
<Ripple rippleCentered onPressIn={() => navigation.navigate(goTo)}> // navigate to goTo screen
<Option>
<Icon name={onIcon} size={28} color={onColor} />
<OptionLabel color={onColor}>{label}</OptionLabel>
</Option>
</Ripple>
);
}
Please notice that Menu needs to be under NavigationContainer so useNavigation can work.
Related
I am facing an error that says "VirtualizedList: You have a large list that is slow to update - make sure your renderItem function renders components that follow React performance best practices like PureComponent, shouldComponentUpdate, etc." Can anyone help me to fix this problem in my code? Also, sometimes posts load after 2-3 secs of time how can I load intensely?
MainPage:
export default function Home({ navigation }) {
const [userdata, setUserdata] = useState(null);
return (
<View style={styles.container}>
<StatusBar />
<ButtomNavbar navigation={navigation} page={'home'} />
<TopNavbar navigation={navigation} page={'home'} />
<Posts />
</View>
);
}
Post Comp:
import { StyleSheet, FlatList } from 'react-native'
import React, { useState, useEffect, useCallback } from 'react';
import PostCard from '../Cards/PostCard';
const Posts = () => {
const [userData, setUserData] = useState([]);
const fetchUserData = useCallback(async () => {
try {
const response = await fetch('http://10.0.2.2:3000/postdata');
const data = await response.json();
setUserData(data);
} catch (err) {
console.error(err);
}
}, []);
useEffect(() => {
fetchUserData();
}, [fetchUserData]);
return (
<FlatList
style={styles.container}
data={userData}
renderItem={({ item, index }) => (
<PostCard
key={index}
username={item.username}
profile_image={item.profile_image}
postImage={item.postImage}
/>
)}
/>
);
}
export default Posts
Post Card:
import React from 'react';
import { StyleSheet, Text, View, Image } from 'react-native';
const PostCard = ({ username, profile_image, postImage }) => {
return (
<View style={styles.container}>
<View style={styles.c1}>
<Image source={{ uri: profile_image }} style={styles.profilepic} />
<Text style={styles.username}>{username}</Text>
</View>
<Image source={{ uri: postImage }} style={styles.image} />
</View>
);
};
Try adding the keyExtractor prop to your FlatList.
Here is an example:
return (
<FlatList
style={styles.container}
data={userData}
keyExtractor={(item) => `${item.id}`} <-----add this line
renderItem={({ item, index }) => (
<PostCard
key={index}
username={item.username}
profile_image={item.profile_image}
postImage={item.postImage}
/>
)}
/>
);
"VirtualizedList: You have a large list that is slow to update - make sure your renderItem function renders components that follow React performance best practices like PureComponent, shouldComponentUpdate, etc."
This warning basically appear when you try to render too much data or objects using FlatList because when you have good amount of data , you have to manage that it will not be render again and again as renderItem call again and again with data when user scroll which consume more memory
You can control this behaviour by creating renderItem class component and extend it with PureComponent instead of React.Component
You can also control this behaviour by shouldComponentUpdate method as shown in this example
https://www.geeksforgeeks.org/what-does-shouldcomponentupdate-do-and-why-is-it-important/
If you think it is going to take your time then as simplest solution you can use this lib
https://shopify.github.io/flash-list/
I have scroll view in my HomeScreen with my measures that is array of Measure component.
<ScrollView>{measures}</ScrollView>
My Measure component looks like this:
class Measure extends Component {
render() {
return (
<View key={this.props.keyval}>
<TouchableOpacity onPress={deleteItem(this.props.val.measure_id)}>
<Text style={styles.measure}>
ID: {this.props.val.measure_id}
</Text>
</TouchableOpacity>
</View>
);
}
deleteItem(id) {
// delete item
);
}
}
My question is, how to notify parent component HomeScreen that Measure was deleted to reload scroll view items? Or Maybe you have better idea how to:
display measures
delete one in onPress item
reload items in scroll view
Thans for any advices
In your case it should be something like this:
class HomeScreen extends Component {
state = {
measures: []
};
handleDelete = (id) => {
// item was deleted
}
render() {
const { measures } = this.state;
const measuresList = measures.map(measure => (
<Measure
key={measure.measure_id}
onDelete={this.handleDelete}
val={measure}
/>
));
return (
<ScrollView>{measuresList}</ScrollView>
);
}
}
class Measure extends Component {
render() {
return (
<View key={this.props.keyval}>
<TouchableOpacity onPress={deleteItem(this.props.val.measure_id)}>
<Text style={styles.measure}>
ID: {this.props.val.measure_id}
</Text>
</TouchableOpacity>
</View>
);
}
deleteItem(id) {
const { onDelete } = this.props;
// delete item
onDelete(id); //will call parent method.
}
}
I recommend using FlatList as it render only those items which are visible. Much better in terms of performance, especially in big lists taken from API.
Example:
<FlatList
data={measures}
keyExtractor={item => item.id}
renderItem={({ item }) => <Measure
id={item.id}
anyOtherNeededPropsKey={anyOtherNeededPropsValue}
/>}
/>
Pass an additional property onDelete to the Measure and call it in deleteItem method
I am trying to render out a list of object data using FlatList in my React Native component, however I am getting a blank screen without any errors on the console which is why it is rather difficult to get to the bottom of the issue here. The data is made available to the component using Redux-Saga approach and supplied to the FlatList which is showing up a blank screen without any errors. To double check if the FlatList is working fine I did a mockup array in component and passed to the FlatList which renders out the UI as expected. Following is the code I am using here;
=======================================================
class Mobile extends Component {
componentDidMount() {
let { readPostsAction } = this.props;
readPostsAction();
}
renderItem = ({ item }) => {
return (
<View>
<TouchableOpacity onPress={() => this.props.navigation.navigate('HomeDetails', { item })}>
<Card>
<CardItem header>
<Text style={styles.titleHeading}>{item.title}</Text>
</CardItem>
<CardItem cardBody>
<Content style={styles.cardContainer}>
<CustomCachedImage
component={FitImage}
source={{ uri: contentURL(item.attachments[0].url) }}
style={{ width: width, height: 200 }}
/>
<HTML tagsStyles={bodyText} html={reduceStringLength(contentText(item.excerpt))} />
</Content>
</CardItem>
</Card>
</TouchableOpacity>
</View>
)
}
keyExtractor = (item, index) => item.id;
render() {
const { dataSource } = this.props;
console.log('this.props', this.props);
return (
<View>
<FlatList
data={dataSource}
keyExtractor={this.keyExtractor}
renderItem={this.renderItem}
/>
</View>
);
}
}
function mapStateToProps({ launchAppReducer }) {
return {
isLoading: launchAppReducer.isLoading,
dataSource: launchAppReducer.data
}
}
export default connect(mapStateToProps, { readPostsAction: actions.readPostsAction })(Mobile);
=======================================================
Here is the screenshot of the console showing that the data is available in the component.
Modify your FlatList code and retry
<FlatList
data={dataSource}
extraData={this.props}
keyExtractor={this.keyExtractor}
/>
There was the problem at in the Actions, I was firing readPostsActions instead I should have fired readMobilePostsActions - it works fine now, thank you guys all the all input and help here.
Regards
You just need to add this style in your Flatlist:
<FlatList
style={{height:'100%'}}
data={dataSource}
keyExtractor={this.keyExtractor}
renderItem={this.renderItem}
/>
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>
);
}
}
I am trying to implement react-native-drawer from https://github.com/root-two/react-native-drawer and the variable I passed into NavigationBarRouteMapper logs openDrawer() function properly, yet when the left nav button, 'Open Menu', is clicked it does nothing:
class practice extends Component {
...
openDrawer(){
this._drawer.open()
}
render() {
return (
<Drawer
content={<DrawerPanel/>}
openDrawerOffset={100}
ref={(ref) => this._drawer = ref}
type='static'
tweenHandler={Drawer.tweenPresets.parallax}
>
<Navigator
configureScene={this.configureScene}
initialRoute={{name: 'Start', component: Start}}
renderScene={this.renderScene}
style={styles.container}
navigationBar={
<Navigator.NavigationBar
style={styles.navBar}
routeMapper={NavigationBarRouteMapper(this.openDrawer)}
/>
}
/>
</Drawer>
);
}
}
var NavigationBarRouteMapper = openDrawer => ({
LeftButton(route, navigator, index, navState){
return(
<TouchableHighlight onPress={()=>{openDrawer}}>
<Text>Open Menu</Text>
</TouchableHighlight>
)
}
},...
What may be stopping the drawer from opening? Seems like everything has been implemented properly.