Currently working on an app this gives users lists of news on a page and on each news has a textbox where you can input your comment.
So for example, 10 news items will have 10 textboxes.
When a user comment after and hit the submit button, the activity indicator appears for all 10 news items, but I want it to only display on where the comment has been made and also after posting the comment, the comment box should be empty
Function
state = {
posts: [],
comment: ""
};
commentPost = item => {
const api = create({
baseURL: "patch-to-api-url",
headers: { Accept: "application/json" }
});
const self = this;
self.setState({ modalLoader: true });
api
.post("news/posts/" + `${item.id}` + "/comments", {
media: "",
text: this.state.comment
})
.then(response => {
console.log(response);
self.setState({ modalLoader: false });
//updating the state
this.setState(prevState => ({
posts: prevState.posts.map(el => {
if (el.id === item.id) {
return {
...el,
commentsCount: el.commentsCount + 1
};
}
return el;
})
}));
});
};
View
<ScrollView>
{posts.map((item, i) => {
return (
<View key={i} style={styles.user}>
<Card>
<ListItem
titleStyle={{ color: "#36c", fontWeight: "500" }}
onPress={() =>
navigation.navigate("PostComments", {
postID: item.id,
groupID: item.writer.group.id,
communityID: item.group.community.id
})
}
titleNumberOfLines={2}
hideChevron={false}
chevronColor="#36c"
roundAvatar
title={item.headline}
avatar={{
uri:
"https://s3.amazonaws.com/uifaces/faces/twitter/brynn/128.jpg"
}}
/>
<Text
style={{
marginBottom: 10,
fontSize: 16,
color: "#000",
fontFamily: "HelveticaNeue-Light"
}}
>
{item.text}
</Text>
<TextInput
onChangeText={onSetComment}
label="Write Comment"
underlineColor="#36a"
style={{ backgroundColor: "#fff", width: "90%" }}
/>
<View>
<Icon
name="md-send"
type="ionicon"
color="#999"
onPress={() => {
onCommentPost(item);
}}
/>
<View style={styles.loading}>
<ActivityIndicator animating={modalLoader} size="small" />
</View>
</View>
</Card>
</View>
);
})}
</ScrollView>
You don't have enough state to accomplish what you want. Wanting an independent spinner in each post implies that you have to store it's state somewhere.
You should add the modalLoader attribute to each post and not globally. Change your function to look like this:
commentPost = item => {
const api = create({
baseURL: "patch-to-api-url",
headers: { Accept: "application/json" }
});
const self = this;
self.setState({ posts: this.state.posts.map(post => post.id === item.id ? {...post, modalLoader: true } : post));
api
.post("news/posts/" + `${item.id}` + "/comments", {
media: "",
text: this.state.comment
})
.then(response => {
console.log(response);
self.setState({ posts: this.state.posts.map(post => post.id === item.id ? {...post, modalLoader: false } : post));
//updating the state
this.setState(prevState => ({
posts: prevState.posts.map(el => {
if (el.id === item.id) {
return {
...el,
commentsCount: el.commentsCount + 1
};
}
return el;
})
}));
});
};
And your component to look like this:
<ScrollView>
{posts.map((item, i) => {
return (
<View key={i} style={styles.user}>
<Card>
<ListItem
titleStyle={{ color: "#36c", fontWeight: "500" }}
onPress={() =>
navigation.navigate("PostComments", {
postID: item.id,
groupID: item.writer.group.id,
communityID: item.group.community.id
})
}
titleNumberOfLines={2}
hideChevron={false}
chevronColor="#36c"
roundAvatar
title={item.headline}
avatar={{
uri:
"https://s3.amazonaws.com/uifaces/faces/twitter/brynn/128.jpg"
}}
/>
<Text
style={{
marginBottom: 10,
fontSize: 16,
color: "#000",
fontFamily: "HelveticaNeue-Light"
}}
>
{item.text}
</Text>
<TextInput
onChangeText={onSetComment}
label="Write Comment"
underlineColor="#36a"
style={{ backgroundColor: "#fff", width: "90%" }}
/>
<View>
<Icon
name="md-send"
type="ionicon"
color="#999"
onPress={() => {
onCommentPost(item);
}}
/>
<View style={styles.loading}>
<ActivityIndicator animating={item.modalLoader} size="small" />
</View>
</View>
</Card>
</View>
);
})}
</ScrollView>
You are sharing the modalLoader state among all iterated posts. A Post should be a single stateful component. Then for the specific component that was updated you need to update only that state.
Related
I am having trouble updating an array that is passed as a prop into my child component. I have searched around but haven't found an answer that can directly solve my problem. My code is as follows:
App.js
import React, { Component } from 'react';
import { StyleSheet, Text, View, SafeAreaView } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import AddMedication from "./src/AddMedication";
import MedicationList from './src/MedicationList';
const Stack = createNativeStackNavigator();
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
medications: [],
}
this.addMedication = this.addMedication.bind(this);
}
addMedication = (name, dosage, measurement, timesDaily) => {
console.log("Medication added.")
var newItem = {name: name, dosage: dosage, measurement: measurement, timesDaily: timesDaily}
this.setState({
medications: [...this.state.medications, newItem]
})
}
render() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Medication List">
{(props) => <MedicationList {...props} medications={this.state.medications} />}
</Stack.Screen>
<Stack.Screen name="Add New Medication">
{(props) => <AddMedication {...props} addMedication={this.addMedication} />}
</Stack.Screen>
</Stack.Navigator>
</NavigationContainer>
);
}
}
This is the home screen where I am trying to display the array but nothing shows up
MedicationList.js
class MedicationList extends Component {
constructor(props) {
super(props);
this.state = {
tableHead: ['Name', 'Dosage', 'Times Daily', 'Prescriber', 'For Diagnosis'],
}
}
medication = ({ item }) => {
<View style={{ flexDirection: 'row' }}>
<View style={{ width: 50, backgroundColor: 'lightyellow'}}>
<Text style={{ fontSize: 16, fontWeight: 'bold', textAlign: 'center'}}>{item.name}</Text>
</View>
<View style={{ width: 400, backgroundColor: 'lightpink'}}>
<Text style={{ fontSize: 16, fontWeight: 'bold' , textAlign: 'center'}}>{item.dosage}{item.selectedMeasurement}</Text>
</View>
<View style={{ width: 400, backgroundColor: 'lavender'}}>
<Text style={{ fontSize: 16, fontWeight: 'bold' , textAlign: 'center'}}>{item.timesDaiy}</Text>
</View>
</View>
}
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', marginTop: '10%'}}>
<Button
title="+ Add New Medication"
onPress={() => {
/* 1. Navigate to the Details route with params */
this.props.navigation.navigate('Add New Medication', {
medications: this.props.medications,
});
}}
/>
<FlatList
data={this.props.medications}
renderItem={this.medication}
/>
</View>
);
}
}
This is where I click the add button to update the medications array
AddMedication.js
class AddMedication extends Component {
constructor(props) {
super(props);
this.state = {
name: '',
dosage: 0,
selectedMeasurement: "mg",
timesDaily: '',
prescriber: '',
forDiagnoses: '',
instructions: '',
validity: false,
};
}
setName = (name) => {
let isValid = this.isFormValid();
this.setState({ name: name, validity: isValid });
}
setDosage = (dosage) => {
let isValid = this.isFormValid();
this.setState({ dosage: dosage, validity: isValid });
}
setMeasurement = (measurement) => {
this.setState({ selectedMeasurement: measurement });
}
setTimesDaily = (timesDaily) => {
let isValid = this.isFormValid();
this.setState({ timesDaily: timesDaily, validity: isValid });
}
setPrescriber = (prescriber) => {
this.setState({ prescriber: prescriber });
}
setDiagnoses = (diagnoses) => {
this.setState({ forDiagnoses: diagnoses });
}
setInstructions = (instructions) => {
this.setState({ instructions: instructions });
}
isFormValid = () => {
return (this.state.name !== '' && (this.state.dosage !== '' && this.state.dosage > 0)
&& (this.state.timesDaily !== '' && this.state.timesDaily > 0));
}
render() {
return (
<View style={styles.container}>
<Text style={{color: 'red', marginBottom: 5, marginLeft: -125}}>* denotes required field</Text>
<View style={{flexDirection: 'row'}}>
<Text style={styles.required}>*</Text>
<TextInput
style={styles.inputText}
onChangeText={(name) => this.setName(name)}
placeholder="Medication Name"
value={this.state.name}
/>
</View>
<View style={{flexDirection: 'row'}}>
<Text style={styles.required}>*</Text>
<TextInput
style={styles.inputText}
onChangeText={(dosage) => this.setDosage(dosage)}
placeholder="Dosage"
value={this.state.dosage}
/>
</View>
<View style={styles.dosageContainer}>
<Text style={{flex: 1, marginTop: 100, marginLeft: 30}}>
Select Measurement:
</Text>
<Picker
style={styles.picker}
selectedValue={this.state.selectedMeasurement}
onValueChange={(itemValue, itemIndex) =>
this.setMeasurement(itemValue)
}>
<Picker.Item label="mg" value="mg" />
<Picker.Item label="g" value="g" />
<Picker.Item label="ml" value="ml" />
</Picker>
</View>
<View style={{flexDirection: 'row'}}>
<Text style={styles.required}>*</Text>
<TextInput
style={styles.inputText}
onChangeText={(timesDaily) => this.setTimesDaily(timesDaily)}
placeholder="Times daily"
value={this.state.timesDaily}
/>
</View>
<TextInput
style={styles.inputText}
onChangeText={(prescriber) => this.setPrescriber(prescriber)}
placeholder="Prescriber"
value={this.state.prescriber}
/>
<TextInput
style={styles.inputText}
onChangeText={(diagnoses) => this.setDiagnoses(diagnoses)}
placeholder="For diagnoses"
value={this.state.forDiagnoses}
/>
<TextInput
style={styles.inputText}
onChangeText={(instructions) => this.setInstructions(instructions)}
placeholder="Instructions"
value={this.state.instructions}
/>
<TouchableOpacity
style={this.isFormValid() ? styles.validButton : styles.invalidButton}
disabled={!Boolean(this.state.name && this.state.dosage && this.state.timesDaily)}
onPress={() => {
this.props.navigation.goBack()
this.props.addMedication(this.state.name, this.state.dosage,
this.state.selectedMeasurement, this.state.timesDaily)
}}
>
<Text style={{color: 'white'}}>Add Medication</Text>
</TouchableOpacity>
</View>
)
}
}
You can pass the state value but I think you cannot pass the addMedication method just like this.
Could you please try passing an arrow function that uses the setState method?
For example:
<Stack.Screen name="Add New Medication">
{(props) => <AddMedication {...props} addMedication={(name, dosage, measurement, timesDaily)=> {this.addMedication(name, dosage, measurement, timesDaily)}} />}
</Stack.Screen>
In React Native, React Native: How to show the result of the newly added data immediately without reloading the page? in my current status, I can add data successfully but I have to refresh the page to reflect in my browser the newest or updated data in my database. this is my code
import Profile from "../../components/profile";
import AddProfile from "../../components/AddProfile";
const [profilemanager, setProfileManager] = useState([]);
const { data, refetch, isLoading, isError, error } = useQuery('userData', async()=> {
const _token = await AsyncStorage.getItem('#token');
setToken(_token);
await getProfileList(_token);
});
const getProfileList= (_token) =>{
fetch(config.API_URL + '/profileManager/500/1', {
method: 'GET',
//body: JSON.stringify(dataToSend),
headers: {
//Header Defination
'Authorization': 'Bearer ' + _token,
'Content-Type': 'application/json',
},
})
.then((response) => response.json())
.then((responseJson) => {
setProfileManager(responseJson);
});
}
const addUserdataHandler = (selectedUserId) => {
fetch(config.API_URL + '/profileManager/add', {
method: 'POST',
body: JSON.stringify({intuserid: selectedUserId}),
headers: {
//Header Defination
'Authorization': 'Bearer '+ token,
'Content-Type': 'application/json',
},
})
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
if(responseJson.hasOwnProperty("errors")){
console.log("errors",responseJson);
setSavingError(JSON.stringify(responseJson.errors));
}else{
setProfileManager(prevDMSMANAGER => [
...prevPROFILEMANAGER,
{ usersid:selectedUserId}
]);
}
}).catch(function(error) {
console.log(error);
});
};
<AddProfile
onAddProfile={addUserdataHandler}
/>
<Text>{savingError}</Text>
<View style={{zIndex:-1}}>
<FlatList
style={{minHeight:200,maxHeight:300, width:550}}
keyExtractor={(item, index) =>index.toString()}
data={profilemanager}
renderItem={({ item }) => (
<Profile
key={item.usersid}
id={item.usersid}
Lastname={item.lastname}
Firstname={item.firstname}
Middlename={item.middlename}
pressHandler={confirmDelete}
/>
)}
/>
</View>
this is from my Profile.js
const Profile = props => {
const [text, setText] = useState(props.title);
const [isEditing, setEdit] = useState(false);
const handleEdit = (dataToSend) => {
props.pressHandler(dataToSend, props.id);
console.log("props.id: ", props.id)
setEdit(false);
};
return (
<View style={styles.items}>
<View style={styles.itemContainer}>
<Formik
initialValues={{
id:props.id,
Firstname:props.Firstname,
Lastname:props.Lastname,
Middlename:props.Middlename
}}
onSubmit={handleEdit}
style={{flexWrap:'wrap'}}
>
{({ handleChange, handleBlur, handleSubmit,isSubmitting, values, errors, isValid,touched,setFieldValue }) => (
<>
<Text style={styles.itemText}>{props.Lastname} {props.Firstname}, {props.Middlename}</Text>
<View style={styles.btnContainer}>
<Button title="Remove" onPress={handleSubmit} style={styles.editBtn} />
</View>
{(errors.UsersId && touched.UsersId) && <View style={{flex:1,flexDirection:'column', flexBasis:'100%'}}></View>}
</>
)}
</Formik>
</View>
</View>
);
};
This is how I handled the add profile
const addTodoHandler =async (dataToSend, { resetForm}) => {
console.log("dataToSend: ", dataToSend.selectedUserId)
await props.onAddProfile(dataToSend.selectedUserId);
resetForm({})
};
return (
<View >
<Formik
enableReinitialize
validationSchema={ValidationSchema}
initialValues={{
selectedUserId:0
}}
onSubmit={addTodoHandler}
>
{({ handleChange, handleBlur,handleReset,resetForm, handleSubmit,isSubmitting, values, errors, isValid,touched,setFieldValue }) => (
<>
<View style={forms.fieldRow}>
{/* <TouchableWithoutFeedback onPress={() => dropDownRef.current.close()}> */}
<View style={{padding:0, width:250,flexDirection:'row',flexGrow:0}}>
<DropDownPicker
name="selectedUserId"
value={selectedUserId}
defaultValue={selectedUserId}
zIndex={9000}
controller={instance => dropDownRef.current = instance}
searchable={true}
placeholder="Select a user"
searchablePlaceholder="Search a user.."
searchablePlaceholderTextColor="gray"
seachableStyle={{maxHeight:500, height:500}}
searchableError={() => <Text>Not Found</Text>}
onSearch={text => { searchUser(text) }}
items={users}
//defaultValue={country}
containerStyle={forms.dropdownStyle}
style={{
backgroundColor: '#fff',
width:230,
paddingTop:10,
borderColor:'#dadae8',
overflow:'hidden'}}
itemStyle={{
justifyContent: 'flex-start',
}}
dropDownStyle={{backgroundColor: '#fff', padding:10, width:200, height:400, maxHeight:500,borderStyle:'solid', zIndex:999, borderColor:'#444', shadowColor: '#000',
shadowOffset: {
width: 0,
height: 1,
},
shadowOpacity: 0.22,
shadowRadius: 2.22,
elevation: 3,}}
onChangeItem={item => {
setFieldValue("selectedUserId",item.value)
setFieldValue("selectedUserName",item.label)
setSelectedUserId(item.value);
}}
/>
</View>
<View style={{padding:0,zIndex:-1,flexDirection:'row',flexGrow: 1}}>
<Button title="Add" onPress={handleSubmit} disabled={!isValid}/>
</View>
</View>
<View style={{zIndex:-1}}>
{(errors.selectedUserId && touched.selectedUserId) && <Text style={formStyles.errorText}>{errors.selectedUserId}
</Text> }
</View>
</>
)}
</Formik>
</View>
);
};
I am using the infinite scrolling Flatlist pattern of react-native mentioned on the internet. So far it's good for having long data. But when the rest API pagination completed, it returns the empty JSON array. Now with infinite scrolling pattern, onEndReached function is infinitely thrown leading to unnecessary rest call. So what's the best way to deal with this situation.
The data from API could be a single row or 500 rows of JSON object depending upon the conditional URL mentioned in code. In the case of 500 rows, it's fine till there's more data to fetch but after the last 480-500 batch, it becomes an infinite loop, which is a problem. In the case of 1 row, it immediately becomes an endless loop. How could I break the onEndReached event conditionally so that it never trigger when I detect empty array from rest api.
Below is my Flatlist implementation:
import React, { Component } from 'react';
import { StyleSheet, Text, View, FlatList, Image, ActivityIndicator, TouchableOpacity, ToastAndroid } from 'react-native';
import * as SecureStore from 'expo-secure-store';
import GLOBAL from './productglobal'
import { Ionicons } from '#expo/vector-icons';
import SearchBar from '../commoncomponents/searchbar'
export default class ProductsList extends Component {
static navigationOptions = ({ navigation }) => {
return {
headerTitle: 'Products',
headerRight: () => (
<TouchableOpacity
style={{ paddingRight: 20 }}
onPress={() => { navigation.navigate("Settings") }}
>
<Ionicons name='md-more' size={25} color='white' />
</TouchableOpacity>
),
}
};
constructor(props) {
super(props);
this.state = {
loading: false,
searchValue: '',
data: [],
page: 1,
error: null,
refreshing: false,
base_url: null,
c_key: null,
c_secret: null,
};
GLOBAL.productlistScreen = this;
}
async componentDidMount() {
await this.getCredentials();
this.fetchProductList();
}
getCredentials = async () => {
const credentials = await SecureStore.getItemAsync('credentials');
const credentialsJson = JSON.parse(credentials)
this.setState({
base_url: credentialsJson.base_url,
c_key: credentialsJson.c_key,
c_secret: credentialsJson.c_secret,
})
}
fetchProductList = () => {
const { base_url, c_key, c_secret, page, searchValue } = this.state;
let url = null
if (searchValue) {
url = `${base_url}/wp-json/wc/v3/products?per_page=20&search=${searchValue}&page=${page}&consumer_key=${c_key}&consumer_secret=${c_secret}`;
} else {
url = `${base_url}/wp-json/wc/v3/products?per_page=20&page=${page}&consumer_key=${c_key}&consumer_secret=${c_secret}`;
}
console.log(url)
this.setState({ loading: true });
setTimeout(() => {
fetch(url).then((response) => response.json())
.then((responseJson) => {
this.setState({
data: this.state.data.concat(responseJson),
error: responseJson.code || null,
loading: false,
refreshing: false
});
}).catch((error) => {
this.setState({
error,
loading: false,
refreshing: false
})
});
}, 1500);
};
renderListSeparator = () => {
return (
<View style={{
height: 1,
width: '100%',
backgroundColor: '#999999'
}} />
)
}
renderListFooter = () => {
if (!this.state.loading) return null;
return (
<View style={{
paddingVertical: 20,
}}>
<ActivityIndicator color='#96588a' size='large' />
</View>
)
}
handleRefresh = () => {
this.setState({
page: 1,
refreshing: true,
data: []
}, () => {
this.fetchProductList();
}
)
}
handleLoadMore = () => {
console.log('loading triggered')
this.setState({
page: this.state.page + 1,
}, () => {
this.fetchProductList();
})
}
handleSearch = (value) => {
// console.log(value)
this.setState({
searchValue: value,
page: 1,
refreshing: true,
data: []
}, () => {
this.fetchProductList()
})
}
render() {
return (
<View style={{flex:1}}>
<SearchBar onSearchPress={this.handleSearch}></SearchBar>
<FlatList
data={this.state.data}
keyExtractor={item => item.id.toString()}
refreshing={this.state.refreshing}
extraData={this.state.data}
onRefresh={this.handleRefresh}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={0.5}
ItemSeparatorComponent={this.renderListSeparator}
ListFooterComponent={this.renderListFooter}
renderItem={({ item }) =>
<TouchableOpacity onPress={() => {
this.props.navigation.navigate('ProductDetails', {
productId: item.id,
productName: item.name,
base_url: this.state.base_url,
c_key: this.state.c_key,
c_secret: this.state.c_secret
});
}}>
<View style={{ flex: 1, flexDirection: 'row', backgroundColor: 'white' }}>
<View style={{ flex: 1, justifyContent: "center", alignContent: "center" }}>
<Image source={(Array.isArray(item.images) && item.images.length) ?
{ uri: item.images[0].src } :
require('../../../assets/images/blank_product.png')}
onError={(e) => { this.props.source = require('../../../assets/images/blank_product.png') }}
style={{ height: 115, width: 115 }} resizeMode='contain' />
</View>
<View style={{ flex: 2, marginTop: 10, marginBottom: 10, justifyContent: "center" }}>
<View style={{ marginLeft: 10 }}>
<Text style={styles.titleText}>{item.name}</Text>
<Text>SKU: {item.sku}</Text>
<Text>Price: {item.price}</Text>
<Text>Stock Status: {item.stock_status}</Text>
<Text>Stock: {item.stock_quantity}</Text>
<Text>Status: {item.status}</Text>
</View>
</View>
</View>
</TouchableOpacity>
}
/>
</View>
);
}
}
const styles = StyleSheet.create({
titleText: {
fontSize: 20,
fontWeight: 'bold',
}
});
You can add a state property hasMoreToLoad which defaults to true.
Then you can check in fetchProductList if the result is less than per_page (data < 20) IF the result is less than per_page you know you've reached the end and you can set hasMoreToLoad to false.
onEndReached={this.state.hasMoreToLoad ? this.handleLoadMore : null}
Just simply put condition on onEndReached() :
<FlatList
data={this.state.data}
keyExtractor={item => item.id.toString()}
refreshing={this.state.refreshing}
extraData={this.state.data}
onRefresh={this.handleRefresh}
onEndReached={this.status.data.length > 0 ? this.handleLoadMore : null} // change here
...
...
/>
How to limit the quantity of View inside of a scrollview.
My component take too much time to render, because the map function renders too many views. I need to show only 10 views, and when scroll up, renders more 10.
I'm using react native, hooks and typescript.
First of all, if you have a large number of list data don't use scrollview. Because initially, it loads all the data to scrollview component & it costs performance as well.
Use flatlist in react-native instead of scrollview & you can limit the number of items to render in the initially using initialNumToRender. When you reach the end of the scroll position you can call onEndReached method to load more data.
A sample will like this
import React, { Component } from "react";
import { View, Text, FlatList, ActivityIndicator } from "react-native";
import { List, ListItem, SearchBar } from "react-native-elements";
class FlatListDemo extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
page: 1,
seed: 1,
error: null,
refreshing: false
};
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const { page, seed } = this.state;
const url = `https://randomuser.me/api/?seed=${seed}&page=${page}&results=20`;
this.setState({ loading: true });
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: page === 1 ? res.results : [...this.state.data, ...res.results],
error: res.error || null,
loading: false,
refreshing: false
});
})
.catch(error => {
this.setState({ error, loading: false });
});
};
handleRefresh = () => {
this.setState(
{
page: 1,
seed: this.state.seed + 1,
refreshing: true
},
() => {
this.makeRemoteRequest();
}
);
};
handleLoadMore = () => {
this.setState(
{
page: this.state.page + 1
},
() => {
this.makeRemoteRequest();
}
);
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "86%",
backgroundColor: "#CED0CE",
marginLeft: "14%"
}}
/>
);
};
renderHeader = () => {
return <SearchBar placeholder="Type Here..." lightTheme round />;
};
renderFooter = () => {
if (!this.state.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: "#CED0CE"
}}
>
<ActivityIndicator animating size="large" />
</View>
);
};
render() {
return (
<List containerStyle={{ borderTopWidth: 0, borderBottomWidth: 0 }}>
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<ListItem
roundAvatar
title={`${item.name.first} ${item.name.last}`}
subtitle={item.email}
avatar={{ uri: item.picture.thumbnail }}
containerStyle={{ borderBottomWidth: 0 }}
/>
)}
keyExtractor={item => item.email}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
onRefresh={this.handleRefresh}
refreshing={this.state.refreshing}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={50}
/>
</List>
);
}
}
export default FlatListDemo;
Check this for more informations.
I changed to Flatlist! But initialNumToRender is not working as expected.
The flatlist is rendering all 150 transactions, not only 15, and i have no idea what to do.
I'm running .map() from another array with all others transactions to create newMonths with only those transactions that i want to use data={newMonths}.
let newMonths = [];
const createArrayMonth = histInfos.map(function (info) {
if (info.created_at.slice(5, 7) === month) {
newMonths = [info].concat(newMonths);
}
});
them, i created my component:
function Item({ value }: { value: any }) {
let createdDay = value.item.created_at.slice(8, 10);
let createdMonth = value.item.created_at.slice(5, 7);
let createdYear = value.item.created_at.slice(2, 4);
function dateSelected() {
if (
month === createdMonth &&
year === createdYear &&
(day === '00' || day == createdDay)
) {
console.log('foi dateSelected');
const [on, setOn] = useState(false);
const Details = (on: any) => {
if (on === true) {
return (
<View style={styles.infos}>
<Text style={styles.TextInfos}>
{' '}
CPF/CNPJ: {value.item.cpf_cnpj}{' '}
</Text>
<Text style={styles.TextInfos}>
{' '}
Criado em: {value.item.created_at}{' '}
</Text>
<Text style={styles.TextInfos}>
{' '}
Endereço da carteira: {value.item.address}{' '}
</Text>
<Text style={styles.TextInfos}> Valor: {value.item.amount}BTC </Text>
</View>
);
} else {
return <View />;
}
};
return (
<View>
<TouchableOpacity
style={styles.card}
onPress={() => setOn(oldState => !oldState)}>
<View style={styles.dateStyleView}>
<Text style={styles.dateStyle}>{createdDay}</Text>
</View>
<View style={styles.left}>
<Text style={styles.title}>Venda rápida</Text>
<Text style={styles.semiTitle}>
{
{
0: 'Pendente',
1: 'Aguardando conclusão',
2: 'Cancelado',
100: 'Completo',
}[value.item.status]
}
</Text>
</View>
<View style={styles.right2}>
<Text style={styles.price}>R$ {value.item.requested_value}</Text>
</View>
</TouchableOpacity>
<View>{Details(on)}</View>
</View>
);
}
}
return dateSelected();}
and i call it here
return (
<ScrollView>
...
<View style={styles.center}>
...
<View style={styles.middle2}>
...
<FlatList
extraData={[refresh, newMonths]}
data={newMonths}
renderItem={(item: any) => <Item value={item} />}
keyExtractor={(item, index) => index.toString()}
initialNumToRender={15}
/>
</View>
</View>
</ScrollView>);}
The scroll bar in the right, start to increase until renders all transactions from the data:
App scroll bar
I'm quite new to react native. I have a user with a profile picture that I'm trying to display on screen, when printing to my console snapshot.val().imageURL the correct url is displayed using this:
var profImage = 'https://www.placeholdit.com';
var user = authFB.currentUser;
if (user) {
database.ref('users').child(user.uid).once('value')
.then((snapshot) => profImage = snapshot.val().imageURL)
.catch(error => console.log(error))
} else {
console.log("No user")
}
I assign that url to profImage and try to set my image uri to that variable:
<Avatar
large
rounded
source={{ uri: profImage }}
onPress={() => alert(profImage)}
activeOpacity={0.7}
containerStyle={{ marginBottom: 12 }}
/>
However, my image container remains blank. Does this have to do that when the render function is run, the url hasn't been retrieved thus the component display is blank? Do I use this.setState to update the component? If so, what is the proper way to do so?
Here is the relevant part of my component:
class Profile extends React.Component {
static navigationOptions = {
title: "Profile",
headerStyle: {
backgroundColor: '#fff',
borderBottomWidth: 0,
},
headerTitleStyle: {
color: 'black'
},
}
constructor() {
super();
this.state = {
image: null,
uploading: null,
}
}
render() {
const { navigate } = this.props.navigation;
let { image } = this.state;
var profImage = 'https://www.placeholdit.com';
var user = authFB.currentUser;
if (user) {
database.ref('users').child(user.uid).once('value')
.then((snapshot) => profImage = snapshot.val().imageURL)
.catch(error => console.log(error))
} else {
console.log("No user")
}
return (
<ScrollView>
<View style={styles.profileTopContainer}>
<Avatar
large
rounded
source={{ uri: img }}
onPress={() => alert(profImage)}
activeOpacity={0.7}
containerStyle={{ marginBottom: 12 }}
/>
<Text
style={styles.usernameText}
>
Jordan Lewallen
</Text>
<TouchableHighlight
onPress={this._pickImage}>
<Text
style={styles.userMinutes}
>
Choose a profile picture
</Text>
</TouchableHighlight>
{this._maybeRenderImage()}
{this._maybeRenderUploadingOverlay()}
{
(image)
? <Image source={{ uri: image }} style={{ width: 250, height: 250 }} />
: null
}
</View>
}
please try this
<Avatar
large
rounded
source={{ uri: img }}
onPress={() => alert(profImage)}
activeOpacity={0.7}
containerStyle={{ marginBottom: 12 }}
/>
replace with
<Avatar
large
rounded
source={{ uri: profImage }}
onPress={() => alert(profImage)}
activeOpacity={0.7}
containerStyle={{ marginBottom: 12 }}
/>
This is because you call the async request inside your render method itself.
When your render method execute you request for imageUrl which is a async call and takes time to resolve and update the value of profImage variable. At that time your render method is finished its execution and set the placeholder.
If you need this to be done you should keep this profImage in your component state. So once the state is updated your render method will be called again and update the UI with new image.
Try this!
class Profile extends React.Component {
static navigationOptions = {
title: "Profile",
headerStyle: {
backgroundColor: '#fff',
borderBottomWidth: 0,
},
headerTitleStyle: {
color: 'black'
},
}
constructor() {
super();
this.state = {
image: null,
uploading: null,
}
}
componentWillRecieveProps(nextProps){
var profImage = 'https://www.placeholdit.com';
var user = authFB.currentUser;
if (user) {
database.ref('users').child(user.uid).once('value')
.then((snapshot) => this.setState({ image: snapshot.val().imageURL }))
.catch(error => console.log(error))
} else {
this.setState({ image: 'https://www.placeholdit.com' });
console.log("No user")
}
}
render() {
const { navigate } = this.props.navigation;
let { image } = this.state;
return (
<ScrollView>
<View style={styles.profileTopContainer}>
<Avatar
large
rounded
source={{ uri: image }}
onPress={() => alert(profImage)}
activeOpacity={0.7}
containerStyle={{ marginBottom: 12 }}
/>
<Text
style={styles.usernameText}
>
Jordan Lewallen
</Text>
<TouchableHighlight
onPress={this._pickImage}>
<Text
style={styles.userMinutes}
>
Choose a profile picture
</Text>
</TouchableHighlight>
{this._maybeRenderImage()}
{this._maybeRenderUploadingOverlay()}
{
(image)
? <Image source={{ uri: image }} style={{ width: 250, height: 250 }} />
: null
}
</View>
}