Related
I created a custom component that consist of a flatList to display a kind like a grid in a collapse component. I am getting an error attached to this post when I tried to use a flatlist inside collpase component.
my flatListcomponent******
import React from "react";
import {
SafeAreaView,
View,
FlatList,
StyleSheet,
Text,
StatusBar,
TouchableHighlight,
} from "react-native";
const DATA = [
{
id: "TR",
title: "TRIM",
},
{
id: "DC",
title: "DEEP CONDITION",
},
{
id: "BR",
title: "BRAIDS",
},
{
id: "TW",
title: "TWISTS",
},
{
id: "FA",
title: "FADE",
},
{
id: "CO",
title: "COLOR",
},
{
id: "LU",
title: "LINE-UP",
},
{
id: "TU",
title: "TOUCH-UP",
},
{
id: "SH",
title: "SHAMPOO",
},
{
id: "DL",
title: "DREAD LOCKS",
},
];
const Item = ({ title, onPress }) => (
<TouchableHighlight
//onHideUnderlayColor,
onPress={() => console.log("tapped", title)}
>
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
</TouchableHighlight>
);
export default function FlatListComponent({ onPress }) {
const renderItem = ({ item }) => <Item title={item.title} />;
const numColumns = 2;
return (
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={(item) => item.id}
numColumns={numColumns}
//onPress={() => console.log("Message selected", item)}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
backgroundColor: "#2B6565",
marginTop: StatusBar.currentHeight || 0,
alignItems: "center",
height: 100,
margin: 5,
},
item: {
backgroundColor: "#084F52",
padding: 15,
marginVertical: 3,
marginHorizontal: 3,
width: 200,
height: 55,
},
title: {
fontSize: 12,
color: "#FFFFFF",
justifyContent: "center",
padding: 10,
fontWeight: "bold",
letterSpacing: 3,
},
});
My main screen where I want to display my FaltList component:
<Collapse>
<CollapseHeader>
<View style={styles.heading_container}>
<Text style={styles.top_heading_text}>SERVICES</Text>
</View>
</CollapseHeader>
<CollapseBody>
<FlatListComponent />
</CollapseBody>
</Collapse>
The error:
I would appreciate any help. Thank you!
I found this piece of code in one of the tutorials, as I understand it, when entering text inside the input, the text is saved in asyncstorage, but how to make that when the button is pressed, an object is formed and added to the state like this {id: 1, name: 'Alex'} how can I reolize it?
import React, { Component } from 'react'
import { StatusBar } from 'react-native'
import { AsyncStorage, Text, View, TextInput, StyleSheet } from 'react-native'
class AsyncStorageExample extends Component {
state = {
'name': ''
}
componentDidMount = () => AsyncStorage.getItem('name').then((value) => this.setState({ 'name': value }))
setName = (value) => {
AsyncStorage.setItem('name', value);
this.setState({ 'name': value });
}
render() {
return (
<View style = {styles.container}>
<TextInput style = {styles.textInput} autoCapitalize = 'none'
onChangeText = {this.setName}/>
<Text>
{this.state.name}
</Text>
</View>
)
}
}
export default AsyncStorageExample
const styles = StyleSheet.create ({
container: {
flex: 1,
alignItems: 'center',
marginTop: 50
},
textInput: {
margin: 5,
height: 100,
borderWidth: 1,
backgroundColor: '#7685ed'
}
})
You can check following code it will help to solve your problem.
import React, { Component } from "react";
import { StatusBar } from "react-native";
import {
AsyncStorage,
TouchableOpacity,
Text,
View,
TextInput,
StyleSheet,
} from "react-native";
class AsyncStorageExample extends Component {
state = {
name: "",
savedData: "",
};
componentDidMount = () =>
AsyncStorage.getItem("name").then((value) =>
this.setState({ savedData: value })
);
setData = (value) => {
AsyncStorage.setItem("name", JSON.stringify([{ id: 1, name: value }]));
};
getData = (value) => {
AsyncStorage.getItem("name").then((value) =>
this.setState({ savedData: value })
);
};
render() {
return (
<View style={styles.container}>
<TextInput
style={styles.textInput}
autoCapitalize="none"
value={this.state.value}
onChangeText={(value) => {
this.setData(value);
}}
/>
<Text>{this.state.savedData}</Text>
<TouchableOpacity
activeOpacity={0.5}
onPress={() => {
this.setData(this.state.name);
}}
>
<View style={styles.saveButton}>
<Text style={styles.textStyle}>{"Save Data"}</Text>
</View>
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.5}
onPress={() => {
this.getData(this.state.name);
}}
>
<View style={styles.saveButton}>
<Text style={styles.textStyle}>{"Get Data"}</Text>
</View>
</TouchableOpacity>
</View>
);
}
}
export default AsyncStorageExample;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
marginTop: 50,
},
textStyle: {
color: "white",
textAlign: "center",
},
saveButton: {
width: 100,
height: 40,
marginBottom:10,
marginTop:15,
borderRadius: 8,
borderColor: "white",
justifyContent: "center",
borderWidth: 1,
color: "white",
backgroundColor: "#7685ed",
},
textInput: {
width: "80%",
height: 100,
borderRadius: 5,
borderWidth: 1,
backgroundColor: "#7685ed",
},
});
I'm stuck with trying to make a flatlist work with one selection and change only it's background. I already got the id from the pressed item and I'm passing the information to another page. But after clicking, it's style is not changing. I need to select just one and if I click on another it should deselect the first one and keep the new one selected.
My code is as folows:
import React, { Component } from 'react';
import { StyleSheet, Text, View, Image, TouchableOpacity, FlatList, Linking, ActivityIndicator } from 'react-native';
import { Searchbar } from 'react-native-paper';
export default class Merchants extends Component {
constructor() {
super()
this.state = {
search: '',
loading: false,
merchantObj: [],
btnDisabled: true,
itemId: null,
imgLink: null,
listClicked: false,
}
this.arrayholder = [];
}
componentDidMount() {
const merchantUrl = 'http://165.227.43.115:8080/merchant/merchant'
fetch(merchantUrl)
.then(response => response.json())
.then(data => {
this.setState({ merchantObj: data, loading: false },
function () {
this.arrayholder = data;
})
})
.catch(error => {
console.log(error)
});
}
search = text => {
console.log(text);
};
clear = () => {
this.search.clear();
};
SearchFilterFunction(text) {
//passing the inserted text in textinput
const newData = this.arrayholder.filter(function (item) {
//applying filter for the inserted text in search bar
const itemData = item.name ? item.name.toUpperCase() : ''.toUpperCase();
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
this.setState({
//setting the filtered newData on datasource
//After setting the data it will automatically re-render the view
merchantObj: newData,
search: text,
});
}
FlatListItemSeparator = () => {
return (
<View
style={{
height: 1,
width: "95%",
justifyContent: 'center',
backgroundColor: "#DCDCDC",
}}
/>
);
}
MerchSelected = (selectedId) => {
if (this.state.btnDisabled === true) {
return (
<View style={styles.btnDsb}>
<Text style={styles.txtBtn}>Select</Text>
</View>
)
} else {
return (
<TouchableOpacity onPress={(item) => this.props.navigation.navigate('Main', { itemId: this.state.itemId, itemImg: this.state.imgLink })}>
<View style={styles.btnSelect}>
<Text style={styles.txtBtn}>Select</Text>
</View>
</TouchableOpacity>
)
}
}
PressedItem = (itemId, itemImg) => {
console.log(itemId)
this.setState({ itemId: itemId, btnDisabled: false, imgLink: itemImg })
}
renderItem = ({ item }) => {
return (
<TouchableOpacity onPress={() => this.PressedItem(item.id, item.image)} >
<View style={styles.listItem} >
<Image
style={{ width: 80, height: 80 }}
source={{ uri: `${item.image}` }} />
<View style={{ flexDirection: 'column', marginLeft: 2 }}>
< Text style={{ fontWeight: 'bold', fontSize: 20 }} > {item.name} </Text>
{item.shoppingOption == 'STORE' ? <Text>Store</Text> : <Text>In-Store & Online</Text>}
<Text>${item.minAmount} - ${item.maxAmount}</Text>
<Text style={{ color: '#00CED1' }}
onPress={() => Linking.openURL(`${item.website}`)}>
view website
</Text>
</View>
</View>
</TouchableOpacity>
)
}
render() {
if (this.state.loading) {
return (
<View>
<Text>Loading...</Text>
</View>
);
}
return (
<View style={styles.container} >
<View style={styles.searchBar}>
<Searchbar
round
placeholder="Search"
onChangeText={text => this.SearchFilterFunction(text)}
onClear={text => this.SearchFilterFunction('')}
value={this.state.search}
/>
</View>
<View style={styles.merchantsList}>
<FlatList
data={this.state.merchantObj}
renderItem={this.renderItem}
ItemSeparatorComponent={this.FlatListItemSeparator}
keyExtractor={item => item.id.toString()}
extraData={this.state}
>
</FlatList>
</View>
<View style={styles.footerBtn}>
<TouchableOpacity onPress={() => this.props.navigation.navigate('Main', { itemId: undefined })}>
<View style={styles.btnSelect}>
<Text style={styles.txtBtn}>Cancel</Text>
</View>
</TouchableOpacity>
{this.state.btnDisabled === true ? this.MerchSelected('Sim') : this.MerchSelected('Nao')}
</View>
</View >
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f2f2f4',
alignItems: 'center',
},
searchBar: {
flex: 1,
top: '5%',
width: '90%',
backgroundColor: 'rgba(242, 242, 244,0.5)'
},
merchantsList: {
flex: 6,
width: '95%',
},
footerBtn: {
flex: 1,
width: '100%',
},
listItem: {
flexDirection: 'row',
marginTop: 5,
},
notSelected: {
backgroundColor: '#f2f2f4'
},
listItemSlc: {
backgroundColor: '#48D1CC',
},
btnSelect: {
justifyContent: 'center',
width: '95%',
borderRadius: 5,
borderColor: '#00CED1',
borderStyle: 'solid',
borderWidth: 2,
height: 40,
marginTop: 5,
marginLeft: 8,
},
btnDsb: {
justifyContent: 'center',
width: '95%',
borderRadius: 5,
backgroundColor: 'gray',
height: 40,
marginTop: 5,
marginLeft: 8,
},
txtBtn: {
textAlign: 'center',
color: '#00CED1',
fontSize: 20,
},
})
import React, { Component } from 'react';
import { StyleSheet, Text, View, Image, TouchableOpacity, FlatList, Linking, ActivityIndicator } from 'react-native';
import { Searchbar } from 'react-native-paper';
export default class Merchants extends Component {
constructor() {
super()
this.state = {
search: '',
loading: false,
merchantObj: [],
btnDisabled: true,
itemId: null,
imgLink: null,
listClicked: false,
itemindex:"",
}
this.arrayholder = [];
}
componentDidMount() {
const merchantUrl = 'http://165.227.43.115:8080/merchant/merchant'
fetch(merchantUrl)
.then(response => response.json())
.then(data => {
//var l_Data = [];
//for (var l_index = 0; l_index < data.length; l_index++)
//{
// l_Data[l_index] = {
// }
//}
this.setState({ merchantObj: data, loading: false },
function () {
this.arrayholder = data;
})
})
.catch(error => {
console.log(error)
});
}
search = text => {
console.log(text);
};
clear = () => {
this.search.clear();
};
SearchFilterFunction(text) {
//passing the inserted text in textinput
const newData = this.arrayholder.filter(function (item) {
//applying filter for the inserted text in search bar
const itemData = item.name ? item.name.toUpperCase() : ''.toUpperCase();
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
this.setState({
//setting the filtered newData on datasource
//After setting the data it will automatically re-render the view
merchantObj: newData,
search: text,
});
}
FlatListItemSeparator = () => {
return (
<View
style={{
height: 1,
width: "95%",
justifyContent: 'center',
backgroundColor: "#DCDCDC",
}}
/>
);
}
MerchSelected = (selectedId) => {
if (this.state.btnDisabled === true) {
return (
<View style={styles.btnDsb}>
<Text style={styles.txtBtn}>Select</Text>
</View>
)
} else {
return (
<TouchableOpacity onPress={(item) => this.props.navigation.navigate('Main', { itemId: this.state.itemId, itemImg: this.state.imgLink })}>
<View style={styles.btnSelect}>
<Text style={styles.txtBtn}>Select</Text>
</View>
</TouchableOpacity>
)
}
}
PressedItem = (itemId, itemImg) => {
console.log(itemId)
this.setState({ itemId: itemId, btnDisabled: false, imgLink: itemImg })
}
renderItem = ({ item }) => {
return (
<TouchableOpacity onPress={() => { this.PressedItem(item.id, item.image), this.setState({ itemindex: item.id }) }} >
<View style={this.state.itemindex == item.id ? styles.SelectedlistItem : styles.listItem} >
<Image
style={{ width: 80, height: 80 }}
source={{ uri: `${item.image}` }} />
<View style={{ flexDirection: 'column', marginLeft: 2 }}>
< Text style={{ fontWeight: 'bold', fontSize: 20 }} > {item.name} </Text>
{item.shoppingOption == 'STORE' ? <Text>Store</Text> : <Text>In-Store & Online</Text>}
<Text>${item.minAmount} - ${item.maxAmount}</Text>
<Text style={{ color: '#00CED1' }}
onPress={() => Linking.openURL(`${item.website}`)}>
view website
</Text>
</View>
</View>
</TouchableOpacity>
)
}
render() {
if (this.state.loading) {
return (
<View>
<Text>Loading...</Text>
</View>
);
}
return (
<View style={styles.container} >
<View style={styles.searchBar}>
<Searchbar
round
placeholder="Search"
onChangeText={text => this.SearchFilterFunction(text)}
onClear={text => this.SearchFilterFunction('')}
value={this.state.search}
/>
</View>
<View style={styles.merchantsList}>
<FlatList
data={this.state.merchantObj}
renderItem={this.renderItem}
ItemSeparatorComponent={this.FlatListItemSeparator}
keyExtractor={item => item.id.toString()}
extraData={this.state}
/>
</View>
<View style={styles.footerBtn}>
<TouchableOpacity onPress={() => this.props.navigation.navigate('Main', { itemId: undefined })}>
<View style={styles.btnSelect}>
<Text style={styles.txtBtn}>Cancel</Text>
</View>
</TouchableOpacity>
{this.state.btnDisabled === true ? this.MerchSelected('Sim') : this.MerchSelected('Nao')}
</View>
</View >
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f2f2f4',
alignItems: 'center',
},
searchBar: {
flex: 1,
top: '5%',
width: '90%',
backgroundColor: 'rgba(242, 242, 244,0.5)'
},
merchantsList: {
flex: 6,
width: '95%',
},
footerBtn: {
flex: 1,
width: '100%',
},
listItem: {
flexDirection: 'row',
marginTop: 5,
},
SelectedlistItem: {
flexDirection: 'row',
marginTop: 5,
backgroundColor:"grey",
},
btnSelect: {
justifyContent: 'center',
width: '95%',
borderRadius: 5,
borderColor: '#00CED1',
borderStyle: 'solid',
borderWidth: 2,
height: 40,
marginTop: 5,
marginLeft: 8,
},
btnDsb: {
justifyContent: 'center',
width: '95%',
borderRadius: 5,
backgroundColor: 'gray',
height: 40,
marginTop: 5,
marginLeft: 8,
},
txtBtn: {
textAlign: 'center',
color: '#00CED1',
fontSize: 20,
},
})
above is code that's you want and the screenshot is here
Since you keep track of the selected itemId, you can simply override the style of selected item as below.
<TouchableOpacity onPress={() => this.PressedItem(item.id, item.image)} >
{/* Suppeose you want to change the background color of selected item as 'red' */}
<View style={item.id !== this.state.itemId ? styles.listItem : [styles.listItem, { backgroundColor: 'red' }]}>
...
</View>
</TouchableOpacity>
But you need to add extraData property in FlatList for telling the list to re-render.
extraData={this.state.itemId}
Hope this helps you. Feel free for doubts.
I am kind of beginner in React world. In my project, i have a different number of custom accordion objects that has flatlist of text inputs. How can i handle such a input system with single button click from parent of accordion objects. I want to have one button that collect all of the current inputs and then end it on a server or any other relevant pages. (I am using functional layout with state hooks.)
Thank you for any response,
Bests
You can see the layout here !
You may see accordion.js below:
import React, { Component, useState, useEffect} from 'react';
import { View, TouchableOpacity, FlatList, StyleSheet, TextInput } from "react-native";
//import { Colors } from './Colors';
import { theme } from "../constants";
import Text from "./Text";
import Icon from "react-native-vector-icons/MaterialIcons";
import { ScrollView } from 'react-native-gesture-handler';
const Colors = {
PRIMARY: '#1abc9c',
WHITE: '#ffffff',
LIGHTGREEN: '#BABABA',
GREEN: '#0da935',
GRAY: '#f7f7f7',
LIGHTGRAY: '#C7C7C7',
DARKGRAY: '#5E5E5E',
CGRAY: '#ececec',
OFFLINE_GRAY: '#535353'
}
export default function Accordion (props) {
const [data, setData] = useState(props.data)
const [expanded, setExpanded] = useState(false)
const onClick = (index) => {
const temp = data.slice()
temp[index].value = !temp[index].value
console.log(temp)
setData(temp)
}
const toggleExpand = (section) => {
//this.setState({ expanded: !this.state.expanded )
setExpanded(prev_state => !prev_state)
props.fromparentonClick(expanded)
}
useEffect(() => {
console.log('will unmount')
}, [expanded]);
return (
<View>
<TouchableOpacity
style={styles.row}
onPress={() => toggleExpand()}
>
<Text style={[styles.title, styles.font]}>
{props.title}
</Text>
<Icon
name={
expanded
? "keyboard-arrow-up" //this is condinational ternary operator rendering :)
: "keyboard-arrow-down"
}
size={30}
color={Colors.DARKGRAY}
/>
</TouchableOpacity>
<View style={styles.parentHr} />
{ expanded && ( //this is short circuit operator
<View style={{}}>
<FlatList
data={data}
numColumns={1}
scrollEnabled={true}
renderItem={({ item, index }) => (
<View styles={styles.deneme}>
<Text style={[styles.font, styles.itemInActive]}>
{item.key}
</Text>
<TouchableOpacity
style={[
styles.childRow,
styles.button,
item.value ? styles.btnInActive : styles.btnActive
]}
onPress={() => onClick(index)}
>
<Icon
name={"check-circle"}
size={24}
color={item.value ? Colors.LIGHTGRAY : Colors.GREEN}
/>
{/* <Text style={[styles.font, styles.itemInActive]}>
{item.key}
</Text>*/}
<TextInput
style={
styles.text_input
}
blurOnSubmit
placeholder="input1"
placeholderTextColor="#60605e"
numeric
keyboardType={"numeric"}
maxLength={3}
/>
<TextInput
style={
styles.text_input
}
blurOnSubmit
placeholder="input2"
placeholderTextColor="#60605e"
numeric
keyboardType={"numeric"}
maxLength={3}
/>
<TextInput
style={
styles.text_input
}
blurOnSubmit
placeholder="input3"
placeholderTextColor="#60605e"
numeric
keyboardType={"numeric"}
maxLength={3}
/>
</TouchableOpacity>
<View style={styles.childHr} />
</View>
)}
/>
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center'
},
font: {
// fontFamily: Fonts.bold,
},
button: {
width: '100%',
height: 54,
alignItems: 'center',
paddingLeft: 35,
paddingRight: 35,
fontSize: 12,
},
title: {
fontSize: 14,
fontWeight: 'bold',
color: Colors.DARKGRAY,
},
itemActive: {
fontSize: 12,
color: Colors.GREEN,
},
itemInActive: {
fontSize: 12,
color: Colors.DARKGRAY,
},
btnActive: {
borderColor: Colors.GREEN,
},
btnInActive: {
borderColor: Colors.DARKGRAY,
},
row: {
flexDirection: 'row',
justifyContent: 'space-between',
height: 56,
paddingLeft: 25,
paddingRight: 18,
alignItems: 'center',
backgroundColor: Colors.CGRAY,
},
childRow: {
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: Colors.GRAY,
},
parentHr: {
height: 1,
color: Colors.WHITE,
width: '100%'
},
childHr: {
height: 1,
backgroundColor: Colors.LIGHTGRAY,
width: '100%',
},
colorActive: {
borderColor: Colors.GREEN,
},
colorInActive: {
borderColor: Colors.DARKGRAY,
},
text_input: {
width: 80,
backgroundColor: "#dde8c9",
padding: 10,
textAlign: 'center'
},
deneme: {
flexDirection: 'column',
textAlign: 'center',
justifyContent: 'center',
}
});
You may see parent component below:
import * as WebBrowser from 'expo-web-browser';
import React, { Component,useState } from 'react';
import {
Image,
Platform,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import { Button, Block, Input,Accordion ,Header} from "../components";
import { theme } from "../constants";
//import {CATEGORIES} from "../Data/dersler";
import SwitchSelector from "react-native-switch-selector";
import { MonoText } from '../components/StyledText';
import 'core-js/es6/symbol'; import 'core-js/fn/symbol/iterator';
export default function HomeScreen (props) {
const options = [
{ label: "x", value: "1" },
{ label: "y", value: "2" }
]
const initial_state = {
courses: [
{
key: "c1",
title: "ss",
data: [
{ key: "dd", value: "false" },
{ key: "ff", value: "false" },
{ key: "gg ", value: "false" }
]
},
{
key: "c2",
title: "ss2",
data: [
{ key: "dd", value: "false" },
{ key: "ff", value: "false" },
{ key: "gg", value: "false" },
{ key: "cc", value: "false" }
]
},
],
}
const second_state = {
courses: [
{
key: "c1",
title: "dd",
data: [
{ key: "cc", value: "false" },
{ key: "dd", value: "false" },
{ key: "ff ", value: "false" }
]
},
]
}
const [exam, setExam] = useState(initial_state)
const [onlineAcc, setonlineAcc] = useState(false)
const [activeSession, setactiveSession] = useState(0)
const controlAccordions = (arg) => {
setonlineAcc(prev_state => !prev_state)
//console.log(onlineAcc)
console.log(arg)
if(arg){
setactiveSession(prev_state => prev_state -1 )
}
else {
setactiveSession(prev_state => prev_state + 1)
}
console.log(activeSession)
}
const renderAccordians = () => {
let items = [];
//console.log(`Call onPress with value: ${ this.state}`);
//console.log(exam.courses);
return exam.courses.map(ex => (<Accordion active={activeSession} fromparentonClick={controlAccordions} title={ex.title} data={ex.data} key={ex.key} /> ))
//return items;
}
return (
<View style={styles.container}>
<Header title="Anasayfa" />
<SwitchSelector
options={options}
initial={1}
buttonColor={theme.colors.gray2}
onPress={value => {
if( value== 1)
{setExam (second_state)}
else {
setExam(initial_state)
}
console.log(value)
}}
/>
<ScrollView style={styles.container}>
{renderAccordians()}
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
developmentModeText: {
marginBottom: 20,
color: 'rgba(0,0,0,0.4)',
fontSize: 14,
lineHeight: 19,
textAlign: 'center',
},
contentContainer: {
paddingTop: 30,
},
welcomeContainer: {
alignItems: 'center',
marginTop: 10,
marginBottom: 20,
},
welcomeImage: {
width: 100,
height: 80,
resizeMode: 'contain',
marginTop: 3,
marginLeft: -10,
},
getStartedContainer: {
alignItems: 'center',
marginHorizontal: 50,
},
homeScreenFilename: {
marginVertical: 7,
},
codeHighlightText: {
color: 'rgba(96,100,109, 0.8)',
},
codeHighlightContainer: {
backgroundColor: 'rgba(0,0,0,0.05)',
borderRadius: 3,
paddingHorizontal: 4,
},
getStartedText: {
fontSize: 17,
color: 'rgba(96,100,109, 1)',
lineHeight: 24,
textAlign: 'center',
},
tabBarInfoContainer: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
...Platform.select({
ios: {
shadowColor: 'black',
shadowOffset: { width: 0, height: -3 },
shadowOpacity: 0.1,
shadowRadius: 3,
},
android: {
elevation: 20,
},
}),
alignItems: 'center',
backgroundColor: '#fbfbfb',
paddingVertical: 20,
},
tabBarInfoText: {
fontSize: 17,
color: 'rgba(96,100,109, 1)',
textAlign: 'center',
},
navigationFilename: {
marginTop: 5,
},
helpContainer: {
marginTop: 15,
alignItems: 'center',
},
helpLink: {
paddingVertical: 15,
},
helpLinkText: {
fontSize: 14,
color: '#2e78b7',
},
});
you will have to keep track of the data of the accordians in the parent class
let accordionData = [];
const addAccordionData = data => {
accordionData.push(data);
}
const renderAccordians = () => {
let items = [];
//console.log(`Call onPress with value: ${ this.state}`);
//console.log(exam.courses);
return exam
.courses.map(ex => (
<Accordion
active={activeSession}
fromparentonClick= {controlAccordions}
title={ex.title}
data={ex.data}
onAccordianDataSet={addAccordianData} // add this line <====
key={ex.key} /> ))
//return items;
}
in the accordion component do this
// in the **accordion ** component do this
const onClick = (index) => {
const temp = data.slice()
temp[index].value = !temp[index].value
console.log(temp)
setData(temp);
// add this line
props.onAccordianDataSet(temp); // now the answer will be in the parents
}
and you can have a button in the parent that calls a function like this
const submitDataToDatabase = () => {
if(accordionData.length === 0) {
alert("answer every question");
return;
}
submit data to database storage
}
As the react-pdf library offering some limited component and no html tag is allowed to render in reactpdfrenderer.So i am in a trouble to make table using this library?
Can any one please help me how can i create a table using this react-pdf library components?
You can use as #David-Kucsai told in comment #david.kucsai/react-pdf-table
or without using
Example
Data
const data = {
id: "5df3180a09ea16dc4b95f910",
items: [
{
sr: 1,
desc: "desc1",
xyz: 5,
},
{
sr: 2,
desc: "desc2",
xyz: 6,
},
],
};
app.js
import React, { Component, Fragment } from "react";
import { PDFViewer } from "#react-pdf/renderer";
import Table from "./components/reports/Table";
import data from "./data";
class App extends Component {
render() {
return (
<Fragment>
<PDFViewer width="1000" height="600">
<Table data={data} />
</PDFViewer>
</Fragment>
);
}
}
export default App;
Table.js
import React from "react";
import { Page, Document, StyleSheet } from "#react-pdf/renderer";
import ItemsTable from "./ItemsTable";
const styles = StyleSheet.create({
page: {
fontSize: 11,
flexDirection: "column",
},
});
const Table = ({ data }) => (
<Document>
<Page size="A4" style={styles.page}>
// ...
<ItemsTable data={data} />
// ...
</Page>
</Document>
);
export default Table;
ItemsTable.js
import React from "react";
import { View, StyleSheet } from "#react-pdf/renderer";
import TableRow from "./TableRow";
const styles = StyleSheet.create({
tableContainer: {
flexDirection: "row",
flexWrap: "wrap",
},
});
const ItemsTable = ({ data }) => (
<View style={styles.tableContainer}>
{/*<TableHeader />*/}
<TableRow items={data.items} />
{/*<TableFooter items={data.items} />*/}
</View>
);
export default ItemsTable;
TableRow.js
import React, { Fragment } from "react";
import { Text, View, StyleSheet } from "#react-pdf/renderer";
const styles = StyleSheet.create({
row: {
flexDirection: "row",
alignItems: "center",
},
description: {
width: "60%",
},
xyz: {
width: "40%",
},
});
const TableRow = ({ items }) => {
const rows = items.map((item) => (
<View style={styles.row} key={item.sr.toString()}>
<Text style={styles.description}>{item.desc}</Text>
<Text style={styles.xyz}>{item.xyz}</Text>
</View>
));
return <Fragment>{rows}</Fragment>;
};
export default TableRow;
For more information check Generate Dynamic PDF Invoices Using React and React-PDF
Thanks goes to Yash for the detailed answer given. This is what I ended up creating after seeing your example.
The main trick to create a "table" is to use fixed width columns on each row.
Note: Make the parent container width: '100%' (table in this case) if you want to have your rows add up beyond 100% without overflowing/growing the parent. I would still recommend you try to have your total width add to 100% though, but the example below shows otherwise.
import { StyleSheet, Text, View } from '#react-pdf/renderer'
import PropTypes from 'prop-types'
const styles = StyleSheet.create({
table: {
width: '100%',
},
row: {
display: 'flex',
flexDirection: 'row',
borderTop: '1px solid #EEE',
paddingTop: 8,
paddingBottom: 8,
},
header: {
borderTop: 'none',
},
bold: {
fontWeight: 'bold',
},
// So Declarative and unDRY 👌
row1: {
width: '27%',
},
row2: {
width: '15%',
},
row3: {
width: '15%',
},
row4: {
width: '20%',
},
row5: {
width: '27%',
},
})
const ReportTable = ({ data, maximumDays }) => {
return (
<View style={styles.table}>
<View style={[styles.row, styles.bold, styles.header]}>
<Text style={styles.row1}>Name</Text>
<Text style={styles.row2}>Start Date</Text>
<Text style={styles.row3}>End Date</Text>
<Text style={styles.row4}>Days</Text>
<Text style={styles.row5}>Info</Text>
</View>
{data.map((row, i) => (
<View key={i} style={styles.row} wrap={false}>
<Text style={styles.row1}>
<Text style={styles.bold}>{row.lastName}</Text>, {row.firstName}
</Text>
<Text style={styles.row2}>{row.startDate}</Text>
<Text style={styles.row3}>{row.endDate}</Text>
<Text style={styles.row4}>
<Text style={styles.bold}>{row.days}</Text> of{' '}
{maximumDays}
</Text>
<Text style={styles.row5}>{row.info}</Text>
</View>
))}
</View>
)
}
ReportTable.propTypes = {
data: PropTypes.array.isRequired,
maximumDays: PropTypes.number.isRequired,
}
export default ReportTable
simple table answer here:
showcase
Code:
/* eslint-disable react-hooks/exhaustive-deps */
import React, { Fragment, useEffect, useState } from "react";
import { View, StyleSheet, Text } from "#react-pdf/renderer";
export default function Table({ data }) {
const [tableData, setTableData] = useState();
const styles = StyleSheet.create({
rowView: {
display: 'flex', flexDirection: 'row', borderTop: '1px solid #EEE', paddingTop: 8, paddingBottom: 8, textAlign: "center"
}
});
useEffect(() => {
if (data !== undefined) setTableData(data);
}, []);
return (
<>
{tableData &&
(
<Fragment>
<View style={styles.rowView}>
{tableData["column"].map((c) => <Text style={{
width: `${100 / tableData["column"].length}%`
}}>{c}</Text>)}
</View>
{tableData["data"].map((rowData) => <>
<View style={styles.rowView}>
{tableData["column"].map((c) =>
<Text style={{ width: `${100 / tableData["column"].length}%` }}>{rowData[c]}</Text>
)}
</View>
</>)}
</Fragment>
)}
</>
)
}
data format:
{
"column": [
"price",
"email",
"time"
],
"data": [
{
"price": "",
"email": "",
"time": ""
},
{
"price": "",
"email": "",
"time": ""
}
]
}
the table auto-populates based on the number of columns and auto sizes to take full page. just make sure to use column names as data keys
function checkStrEmpty(str) {
return !(str && str.length > 1 && str.split(" ").join("").length > 0);
}
import { Text, View, StyleSheet } from "#react-pdf/renderer";
import React from "react";
function CustomTablePDF(props) {
const { fields = [], data = [] } = props;
let tableCol = {
borderStyle: BORDER_STYLE,
borderColor: BORDER_COLOR,
borderBottomColor: "#000",
borderWidth: 1,
borderLeftWidth: 0,
borderTopWidth: 0
};
return (
<View style={styles.table}>
<View style={[styles.tableRow, styles.headerBg]}>
{fields.map((_item, _idx) => (
<View
key={_idx}
style={[tableCol, { width: _item.width + "%" }]}
>
<Text
style={[
styles.tableCellHeader,
{ textAlign: "center" }
]}
>
{_item.title}
</Text>
</View>
))}
</View>
{data.map(
(item, idx) =>
item && (
<View key={idx} style={styles.tableRow}>
{fields.map((_item, _idx) => {
let val = item[_item.value] || "";
let value_alt =
(_item.value_alt &&
item[_item.value_alt]) ||
"";
if (_item.custom) {
return (
<View
key={_idx}
style={[
tableCol,
{ width: _item.width + "%" }
]}
>
<Text
style={[
styles.tableCell,
item.style ? item.style : {}
]}
>
{_item.component(item)}
</Text>
</View>
);
} else {
return (
<View
style={[
styles.tableCol,
{ width: _item.width + "%" }
]}
>
<Text
style={[
styles.tableCell,
item.style ? item.style : {}
]}
>
{checkStrEmpty(val)
? value_alt
: val || "-"}
</Text>
</View>
);
}
})}
</View>
)
)}
</View>
);
}
const BORDER_COLOR = "#000";
const BORDER_STYLE = "solid";
const styles = StyleSheet.create({
headerBg: {
backgroundColor: "#aaa"
},
table: {
display: "table",
width: "auto",
borderStyle: BORDER_STYLE,
borderColor: BORDER_COLOR,
borderWidth: 1
// borderRightWidth: 0,
// borderBottomWidth: 0,
},
tableRow: {
margin: "auto",
flexDirection: "row"
},
tableCellHeader: {
margin: 2,
fontSize: 13,
fontWeight: "bold"
// fontFamily: "CustomRoboto",
},
tableCell: {
margin: 2,
fontSize: 12
// fontFamily: "CustomRoboto",
},
textCenter: {
textAlign: "center"
}
});
export default CustomTablePDF;
use the component like this
const fields = [
{
title: " Agent Name",
custom: true,
component: (item) => `${item.agent_name}`,
width: "30"
},
{
title: " Policy No",
custom: true,
component: (item) => `${item.policy_no}`,
width: "35"
},
{
title: "Policy Class",
value: 'policy_class',
width: "20"
},
{
title: "Amount",
custom: true,
style: { textAlign: "right" },
className: "text-right",
component: (item) =>`${formatNumber(item.contribution)}`,
width: "15"
}
];
<CustomTablePDF fields={fields} data={details} />