I'm having difficulty scrolling the flatlist in react native - react-native

export default class Chat extends Component {
constructor(props) {
super(props);
this.state = {
chatMessage: "",
chatMessages: [],
roomId: props.route.params.roomId,
keyboardHeight: 0
};
}
componentDidMount() {
if (Platform.OS === "ios") {
Keyboard.addListener("keyboardWillShow", (e) => {
this.setState({ keyboardHeight: e.endCoordinates.height + 5 });
});
Keyboard.addListener("keyboardWillHide", () => {
this.setState({ keyboardHeight: 0 });
});
}
// Socket.io code ...
}
componentWillUnmount() {
if (Platform.OS === "ios") {
Keyboard.removeListener("keyboardWillShow", (e) => {
this.setState({ keyboardHeight: e.endCoordinates.height + 5 });
});
Keyboard.removeListener("keyboardWillHide", () => {
this.setState({ keyboardHeight: 0 });
});
}
this.socket.disconnect();
}
submitChatMessage() {
if (this.state.chatMessage.trim()) {
this.socket.emit("chat message", this.state.chatMessage);
this.setState({ chatMessage: "" });
}
}
render() {
return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<SafeAreaView style={{flex:1}}>
<View
style={{
flex:1,
width: "100%",
height:"90%"
}}
>
<FlatList
style={{ padding: 10, height:"98%", maxHeight: "98%" }}
data={this.state.chatMessages}
renderItem={(itemData) => (
<View>
<Text>{itemData.item.value}</Text>
</View>
)}
/>
</View>
<View
style={{
height:
Platform.OS === "ios"
? this.state.keyboardHeight + 20
: 20,
position: "absolute",
bottom: 0,
width: "100%",
backgroundColor: "grey"
}}
>
<TextInput
style={{
borderColor: "grey",
borderWidth: 0.75,
borderRadius: 30
}}
autoCorrect={false}
value={this.state.chatMessage}
onSubmitEditing={() => this.submitChatMessage()}
onChangeText={(chatMessage) => {
this.setState({ chatMessage });
}}
/>
</View>
</SafeAreaView>
</TouchableWithoutFeedback>
);
}
}
I was able to scroll through the FlatList easily but when I added event listeners to the keyboard so I can use it instead of a KeyboardAvoidingView component it stopped letting me scroll easily (it still scrolls but you have to try a lot).
I switched to changing the padding on the text input manually because the KeyboardAvoidingView component wasn't working properly.

Replace this :
<View
style={{
flex:1,
width: "100%",
height:"90%"
}}
>
<FlatList
style={{ padding: 10, height:"98%", maxHeight: "98%" }}
data={this.state.chatMessages}
renderItem={(itemData) => (
<View>
<Text>{itemData.item.value}</Text>
</View>
)}
/>
</View>
with that
<FlatList
contentContainerStyle={{padding: 10}}
style={{ height:"90%", alignSelf : "stretch" }}
data={this.state.chatMessages}
renderItem={(itemData) => (
<View>
<Text>{itemData.item.value}</Text>
</View>
)}
/>

Related

React navigation: navigate from screen to another screen showing previous data

I'm developing a food ordering app using react native and ASP .NET MVC Entity Framework.
I've Search screen with date(field) and vendor list(dropdown) and when I click on search button it will navigate to Orders Screen with date and vendor Id as parameters and with these 2 parameter I fetch data from API and show data with in Orders screen. But problem is first time it's showing correct list and 2nd time with different date and vendor id not updating list in Orders screen while API fetching correct data and showing previous data.
Search Screen Code
<View style={[styles.container, { backgroundColor: colors.background }]}>
<StatusBar style={theme.dark ? "light" : "dark"} />
<CustomLoading isLoading={isLoading} />
{/* Header */}
<View style={styles.header}>
<Animatable.View animation="bounceIn" duration={2000}>
<Image style={styles.logo} source={images.FinalLogoOnLight} />
</Animatable.View>
<Animatable.Text
animation="bounceInLeft"
duration={2000}
style={[styles.screenTitle, { color: colors.black }]}
>
Search/Filter Receipts
</Animatable.Text>
</View>
{/* Form */}
<View>
{/* Date */}
<TouchableOpacity
style={[styles.datePickerWrapper, { backgroundColor: colors.white }]}
onPress={showDatePicker}
>
<DateTimePickerModal
isVisible={isDatePickerVisible}
mode="date"
value={model.date}
onConfirm={handleConfirm}
onCancel={hideDatePicker}
/>
<Text style={{ color: colors.black, fontFamily: "PoppinsRegular" }}>
{moment(model.date).format("YYYY-MM-DD")}
</Text>
<AntDesign name="calendar" color={colors.gray} size={sizes.h2} />
</TouchableOpacity>
{/* DropDown */}
{/* <FormDropDown dropDownRef={dropDownRef} options={vendors} /> */}
<View
style={[
styles.dropDownWrapper,
{
flexDirection: "row",
marginTop: sizes.m10,
justifyContent: "center",
alignItems: "center",
backgroundColor: colors.white,
borderColor: colors.grayLight,
},
]}
>
{model.vendors != null ? (
<FormPicker
placeholderText="Select Vendor"
selectedValue={model.vendorUserId}
onValueChange={(val) => setModel({ ...model, vendorUserId: val })}
data={model.vendors}
/>
) : null}
</View>
{/* Search Button */}
<FormButton
mystyle={{ marginTop: sizes.m20 }}
buttonTitle="Search"
onPress={() => handleSubmit()}
/>
</View>
const handleSubmit = async () => {
try {
if (model.vendorUserId == "" && model.date == "") {
Toast.show("Date or Vendor filed not be empty.");
return;
}
navigation.navigate("RootOrder", {
screen: "SearchedOrders",
params: { searchedOrders: model },
});
} catch (error) {
console.log(error);
setIsLoading(false);
}
};
Searched Order Screen
const [isLoading, setIsLoading] = useState(true);
const [start, setStart] = useState(0);
const [model, setModel] = useState({
data: [],
token: userState.token,
date: searchedOrders.date,
vendorUserId: searchedOrders.vendorUserId,
recordTotal: null,
});
// Functions
const fetchOrderHistoryByDate = async () => {
try {
setIsLoading(true);
var res = await orderHistoryByDate(model);
if (!res.Success) {
console.log("Error: ", res.Data);
setIsLoading(false);
alert(res.Data);
return;
}
var resModel = res.Data;
// console.log(resModel)
setModel({ ...model, data: resModel });
// console.log(resModel);
setIsLoading(false);
} catch (error) {
console.log(error);
setIsLoading(false);
}
};
// Functions END
const init = async () => {
await fetchOrderHistoryByDate();
setIsLoading(false);
};
useEffect(() => {
const unsubscribe = navigation.addListener("focus", () => {
setStart(Math.random() * 10000);
init();
});
return unsubscribe;
}, [start]);
function renderCardItems({ item, index }) {
return (<View style={styles.OrderItemWrapper}>
<LinearGradient
style={[styles.OrderItemWrapperLinearGrad]}
colors={[colors.white, colors.linearGray]}
>
<TouchableOpacity
// style={[styles.OrderItemWrapper, { backgroundColor: colors.white }]}
onPress={() =>
navigation.navigate("OrderDetails", { itemOrderDetails: item })
}
// key={index}
>
<View style={styles.cartItemWrapper}>
<View style={styles.cartItemDetails}>
<Text style={[styles.cartItemText, { color: colors.primary }]}>
{item.ShopName}
{/* ({index}) */}
</Text>
{/* <Text style={{ color: "#000" }}>{item.InvoiceNo}</Text> */}
<Text
style={[styles.cartItemQuantityText, { color: colors.gray }]}
>
{item.Date}
</Text>
<View
style={{
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
}}
>
<Text
style={[styles.cartItemPriceText, { color: colors.black }]}
>
AED {item.Total}
</Text>
<View
style={[
styles.statusWrapper,
{ backgroundColor: colors.primary },
]}
>
<Text style={[styles.statusText, { color: colors.white }]}>
{item.Status}
</Text>
</View>
</View>
</View>
<Image
source={{
uri: `${domain}${item.ShopPicture}`,
}}
style={styles.cardItemImage}
/>
</View>
</TouchableOpacity>
</LinearGradient>
</View>);
}
return (
<View style={[styles.container, { backgroundColor: colors.background }]}>
<StatusBar style={theme.dark ? "light" : "dark"} />
<CustomLoading isLoading={isLoading} />
<FlatList
data={model.data}
renderItem={renderCardItems}
keyExtractor={(item, index) => item.InvoiceNo + "_" + index}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
style={{ marginTop: sizes.m10 }}
style={{
paddingVertical: sizes.m10,
backgroundColor: colors.background,
}}
/>
</View>
you can use navigation.push('screanName') instead of navigation.navigate('screenName')

Getting a warning when passing from one screen to another

The error doesn't keep me from going from one screen to another but I want to understand it.
The error is:
Warning: Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state.
The first screen code is:
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isFirstConnection: true,
status: 0,
fontLoaded: false,
isConnected: false
};
}
async UNSAFE_componentWillMount() {
let lang = await retrieveAppLang();
if (lang.length == 2) {
i18n.changeLanguage(lang);
}
}
async componentDidMount() {
let isConnected = await userSessionActive();
await Font.loadAsync({
FunctionLH: require("./assets/fonts/FunctionLH-Light.ttf"),
});
const data = await this.performTimeConsumingTask();
if (data !== null && (isConnected === false || isConnected === true)) {
this.setState({
isFirstConnection: false,
status: 1,
fontLoaded: true,
isConnected: isConnected
});
}
}
performTimeConsumingTask = async () => {
return new Promise((resolve) =>
setTimeout(() => {
resolve("result");
}, 1500)
);
};
render() {
if (this.state.status == 1) {
if (this.state.isFirstConnection && this.state.fontLoaded) {
return <SplashScreen />;
} else if (this.state.isConnected === true) {
// TODO : Use Navigation !
return <Navigation screenProps={'MyTrips'}/>;
} else {
return <Navigation screenProps={'Authentication'}/>;
}
}
return (
<ImageBackground
source={require("./assets/images/background.jpg")}
style={{ flex: 1 }}
>
<View style={[styles2.container, styles2.containerCentered]}>
<StatusBar hidden={true} />
<View style={styles2.subContainer}>
<Image
style={styles2.logo}
source={require("./assets/images/logo.png")}
/>
<ActivityIndicator size="large" color="#43300E" />
<Text>Loading data...</Text>
</View>
</View>
</ImageBackground>
);;
}
}
The second screen (I guess this is the origin of the problem):
export default class MyTrips extends Component {
constructor(props) {
super(props);
this.state = {
location: null,
errorMessage: null,
measured: false,
height: 0,
value1: 0,
};
}
handleLayout = (e) => {
this.setState({
measured: true,
height: e.nativeEvent.layout.height + 1,
});
};
render() {
return (
<ImageBackground
source={require("../../assets/images/background.jpg")}
style={styles.backgroundImage}
>
<Header
backgroundImage={require("../../assets/images/bg-header.png")}
backgroundImageStyle={{
resizeMode: "stretch",
}}
centerComponent={{
text: i18n.t("mytrips.title"),
style: styles.headerComponentStyle,
}}
containerStyle={[styles.headerContainerStyle, { marginBottom: 0 }]}
statusBarProps={{ barStyle: "light-content" }}
/>
<ScrollView style={styles.containerScrollNoMargins}>
<View style={{ width: '100%', height: height / 3 + 40}}>
<WebView
geolocationEnabled={true}
source={{
uri:
"https:blabla",
}}
originWhitelist={[
"https://www.blabla.org",
"https://www.hophop.com",
]}
injectedJavaScript={`const meta = document.createElement('meta');
meta.setAttribute('content', 'width=device-width, initial-scale=0.5, maximum-
scale=0.5, user-scalable=0'); meta.setAttribute('name', 'viewport');
document.getElementsByTagName('head')[0].appendChild(meta); `}
scalesPageToFit={false}
style={{ marginHorizontal: 0, backgroundColor: "transparent" }}
/>
</View>
<View style={styles.container}>
<Text>{"\n"}</Text>
<TouchableOpacity
style={styles.touchable}
onPress={() => this.props.navigation.navigate("TripsForm")}
>
<View style={styles.view}>
<Text style={styles.textimg}>{i18n.t("mytrips.trip")}</Text>
</View>
<Image
source={require("../../assets/images/btn-background.png")}
style={styles.tripsimg}
/>
</TouchableOpacity>
</View>
<View style={styles.container}>
<Image
source={require("../../assets/images/cadran.png")}
style={styles.btnWithIcon}
/>
<View style={[styles.row, { marginTop: 28 }]}>
<Text style={styles.statText}>
{i18n.t("stats.action.dist")}
{"\n"}
<AnimateNumber
value={10000}
countBy={100}
style={{
fontFamily: "FunctionLH",
fontSize: 24,
color: "#FFF",
}}
/>
</Text>
</View>
<Text
style={[styles.textimg, styles.measure]}
onLayout={this.handleLayout}
>
0
</Text>
<Image
source={require("../../assets/images/btn-background.png")}
style={styles.cadran}
/>
</View>
<View style={styles.container}>
<Image
source={require("../../assets/images/hublo.png")}
style={styles.btnWithIcon}
/>
<View style={[styles.row, { marginTop: 28 }]}>
<Text style={styles.statText}>
{i18n.t("stats.action.flights")}
{"\n"}
<AnimateNumber
value={100}
countBy={1}
style={{
fontFamily: "FunctionLH",
fontSize: 24,
color: "#FFF",
}}
/>
</Text>
</View>
<Text
style={[styles.textimg, styles.measure]}
onLayout={this.handleLayout}
>
0
</Text>
<Image
source={require("../../assets/images/btn-background.png")}
style={styles.cadran}
/>
</View>
<Text>{"\n"}</Text>
</ScrollView>
</ImageBackground>
);
}
}
I found the solution but I don't really understand why there was an error...
Instead of:
return <Navigation screenProps={'MyTrips'}/>;
} else {
return <Navigation screenProps={'Authentication'}/>;
}
I directly imported the screens and this works:
return <MyTrips/>;
} else {
return <Authentication />;

componentWillReceiveProps not triggering from child screen inside Tabview

I'm new in React native here. I got stuck on this scenario. I have a Dashboard screen. Inside dashboard screen, there's Hitcher screen that has Tabview. And there are HitcherTrip and HitcherChat inside the Tabview. In HitcherTrip, i expected componentWillReceiveProps() will trigger after calling Actions.goToOtherLayout() but componentWillReceiveProps() is triggered on Dashboard screen (Parent screen).
Dashboard
const Menu = createDrawerNavigator(
{
First: { screen: Hitcher },
Second: { screen: Driver }
},
{
contentComponent: props => (
<ScrollView>
<View style={{ padding:20, backgroundColor:'#4ca858' }}>
<Image source={require('../assets/pp.png')} style={{ borderRadius: 40, borderWidth: 1, borderColor: '#fff',width:80, height: 80 }} />
<CustomDrawerText/>
</View>
<SafeAreaView forceInset={{ top: "always", horizontal: "never" }}>
<Drawer.Item
label="I am Hitcher"
style={styles.drawerItem}
onPress={
() => {}
}
/>
<View style={{ height:1, backgroundColor:'#a8a8a8', marginLeft: 15, marginRight: 15 }}/>
<Drawer.Item
label="I am Driver"
onPress={
() => {}
}
style={styles.drawerItem}
/>
<View style={{ height:1, backgroundColor:'#a8a8a8', marginLeft: 15, marginRight: 15 }}/>
<Drawer.Item
label="Settings"
style={styles.drawerItem}
/>
<View style={{ height:1, backgroundColor:'#a8a8a8', marginLeft: 15, marginRight: 15 }}/>
<Drawer.Item
label="Log Out"
style={styles.drawerItem}
onPress={
() => {}
}
/>
<View style={{ height:1, backgroundColor:'#a8a8a8', marginLeft: 15, marginRight: 15 }}/>
</SafeAreaView>
</ScrollView>
)
}
);
const AppNav = createAppContainer(Menu);
export default class Dashboard extends React.Component {
constructor(props) {
super(props)
}
componentWillReceiveProps(props) {
console.log("Dashboard");
}
render() {
return(
<AppNav />
)
}
}
Hitcher
export default class Hitcher extends React.Component {
state = {
index: 0,
routes: [
{ key: 'first', title: 'My Trips' },
{ key: 'second', title: 'Chats' },
],
};
async _storeItem(key, token) {
try {
var token = await AsyncStorage.setItem(key, token);
return token;
} catch (error) {
console.log(error.message);
}
}
render() {
return (
<View style={styles.container}>
<Appbar.Header
style={{ backgroundColor: '#4ca858' }}>
<Appbar.Action
icon="menu"
color="white"
onPress={() =>
this.props.navigation.dispatch(DrawerActions.toggleDrawer())
}
/>
<Appbar.Action icon={require('../assets/logo_inverted.png')} style={{flex:1, alignSelf:'center'}} size={65} color="white"/>
<Appbar.Action icon="bell" color="white"/>
</Appbar.Header>
<View style={styles.container}>
<TabView
style={{ marginTop: 10 }}
navigationState={this.state}
renderScene={SceneMap({
first: HitcherTrip,
second: HitcherChat,
})}
renderTabBar={props =>
<TabBar
{...props}
labelStyle={styles.label}
indicatorStyle={styles.indicator}
style={styles.tabbar}
getLabelText={({ route }) => route.title}
/>
}
onIndexChange={index => this.setState({ index })} />
</View>
</View>
);
}
}
HitcherTrip
export default class HitcherTrip extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
isFetching: false,
spinner: false,
itemId: 0
};
}
componentDidMount() {
this.getTrips();
}
getTrips = () => {
// codes
};
componentWillReceiveProps(props) {
console.log("HitcherTrip");
}
onRefresh() {
this.setState({isFetching: true,},() => {this.getTrips();});
}
createTrip = () => {
Actions.goToOtherLayout();
};
render() {
return(
<View style={styles.scene} >
<FlatList
showsVerticalScrollIndicator={false}
data={this.state.data}
renderItem={({item}) => {
var date = Moment(item.created_at).format('DD MMM');
var now = Moment();
var expired = Moment(item.expired_at);
var status = item.status_name;
if (now > expired && item.status == 0)
status = "Expired";
return (
<TouchableOpacity onPress={
() => {
}
}>
<Spinner
visible={this.state.spinner}
textContent={'Loading...'}
textStyle={styles.spinnerTextStyle}
/>
<View style={styles.container}>
{item.status == 0 ?
<View style={styles.header}>
<Text style={styles.headerLeftActive}>{status}</Text>
<Text style={styles.headerRightActive}>{item.request_no}</Text>
</View> :
<View style={styles.headerInActive}>
<Text style={styles.headerLeftInactive}>{status}</Text>
<Text style={styles.headerRightInactive}>{date}</Text>
</View>
}
<View style={styles.content}>
<Image source={require('../assets/fromto.png')} style={styles.image} />
<View style={styles.textContent}>
<Text style={styles.address}>{item.pickup_location}</Text>
<View style={{flex: 1}} />
<Text style={styles.address}>{item.dropoff_location}</Text>
</View>
</View>
</View>
</TouchableOpacity>
)}
}
keyExtractor={item => item.request_no}
onRefresh={() => this.onRefresh()}
refreshing={this.state.isFetching}
/>
<TouchableOpacity
style={{
alignItems:'center',
justifyContent:'center',
width:70,
position: 'absolute',
bottom: 10,
right: 10,
height:70,
backgroundColor:'#4ca858',
borderRadius:100,
elevation: 6,
}} onPress={this.createTrip}>
<Text style={{color:'#fff', fontSize: 32}}>+</Text>
</TouchableOpacity>
</View>
)
}
}
On other layout, i have set
Actions.pop(); setTimeout(()=> Actions.refresh(), 500);
to trigger componentWillReceiveProps() when press back button. But it only triggers on Dashboard screen.
How to trigger componentWillReceiveProps() on HitcherTrip? Or maybe trigger HitcherTrip function from Dashboard screen?

Access state data in other component

Hi how can I access the state where I'm fetching all the data in an other component ? I have a component Accueil where I'm fetching the data and a component UserItem where I'm styling and showing the info. So I'm in my Accueil component I have a flatlist and inside this flatlist in the renderItem function I'm passing the . So how can I access the state dataSource in UserItem ?
class UserItem extends React.Component {
render() {
const user = this.props.user;
const displayDetailForUser = this.props.displayDetailForUser;
var colorConnected;
if (user.Statut == "ON") {
colorConnected = "#1fbc26";
}
else if (user.Statut == "OFF") {
colorConnected = "#ff0303";
}
else {
colorConnected = "#ffd200";
}
return (
<TouchableOpacity style={styles.view_container} onPress={() =>
displayDetailForUser(user.MembreId, user.Pseudo, user.Photo, user.Age, user.Genre, user.Description, user.distance, user.Statut, user.localisation, user.principale )}>
<ImageBackground source={{ uri : user.Photo}} style={{ flex: 1, aspectRatio: 1, position: 'relative', borderColor: '#d6d6d6', borderWidth: 1}}>
<View style={[styles.bulle_presence, { backgroundColor: colorConnected } ]}></View>
<TouchableOpacity>
<Image style = {{aspectRatio:0.6, position:'absolute', resizeMode:'contain', height:40, width:40, right:15, top:15 }} source = {require("../Images/chat-bg.png")}/>
</TouchableOpacity>
</ImageBackground>
<View style={{ backgroundColor: '#d6d6d6', padding: 6 }}>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<View>
<Text style={{ fontSize: 25 }}>{user.Age}</Text>
</View>
<View style={{ marginRight: 7, marginLeft: 7, backgroundColor: '#000000', width: 1, height: 26 }}></View>
<View style={{ flexDirection: 'column', flex:1 }}>
<Text style={{ fontSize: 13, fontWeight: '600' }}>{user.Pseudo}</Text>
<View style={{ flexDirection: 'row', justifyContent: 'space-between'}}>
<Text style={{ fontSize: 12 }}>{user.distance}</Text>
<Text style={{ fontSize: 12 }}>{user.Genre}</Text>
</View>
</View>
</View>
</View>
</TouchableOpacity>
)
}
}
I've tried with const user = this.props.dataSource but it's not working. Thanks
Edit:
lass Accueil extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
refreshing: false,
location: null,
latitude:null,
longitude:null,
dataSource:[]
}
this.displayPosition = this.displayPosition.bind(this);
}
handleRefresh = () => {
this.setState (
{
refreshing: true,
},
() => {
setTimeout(() => {this.setState({refreshing: false,})}, 1000)
}
);
};
componentDidMount() {
const url = "SomeUrl";
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
dataSource: res
});
})
.catch(error => {
console.log("get data error:" + error);
});
}
static navigationOptions = ({ navigation }) => {
return {
headerRight: () => (
<View style={{marginLeft: 8, marginRight: 8, flexDirection: 'row', alignItems: 'center' }}>
<TouchableOpacity style={{ marginRight: 10 }} onPress={ () =>{ }}>
<Image style={{ width: 22, height: 22 }} source={require("../Images/search.png")} />
</TouchableOpacity>
<TouchableOpacity style={{marginLeft: 10, marginRight: 10 }} onPress={ () =>{{this.displayPosition()}}}>
<Image style={{ width: 22, height: 22 }} source={require("../Images/localisation.png")} />
</TouchableOpacity>
<TouchableOpacity style={{marginLeft: 10 }} onPress={ () =>{ }}>
<Image style={{ width: 22, height: 22 }} source={require("../Images/refresh.png")} />
</TouchableOpacity>
</View>
),
};
};
displayPosition = () => {
Geolocation.getCurrentPosition(
(position) => {
this.setState({
latitude: JSON.stringify(position.coords.latitude),
longitude: JSON.stringify(position.coords.longitude),
error: null,
});
console.log(this.state.latitude)
},
(error) => this.setState({ error: error.message }),
{ enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 },
);
}
_displayDetailForUser = (idUser, name, photo, age, pratique, description, distance, Statut, localisation, principale ) => {
//console.log("Display user with id " + idUser);
this.props.navigation.navigate("UserProfil", { idUser: idUser, name:name, photo: photo, age:age, pratique:pratique, description:description, distance:distance, Statut:Statut, localisation:localisation, principale:principale });
}
render() {
return (
<SafeAreaView style={{ flex:1 }}>
<View style={styles.main_container}>
<FlatList style={styles.flatList}
data={this.state.dataSource}
keyExtractor={(item) => item.MembreId}
renderItem={() => <UserItem user={this.state.dataSource} displayDetailForUser={this._displayDetailForUser} />}
numColumns={numColumns}
refreshing={this.state.refreshing}
onRefresh={this.handleRefresh} />
</View>
</SafeAreaView>
)
}
}

Modal and FlatList

i work on my first react-native project and its also my firts javascript work. I want at least a news-app with own database informations. The backend is already finish. Now im in struggle with the app- i want an Popup with Modal with informations from my api like news_image, news_content and news_title. The News are in the FlatList and now i want to click on a item to show the content in an modal popup. so, here is my code where im struggle. i get always an error. so how can i fix this problem? tanks a lot!
import React from "react";
import {
AppRegistry,
FlatList,
Image,
Platform,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
ActivityIndicator,
ListView,
YellowBox,
Alert,
TextInput
} from "react-native";
import { WebBrowser } from "expo";
import Button from "react-native-button";
import Modal from "react-native-modalbox";
import Slider from "react-native-slider";
import { MonoText } from "../components/StyledText";
export default class NewsFeed extends React.Component {
static navigationOptions = {
title: "HomeScreen"
};
constructor(props) {
super(props);
this.state = {
isLoading: true
};
YellowBox.ignoreWarnings([
"Warning: componentWillMount is deprecated",
"Warning: componentWillReceiveProps is deprecated"
]);
}
FlatListItemSeparator = () => {
return (
<View
style={{
height: 0.5,
width: "100%",
backgroundColor: "#000"
}}
/>
);
};
webCall = () => {
return fetch("http://XXXXXXXXXXXX.com/connection.php")
.then(response => response.json())
.then(responseJson => {
this.setState(
{
isLoading: false,
dataSource: responseJson
},
function() {
// In this block you can do something with new state.
}
);
})
.catch(error => {
console.error(error);
});
};
onClose() {
console.log("Modal just closed");
}
onOpen() {
console.log("Modal just opened");
}
onClosingState(state) {
console.log("the open/close of the swipeToClose just changed");
}
componentDidMount() {
this.webCall();
}
render() {
if (this.state.isLoading) {
return (
<View
style={{ flex: 1, justifyContent: "center", alignItems: "center" }}
>
<ActivityIndicator size="large" />
</View>
);
}
return (
<View style={styles.MainContainer}>
<FlatList
data={this.state.dataSource}
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={({ item }) => (
<View style={{ flex: 1, flexDirection: "row" }}>
<Image
source={{ uri: item.news_image }}
style={styles.imageView}
/>
<Text
onPress={() => this.refs.modal.open()}
style={styles.textView}
>
{item.news_title}
{"\n"}
<Text style={styles.textCategory}>{item.author}</Text>
</Text>
<Text style={styles.textViewDate}>{item.created_at}</Text>
<Modal
style={[styles.modal]}
position={"bottom"}
ref={"modal"}
swipeArea={20}
>
<ScrollView>
<View style={{ width: "100%", paddingLeft: 10 }}>
{item.news_content}
</View>
</ScrollView>
</Modal>
</View>
)}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
}
const styles = StyleSheet.create({
MainContainer: {
justifyContent: "center",
flex: 1,
margin: 5
},
imageView: {
width: "25%",
height: 100,
margin: 7,
borderRadius: 7
},
textView: {
width: "100%",
height: "100%",
textAlignVertical: "center",
padding: 10,
fontSize: 20,
color: "#000"
},
textViewDate: {
width: "30%",
textAlignVertical: "center",
padding: 15,
color: "#afafaf"
},
textCategory: {
color: "#d3d3d3",
fontSize: 12
},
modal: {
justifyContent: "center",
alignItems: "center",
height: "90%"
}
});
Check the code below and compare it with your code.
I am not sure where your error is located or what your exact error is, but you can check the example code below, which is similar to yours, for comparison.
I am using 'Axios' over fetch() because of the automatic transformation to JSON and some other beneficial stuff.
npm install --save axios
Code:
import React, { Component } from 'react'
import {
ActivityIndicator,
FlatList,
Image,
ScrollView,
Text,
TouchableOpacity,
View
} from 'react-native';
import Axios from 'axios';
import Modal from "react-native-modalbox";
export default class NewsFeed extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: [],
selectedIndex : -1
}
}
componentDidMount = () => {
Axios.get('<URL>')
.then(response => {
const { data } = response;
this.setState({dataSource : data});
}).catch(error => {
const { data } = error;
console.log(data);
});
}
_openModal = index => {
this.setState({ selectedIndex : index });
this.modalReference.open();
}
_renderSeparator = () => {
return <View style={{ flex: 1, borderBottomWidth: 0.5, borderBottomColor: '#000000' }} />
}
_renderItem = ({item, index}) => {
const {news_image, news_title, news_content, author, created_at} = item;
return <TouchableOpacity onPress={() => this._openModal(index)} >
<View style={{ flex: 1, flexDirection: 'row' }}>
<Image style={{ flex: 1, width: null, height: 200 }} source={{ uri: news_image }} />
<Text>{news_title}</Text>
<Text>{author}</Text>
<Text>{created_at}</Text>
</View>
</TouchableOpacity>;
}
render = () => {
const { dataSource, selectedIndex } = this.state;
const { news_content } = dataSource[selectedIndex];
return dataSource.length === 0 ?
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<ActivityIndicator size="large" />
</View> :
<View style={styles.MainContainer}>
<FlatList
data={dataSource}
keyExtractor={(item, index) => index.toString()}
ItemSeparatorComponent={this._renderSeparator}
renderItem={this._renderItem}
/>
<Modal ref={reference => modalReference = reference}>
<ScrollView style={{ flex: 1, padding: 20 }}>
<Text>{news_content}</Text>
</ScrollView>
</Modal>
</View>
}
}
I think the issue is in modal,
Can you re-write the code like below?
return (
<View style={styles.MainContainer}>
<FlatList
data={this.state.dataSource}
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={({ item }) => (
<View style={{ flex: 1, flexDirection: "row" }}>
<Image source={{ uri: item.news_image }} style={styles.imageView} />
<Text onPress={() => { this.setState({ item: item.news_content }, () => this.refs.modal.open()); }} style={styles.textView}>
{item.news_title}
<Text style={styles.textCategory}>{item.author}</Text>
</Text>
<Text style={styles.textViewDate}>{item.created_at}</Text>
</View>
)}
keyExtractor={(item, index) => index.toString()}
/>
<Modal
style={[styles.modal]}
position={"bottom"}
ref={"modal"}
swipeArea={20}
>
<ScrollView>
<View style={{ width: "100%", paddingLeft: 10 }}>
{this.state.item}
</View>
</ScrollView>
</Modal>
</View>
);
Only single modal is enough for popup screen.
And you can try this also,
Change your reference to
ref={ref => this.modalRef = ref}
And use like this,
this.modalRef.open()