how to use the state hook for dynamically created components in react native - react-native

I am trying to do is use useState for dynamically created components in React. In the code below I am, rendering the new components, but I cannot find a way to keep track of their state like I did for the hardcoded components. The new components are rendered when the user creates a new "post", causing new "posts" to be generated.
return (
{
results.map( (eachPost) =>
{ const [status, setStatus] = React.useState(true); },
<TouchableOpacity>
<Card>
<Text>
{JSON.parse(eachPost).title}
</Text>
<Collapsible collapsed = {status}>
{JSON.parse(eachPost).content}
</Collapsible>
</Card>
</TouchableOpacity>
)
}
)
UPDATE: I need to know how I can re-render the page
import * as React from 'react';
import { StyleSheet, Button, TextInput, Image, TouchableOpacity } from 'react-native';
import EditScreenInfo from '../components/EditScreenInfo';
import { Text, View } from '../components/Themed';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { Card } from 'react-native-elements';
import Collapsible from 'react-native-collapsible';
import Accordion from 'react-native-collapsible/Accordion';
import { CardStyleInterpolators } from '#react-navigation/stack';
import AsyncStorage from '#react-native-community/async-storage';
import { useState, useCallback } from 'react';
import Post from './Post'
import RenderToLayer from 'material-ui/internal/RenderToLayer';
import { useForceUpdate } from './useForceUpdate'
async function createNewPost (titleAndContent : string){
const post = {titleAndContent_: titleAndContent}
try {
await AsyncStorage.setItem('post' + Math.floor(100000 + Math.random() * 900000), JSON.stringify(post))
} catch (e) {
console.log(e)
}
//console.log(newPosts)
}
export default function NewsScreen({ navigation }: { navigation: any }) {
const [status, setStatus] = React.useState(true);
const [status2, setStatus2] = React.useState(true);
const [status3, setStatus3] = React.useState(true);
const [renderedData, setRenderedData] = React.useState(new Array<string>());
const [input, setInput] = React.useState('Please enter the details of the post in the format of title,content');
const [, forceUpdate] = useState();
const getData = async () => {
try {
const keys = await AsyncStorage.getAllKeys();
const result = await AsyncStorage.multiGet(keys);
console.log(result)
return result;
} catch (e) {
console.log(e);
}
};
async function parseData() {
const payments = await getData();
if (payments) {
const res = payments
//.filter((e: any[]) => e[1] && e.includes('{'))
//.map((e: any[]) => e[1]);
setRenderedData(res);
console.log(res)
}
return renderedData;
}
const results: string[] = []
renderedData.forEach( (eachPost) => {
if ((/\{"title.*}/g.test(eachPost[1]))) results.push(eachPost[1])
})
console.log(results)
React.useEffect(() => {
if (renderedData.length === 0) {
parseData();
}
forceUpdate
},[]);
return (
<View style={styles.container}>
<Text style={{ fontSize: 25}}>News</Text>
<View style = {{flexDirection: "row"}}>
<TextInput
style={{ height: 40, width: 700, borderColor: 'gray', borderWidth: 1 }}
value = {input}
onChangeText={text=>setInput(text)}
/>
<Button
onPress={()=> createNewPost(input)}
title="Add post"
color="blue"
accessibilityLabel="A button to allow the admin to create a new post"
/>
<Button
onPress = {useForceUpdate()}
title="submit"
/>
</View>
<View>
{
results.map( (eachPost) =>
<Post titleAndContent={JSON.parse(eachPost)}/>
)
}
<TouchableOpacity onPress={() => setStatus(!status)}>
<Card>
<Text>
Moore Park Lift Outage
</Text>
<Image
source={{
uri: 'https://i.imgur.com/Etr5xBn.png'
}}
style={{width: 20, height: 20, marginLeft: 10}}
>
</Image>
<Collapsible collapsed = {status}>
<Text style={{fontWeight: 'bold'}}>
UPDATED 6 Aug 09:30 {'\n'}
Details: Ongoing {'\n'}
Lift 2 at Moore Park Light Rail is temporarily out of service. For assistance, ask staff.. {'\n'}
</Text>
<Image
source={{
uri: 'https://svg-clipart.com/svg/blue/PGaAIh0-blue-wheelchair-vector.svg',
}}
style={{width: 70, height: 90, marginTop: 30}}
>
</Image>
</Collapsible>
</Card>
</TouchableOpacity>
<TouchableOpacity onPress={() => setStatus2(!status2)}>
<Card>
<Text>Dulwich Hill Line running slow</Text>
<Image
source={{
uri: 'https://i.imgur.com/iNjXQMW.png'
}}
style={{width: 20, height: 20, marginLeft: 10}}
>
</Image>
<Collapsible collapsed = {status2}>
<Text>
The Dulwich Hill Line is running slow due to track-work being conducted between Lilyfield and Rozelle Bay
</Text>
</Collapsible>
</Card>
</TouchableOpacity>
<TouchableOpacity onPress={() => setStatus3(!status3)}>
<Card >
<Text>Extra services running</Text>
<Image
source={{
uri: 'https://imgur.com/Etr5xBn'
}}
style={{width: 20, height: 20, marginLeft: 10}}
>
</Image>
<Image
source={{
uri: 'https://i.imgur.com/Etr5xBn.png'
}}
style={{width: 20, height: 20, marginLeft: 10}}
>
</Image>
<Collapsible collapsed = {status3}>
<Text>
Extra services will be running this week to help students get back to school
</Text>
</Collapsible>
</Card>
</TouchableOpacity>
</View>
<View style={styles.button}>
<Button
onPress={() => navigation.navigate('HomeScreen')}
title="Go back"
color="blue"
accessibilityLabel="A button to click so that you can go back to the home page"
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'flex-start',
justifyContent: 'flex-start',
flexDirection: 'column',
},
title: {
fontSize: 20,
fontWeight: 'bold',
alignItems: 'center',
justifyContent: 'center',
},
separator: {
marginVertical: 30,
height: 1,
width: '80%',
},
button: {
margin: 10,
flexDirection: 'row',
},
news:{
flexDirection:'column',
backgroundColor: 'white',
},
eachnews:{
margin: 500,
fontWeight: 'bold',
alignItems: 'flex-start',
justifyContent: 'flex-start',
},
textnews:{
fontSize: 20,
},
});

I think you need more knowledge related to basics of react.
You need a Post component
Post.js
export default (props) => {
const [status, setStatus] = useState();
return (<TouchableOpacity>
<Card>
<Text>
{props.title}
</Text>
<Collapsible collapsed = {status}>
{props.content}
</Collapsible>
</Card>
</TouchableOpacity>);
};
When u call Post
App.js
import Post from './Post.js'
export default App = ()
{
const result = [];
return (
results.map( (eachPost) =>{
const eachPost = JSON.parse(eachPost);
return (<Post title={eachPost.title} content={eachPost.content})/>)
}
}
)

Related

Algolia and Hooks in React Native can't navigate to an other page

I'm using algolia with react Native and Expo, I make a connectInfiniteHits and i want to navigate to an other page when the user press a hit, i have the following code:
import React from "react";
import { StyleSheet, Text, View, FlatList, Image, TouchableOpacity, AsyncStorage } from "react-native";
import { connectInfiniteHits } from "react-instantsearch-native";
import PropTypes from 'prop-types';
import { useNavigation } from '#react-navigation/native';
const navigation = (id) => {
const navigation = useNavigation();
AsyncStorage.setItem("RepID", id);
navigation.navigate("RepProfile");
}
const InfiniteHitsRep = ({ hits, hasMore, refine }) => (
<View style={styles.container}>
<FlatList
data={hits}
onEndReached={() => hasMore && refine()}
onEndReachedThreshold={0}
keyExtractor={item => item.objectID}
initialNumToRender={10}
ItemSeparatorComponent={() => <View style={styles.separator} />}
renderItem={({ item }) => (
<TouchableOpacity onPress={() => navigation(item.rep_id)} style={{ flexDirection: 'row', alignItems: 'center' }}>
<Image style={styles.image} source={{ uri: item.rep_img_url }} />
<Text style={{ flex: 3 }}>{item.rep_title} {item.rep_first_name} {item.rep_last_name}</Text>
<Image style={styles.image} source={{ uri: item.house_img_url }} />
</TouchableOpacity>
)}
/>
</View>
)
);
InfiniteHitsRep.propTypes = {
hits: PropTypes.arrayOf(PropTypes.object).isRequired,
hasMore: PropTypes.bool.isRequired,
refine: PropTypes.func.isRequired,
};
export default connectInfiniteHits(InfiniteHitsRep);
const styles = StyleSheet.create({
container: {
padding: 16,
},
separator: {
marginTop: 16,
marginBottom: 16,
height: 1,
backgroundColor: "#DDDDDD",
},
image: {
flex: 1,
width: 50,
height: 50,
borderRadius: 5,
backgroundColor: 'white',
resizeMode: 'contain',
}
});
And when I press a hit I have this error message:
[![```
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
[1]: https://i.stack.imgur.com/yDYRg.png
Hooks can be used inside a function component only
Change your code to this
import React from "react";
import {
StyleSheet,
Text,
View,
FlatList,
Image,
TouchableOpacity,
AsyncStorage,
} from "react-native";
import { connectInfiniteHits } from "react-instantsearch-native";
import PropTypes from "prop-types";
import { useNavigation } from "#react-navigation/native";
const InfiniteHitsRep = ({ hits, hasMore, refine }) => {
const navigation = useNavigation(); // Call hook here
const navigation = (id) => {
AsyncStorage.setItem("RepID", id);
navigation.navigate("RepProfile"); // Use Here
};
return (
<View style={styles.container}>
<FlatList
data={hits}
onEndReached={() => hasMore && refine()}
onEndReachedThreshold={0}
keyExtractor={(item) => item.objectID}
initialNumToRender={10}
ItemSeparatorComponent={() => <View style={styles.separator} />}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() => navigation(item.rep_id)}
style={{ flexDirection: "row", alignItems: "center" }}
>
<Image style={styles.image} source={{ uri: item.rep_img_url }} />
<Text style={{ flex: 3 }}>
{item.rep_title} {item.rep_first_name} {item.rep_last_name}
</Text>
<Image style={styles.image} source={{ uri: item.house_img_url }} />
</TouchableOpacity>
)}
/>
</View>
);
};
InfiniteHitsRep.propTypes = {
hits: PropTypes.arrayOf(PropTypes.object).isRequired,
hasMore: PropTypes.bool.isRequired,
refine: PropTypes.func.isRequired,
};
export default connectInfiniteHits(InfiniteHitsRep);
const styles = StyleSheet.create({
container: {
padding: 16,
},
separator: {
marginTop: 16,
marginBottom: 16,
height: 1,
backgroundColor: "#DDDDDD",
},
image: {
flex: 1,
width: 50,
height: 50,
borderRadius: 5,
backgroundColor: "white",
resizeMode: "contain",
},
});

Expo Camera crashing on Android devices

I'm trying to use Expo Camera to take pictures and scan barcodes, but for some reason whenever I run it on an Android device when I am about to use the camera the app crashes. This is the code used for taking pictures/scanning barcodes. I don't believe it is a code issue though:
import React, { useState, useEffect } from 'react';
import { Text, View, TouchableOpacity, Image, Alert } from 'react-native';
import { IconButton, Colors, Button } from 'react-native-paper'
import { Camera } from 'expo-camera';
const CustomBarcodeScanner = ({ handleBarCodeScanned, scanning, handleTakePhotos, handleGoBack }) => {
const [hasPermission, setHasPermission] = useState(null)
const [preview, setPreview] = useState(false)
const [currentPhoto, setCurrentPhoto] = useState('')
const [photoCount, setPhotoCount] = useState(0)
const displaySnap = scanning ? "none" : "flex"
const snap = async() => {
if(this.camera){
let photo = await this.camera.takePictureAsync({base64: true})
setCurrentPhoto(photo['base64'])
setPhotoCount(photoCount + 1)
setPreview(true)
}
}
const acceptPhoto = () => {
setPreview(false)
handleTakePhotos(currentPhoto)
setCurrentPhoto('')
console.log(photoCount)
if(photoCount >= 2){
handleGoBack()
return
}
Alert.alert(
"Tomar otra foto",
"¿Desea tomar otra foto?",
[
{
text: 'Sí',
onPress: () => {
}
},
{
text: 'No',
onPress: () => {
handleGoBack()
},
style: "cancel"
}
],
{ cancelable: false }
)
}
const retakePhoto = () => {
setPreview(false)
setCurrentPhoto('')
}
useEffect(() => {
(async () => {
const { status } = await Camera.requestPermissionsAsync();
setHasPermission(status === 'granted');
})()
}, [])
if (hasPermission === null) {
return <View />;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
return preview ?
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Image style={{width: '100%', height: '70%'}} source={{ uri: `data:image/jpg;base64,${currentPhoto}` }} />
<View style={{flexDirection: 'row'}}>
<Button
mode='contained'
color={Colors.purple500}
style={{padding: 10, margin: 10}}
onPress={acceptPhoto}
>
Aceptar
</Button>
<Button
mode='contained'
color={Colors.purple500}
style={{padding: 10, margin: 10}}
onPress={retakePhoto}
>
Re-Tomar Foto
</Button>
</View>
</View>
:
<View style={{ flex: 1 }}>
<Camera
style={{ flex: 1 }} type={Camera.Constants.Type.back} onBarCodeScanned={handleBarCodeScanned} ref={ref => {this.camera = ref}}>
<View
style={{
flex: 1,
backgroundColor: 'transparent',
flexDirection: 'row',
}}
>
<TouchableOpacity
style={{
flex: 0.1,
alignSelf: 'flex-end',
alignItems: 'center',
}}
onPress={handleGoBack}
>
<Text style={{ fontSize: 18, marginBottom: 10, color: 'white' }}>Regresar</Text>
</TouchableOpacity>
<TouchableOpacity
style={{
flex: 0.8,
alignSelf: 'flex-end',
alignItems: 'center',
display: displaySnap
}}
onPress={() => snap() }
>
<IconButton icon='camera' background={Colors.black} size={50} color={Colors.white} />
</TouchableOpacity>
</View>
</Camera>
</View>
}
export default CustomBarcodeScanner
I'm thinking it could be dependency related? I'm not sure if I need to install some libraries or add some code to get it to work. The error I get says Error while updating property 'nativeBackgroundAndroid' of a view managed by: RCTView
My expo version is 4.1.6
Apparently this is an issue with the Touchable Element with Ripple Effect. People are discussing that here.
react-native-paper seems to implement that on their button. Try to use the default button from React-Native, that should fix it :)

How to Choose File From Button next to input filed in the form in react native. (See image for better understanding)

I am new to react native. and I have created A form. Now in that form I want A choose file button next to the input filed. And when user click on that Choose file button. Either camera will open or gallery will open(as per user choice) and then When user click on choose file button A small image or just image name comes below the choose file button
here is my image for better understanding
here is my code
import React, {useState, Component} from 'react';
import {Picker, Text, StyleSheet, View,
TextInput, Button, KeyboardAvoidingView,
ScrollView, Alert, alert, TouchableOpacity, Dimensions,} from 'react-native';
import { StackNavigator, navigation} from "react-navigation";
import { Card, Badge, Block, } from "../components";
import { theme, mocks } from "../constants";
import DigSign from "./DigSign"
import { Ionicons } from '#expo/vector-icons';
const { height } = Dimensions.get("window");
const { width } = Dimensions.get("window");
class PickerDemo extends Component{
constructor(props) {
super(props);
this.state={
};
}
validateInputs = () => {
// if (!this.state.accountNo.trim())
if (!/[A-Z]{5}[0-9]{4}[A-Z]{1}/.test(this.state.PAN))
{
this.setState({ PANError: 'Please enter valid PAN Number' })
return;
}
if (!/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}$/.test(this.state.GST))
{
this.setState({ GSTError: 'Please enter valid GST Number' })
return;
}
if (!/^[2-9]{1}[0-9]{3}\\s[0-9]{4}\\s[0-9]{4}$/.test(this.state.Aadhar))
{
this.setState({ AadharError: 'Please enter valid Aadhar Number' })
return;
}
else {
Alert.alert("All fields validated")
return;
}
}
handlePAN = (text) => {
this.setState({ PANError: '' })
this.setState({ PAN: text })
}
handleGST = (text) => {
this.setState({ GSTError: '' })
this.setState({ GST: text })
}
handleAadhar = (text) => {
this.setState({ AadharError: '' })
this.setState({ Aadhar: text })
}
render(){
const offset = (Platform.OS === 'android') ? -200 : 0;
const { navigation } = this.props;
return (
<View style={{flex: 1}}>
<View style={styles.header}>
<Ionicons style={{paddingLeft:20}} name="arrow-back" size={40}
color="black" onPress={() => navigation.navigate("FormItems")} />
<Text style={{fontSize:20, paddingLeft: 70, paddingTop: 10}}>KYC Details</Text>
</View>
<KeyboardAvoidingView keyboardVerticalOffset={offset} style={styles.form} behavior='padding'>
<Text style={styles.formLabel}> OTHER INFORMATION Form </Text>
<ScrollView style={{flex: 1,}} showsVerticalScrollIndicator={false}>
<View style={{flexDirection:'row'}}>
<TextInput maxLength={30} placeholder="PAN Card Number *" style={styles.inputStyle}
onChangeText={this.handlePAN} />
<View style={{justifyContent:"center"}}>
<Button title={'Choose File'}/>
</View>
</View>
<Text>{this.state.PANError}</Text>
<View style={{flexDirection:'row'}}>
<TextInput maxLength={30} placeholder="GSTIN Number*" style={styles.inputStyle}
onChangeText={this.handleGST} />
<View style={{justifyContent:"center"}}>
<Button title={'Choose File'}/>
</View>
</View>
<Text>{this.state.GSTError}</Text>
<View style={{flexDirection:'row'}}>
<TextInput maxLength={30} placeholder="Aadhar Card Number*" style={styles.inputStyle}
onChangeText={this.handleAadhar} />
<View style={{justifyContent:"center"}}>
<Button title={'Choose File'}/>
</View>
</View>
<Text>{this.state.AadharError}</Text>
<TouchableOpacity
onPress={() => navigation.navigate("DigSign")}
>
<Card center middle shadow style={styles.category}>
<Text medium height={1} size={1}>
Digital Signature
</Text>
</Card>
</TouchableOpacity>
<TouchableOpacity
onPress={() => navigation.navigate("ImgpickWithCam")}
>
<Card center middle shadow style={styles.category}>
<Text medium height={1} size={1}>
Pick An Image From Camera
</Text>
</Card>
</TouchableOpacity>
</ScrollView>
<View style={{ height: 30 }} />
<Button style={styles.inputStyleB}
title="Submit"
color="#808080"
onPress={() => this.validateInputs()}
/>
</KeyboardAvoidingView>
</View>
);
};
}
const styles = StyleSheet.create({
form: {
flex: 1,
justifyContent: "center",
flex: 1,
backgroundColor: "rgb(247, 146, 57)",
alignItems: 'center',
paddingTop: 50,
},
container: {
flex: 1,
backgroundColor: "rgb(247, 146, 57)",
alignItems: 'center',
// justifyContent: 'center',
paddingTop: 15
},
formLabel: {
fontSize: 20,
color: 'rgb(10, 10, 10)',
},
inputStyle: {
marginTop: 20,
width: 220,
height: 40,
paddingHorizontal: 10,
borderRadius: 50,
backgroundColor: 'rgb(255, 252, 252)',
},
formText: {
alignItems: 'center',
justifyContent: 'center',
color: '#fff',
fontSize: 20,
},
text: {
color: '#fff',
fontSize: 20,
},
category: {
marginTop: 20,
// this should be dynamic based on screen width
minWidth: (width - theme.sizes.padding * -10 - theme.sizes.base) / 2,
maxWidth: (width - theme.sizes.padding * -10 - theme.sizes.base) / 2,
maxHeight: (height - theme.sizes.padding * -50 - theme.sizes.base) / 2,
},
header:{
flexDirection: 'row'
}
});
export default PickerDemo;
Here is the solution based on the previous examples that I have given you.
You just had to implement the conditional rendering to it.
Just a couple of lines of code what was needed :)
Working Example: Expo Snack
import React, { useState, useEffect } from 'react';
import {
StyleSheet,
View,
Button,
Image,
FlatList,
Text,
TextInput,
} from 'react-native';
import { Camera } from 'expo-camera';
import { Ionicons } from '#expo/vector-icons';
import * as ImagePicker from 'expo-image-picker';
export default function Add() {
const [cameraPermission, setCameraPermission] = useState(null);
const [galleryPermission, setGalleryPermission] = useState(null);
const [showCamera, setShowCamera] = useState(false);
const [camera, setCamera] = useState(null);
const [imageUri, setImageUri] = useState([]);
const [type, setType] = useState(Camera.Constants.Type.back);
const [imageArray, setImageArray] = useState([]);
const permisionFunction = async () => {
// here is how you can get the camera permission
const cameraPermission = await Camera.requestPermissionsAsync();
console.log('camera permission:', cameraPermission.status);
setCameraPermission(cameraPermission.status === 'granted');
const imagePermission = await ImagePicker.getMediaLibraryPermissionsAsync();
console.log('permission:', imagePermission.status);
setGalleryPermission(imagePermission.status === 'granted');
if (
imagePermission.status !== 'granted' &&
cameraPermission.status !== 'granted'
) {
alert('Permission for media access needed.');
}
};
useEffect(() => {
permisionFunction();
}, []);
const takePicture = async () => {
if (camera) {
const data = await camera.takePictureAsync(null);
console.log(data.uri);
setImageUri(data.uri);
setImageArray([...imageArray, data.uri]);
setShowCamera(false);
}
};
const pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
quality: 1,
});
console.log(result.uri);
if (!result.cancelled) {
setImageArray([...imageArray, result.uri]);
}
};
return (
<View style={styles.container}>
{showCamera && (
<Camera ref={(ref) => setCamera(ref)} style={{ flex: 1 }} type={type} />
)}
{showCamera && <Button title={'Click'} onPress={takePicture} />}
{!showCamera && (
<>
<View
style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<TextInput placeholder={'Enter PAN Number'} />
<View style={{ flexDirection: 'row' }}>
<Button
title={'Camera'}
onPress={() => {
setShowCamera(true);
}}
/>
<Button title={'Gallery'} onPress={pickImage} />
</View>
</View>
{imageArray.length > 0 && (
<View style={{ height: 110 }}>
<FlatList
horizontal
data={imageArray}
renderItem={({ item }) => (
<Image
source={{ uri: item }}
style={{
width: 100,
height: 100,
borderRadius: 10,
margin: 5,
}}
/>
)}
/>
</View>
)}
</>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
marginTop: 30,
flex: 1,
},
fixedRatio: {
flex: 1,
},
});

React Native TouchableOpacity onPress using index/key of the item in FlatList

We created a FlatList component and a Contact screen in our app. We want to add 'heart' icon to the near of the images in Contact screen. We added heart icon near to all of items. But, if we pressed one of these icons, it changes all of theirs colors to red, not only one of them. We want to change the color of clicked item.
Screenshot of our program:
This is our FlatList component:
import React, { Component } from 'react';
import { View, Text, SafeAreaView, StyleSheet, FlatList, Image, TouchableOpacity,
TouchableWithoutFeedback, TextInput } from 'react-native';
import { Right, Icon } from 'native-base';
import data from '../../data';
export default class FlatListComponent extends Component {
state = {
text: '',
contacts: data,
like: false,
color: 'white',
}
toggleLike=()=>{
this.setState({
like: !this.state.like
})
if(this.state.like){
this.setState({
color: 'red',
})
}else{
this.setState({
color: 'white',
})
}
}
renderContactsItem = ({item, index}) => {
return (
<View style={[styles.itemContainer]}>
<Image
style={styles.avatar}
source={{ uri: item.image }} />
<View style={styles.textContainer}>
<Text style={[styles.name], {color: '#fafafa'}}>{item.first_name}</Text>
<Text style={{ color: '#fafafa' }}>{item.last_name}</Text>
</View>
<Right style={{justifyContent: 'center'}}>
<TouchableWithoutFeedback onPress={this.toggleLike}>
{/*{this.like ? (
<Icon name="heart" type='FontAwesome' style={{paddingRight: 10, fontSize: 30, color: 'red'}} />
) :
( <Icon name="heart" type='FontAwesome' style={{paddingRight: 10, fontSize: 30, color: 'white'}} /> )
}*/}
<Icon name='heart' type='FontAwesome' size={32} style={{color: this.state.color === "white" ? 'white' :'red', paddingRight: 10 }}/>
{/*<Icon name="heart" type='FontAwesome' style={{paddingRight: 10, fontSize: 30, color: this.state.color}} />*/}
</TouchableWithoutFeedback>
</Right>
</View>
);
}
searchFilter = text => {
const newData = data.filter(item => {
const listItems = `${item.first_name.toLowerCase()}`
return listItems.indexOf(text.toLowerCase()) > -1;
});
this.setState({
contacts: newData,
});
};
renderHeader = () => {
const {text} = this.state;
return (
<View style={styles.searchContainer}>
<TextInput
onChangeText = {text => {
this.setState ({
text,
});
this.searchFilter(text);
}}
value={text}
placeholder="Search..."
style={styles.searchInput} />
</View>
)
}
render() {
return (
<FlatList
ListHeaderComponent={this.renderHeader()}
renderItem={this.renderContactsItem}
keyExtractor={item => item.id}
data={this.state.contacts}
/>
);
}
}
const styles = StyleSheet.create({
itemContainer: {
flex: 1,
flexDirection: 'row',
paddingVertical: 10,
borderBottomWidth: 1,
borderBottomColor: '#eee'
},
avatar: {
width: 50,
height: 50,
borderRadius: 25,
marginHorizontal: 10,
},
textContainer: {
justifyContent: 'space-around',
},
name: {
fontSize: 16,
},
searchContainer: {
padding: 10
},
searchInput: {
fontSize: 16,
backgroundColor: '#f9f9f9',
padding: 10,
}
});
Our Contact screen is just:
import React from 'react';
import 'SafeAreaView' from 'react-native';
import FlatList from './FlatList';
export default function Contact() {
<SafeAreaView style={{ flex: 1 }}>
<FlatList />
</SafeAreaView>
}
How can we implement this?
I've run into this recently :) One option is to make the renderContactsItem its own component. For example:
const RenderContactsItem = ({item, index}) => {
const [like, setLike] = useState(false);
const [color, setColor] = useState("white");
const toggleLike = () => {
setLike(!like)
if(like) {
setColor("red");
} else {
setColor("white");
}
}
return (
<View style={[styles.itemContainer]}>
<Image
style={styles.avatar}
source={{ uri: item.image }} />
<View style={styles.textContainer}>
<Text style={[styles.name], {color: '#fafafa'}}>{item.first_name}</Text>
<Text style={{ color: '#fafafa' }}>{item.last_name}</Text>
</View>
<Right style={{justifyContent: 'center'}}>
<TouchableWithoutFeedback onPress={toggleLike}>
<Icon name='heart' type='FontAwesome' size={32} style={{color, paddingRight: 10 }}/>
</TouchableWithoutFeedback>
</Right>
</View>
);
}
In this case, each item manages its own state, so setting like doesn't set it for every item.
Another option would be to build an object with "like" states and set the values as the hearts are pressed. For example:
state = {
text: '',
contacts: data,
like: {},
color: 'white', // You don't need this
}
Then, when a heart is pressed, you can send toggleLike the index, and set the state like so:
toggleLike = (index) => {
let newLike = {...this.state.like};
newLike[index] = !Boolean(newLike[index]);
this.setState({
like: newLike,
});
}
And render the color conditionally depending on the index value of the like state, like so:
<Icon name='heart' type='FontAwesome' size={32} style={{color: this.state.like[index] ? 'red' :'white', paddingRight: 10 }}/>

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()