React Native: building a layout from data in an array - arrays

So I have a layout on screen which duplicates itself multiple times. I really want to clean it up. Currently it's like this:
import { content } from '../content.js';
class Display extends Component {
render() {
return (
<View style={styles.displayContainer}>
<ScrollView>
<View style={styles.row}>
<View style={styles.displayItemBlock}>
<TouchableOpacity style={styles.displayItem} onPress={this.item1}>
<Image
style={styles.displayItemImage}
source={require('../images/display-item1.jpg')}
/>
<View style={styles.displayItemText}>
<Text style={styles.displayItemTitle}>{content.display_title_item1}</Text>
<Text style={styles.recipeItemTime}>
<IconMCI name="clock" color="#4F8EF7" /> 2h 30m
</Text>
</View>
</TouchableOpacity>
</View>
<View style={styles.displayItemBlock}>
<TouchableOpacity style={styles.displayItem} onPress={this.item2}>
<Image
style={styles.displayItemImage}
source={require('../images/display-item2.jpg')}
/>
<View style={styles.displayItemText}>
<Text style={styles.displayItemTitle}>{content.display_title_item2}</Text>
<Text style={styles.recipeItemTime}>
<IconMCI name="clock" color="#4F8EF7" /> 45m
</Text>
</View>
</TouchableOpacity>
</View>
{/* AND REPEAT... */}
</View>
</ScrollView>
</View>
);
}
}
So I thought I could easily replace most of this with a map which will take the information which changes and put that into an array. So now I have this:
import { content } from '../content.js';
const dataArray = [
{ img: '../images/display-item1.jpg',
title: 'content.display_title_item1',
func: 'item1',
time: '2h 30m' },
{ img: '../images/display-item2.jpg',
title: 'content.display_title_item2',
func: 'item2',
time: '45m' },
];
class Display extends Component {
ShowEverything() {
return dataArray.map(function (data, i) {
return (
<View key={i} style={styles.displayItemBlock}>
<TouchableOpacity style={styles.displayItem} onPress={this.{data.func}}>
<Image
style={styles.displayItemImage}
source={require({data.img)}
/>
<View style={styles.displayItemText}>
<Text style={styles.displaytemTitle}>{data.title}</Text>
<Text style={styles.displayItemTime}>
<IconMCI name="clock" color="#4F8EF7" /> {data.time}
</Text>
</View>
</TouchableOpacity>
</View>
);
});
}
render() {
return (
<View style={styles.displayContainer}>
<ScrollView>
<View style={styles.row}>
{this.ShowEverything()}
</View>
</Scroll>
</View>
);
}
}
What could be easier, right? :)
This, of course, doesn't work. The time (data.time) is output correctly, but the title doesn't pull the correct text from content.js (it just outputs the string content.display_title_item from the array). The img and func items also don't work as I would have expected.
Am I way off here? It seems like this is the most obvious way of keeping this DRY; any help would be appreciated.

Try this
import { content } from '../content.js';
const dataArray = [
{ img: require('../images/display-item1.jpg'), //this is a known issue in react, dynamic paths needs to assigned like this
title: 'display_title_item1',
func: 'item1',
time: '2h 30m' },
{ img: require('../images/display-item2.jpg'),
title: 'display_title_item2',
func: 'item2',
time: '45m' },
];
class Display extends Component {
ShowEverything() {
return dataArray.map((data, i) => { // changed to fat arrow func to be in scope of this
return (
<View key={i} style={styles.displayItemBlock}>
<TouchableOpacity style={styles.displayItem} onPress={this[data.func]}> // syntax error
<Image
style={styles.displayItemImage}
source={data.img}
/>
<View style={styles.displayItemText}>
<Text style={styles.displaytemTitle}>content[data.title]</Text> // syntax error
<Text style={styles.displayItemTime}>
<IconMCI name="clock" color="#4F8EF7" /> {data.time}
</Text>
</View>
</TouchableOpacity>
</View>
);
});
}

Related

Show button when 2 inputs are full and valid in react native

****I have a react native page app with 2 inputs. ****
I want to show the "submit" button once all of the 2 fields have a value.
(button is invisible until the 2 input fields have value)
this is my page code :
class Page3 extends Component {
render() {
const {navigate} = this.props.navigation;
return (
<View style={styles.container}>
<View style={styles.question1Stack}>
<Text style={styles.question1}>Question1</Text>
<ImageBackground
source={require("./assets/undraw_Friends_online_re_r7pq.png")}
resizeMode="contain"
style={styles.image}
imageStyle={styles.image_imageStyle}
>
<TouchableOpacity onPress={() =>{this.props.navigation.navigate('Page2')}}>
<Icon name="chevron-left" style={styles.icon1}></Icon>
</TouchableOpacity>
</ImageBackground>
</View>
<TouchableOpacity
title="submit"
onPress={() => {
this.props.navigation.navigate('Page4');
}}>
<Text style={styles.next}>
Next
</Text>
</TouchableOpacity>
<MaterialHelperTextBox
style={styles.materialHelperTextBox}
></MaterialHelperTextBox>
<MaterialHelperTextBox1
style={styles.materialHelperTextBox1}
></MaterialHelperTextBox1>
</View>
);
}
}
these are my input fields :
<MaterialHelperTextBox
style={styles.materialHelperTextBox}
></MaterialHelperTextBox>
<MaterialHelperTextBox1
style={styles.materialHelperTextBox1}
></MaterialHelperTextBox1>
you most define to two state for two TextInput , than set TextInput value in onChangeText check value is not null and valid , set true states for check, I don't know you use MaterialHelperTextBox1 by witch component but code maybe like this:
class Page3 extends Component {
constructor(props) {
super(props);
this.state = {
checkInput1:false,
checkInput2:false,
inputValue1:'',
inputValue2:'',
};
};
render() {
const {navigate} = this.props.navigation;
return (
<View style={styles.container}>
<View style={styles.question1Stack}>
<Text style={styles.question1}>Question1</Text>
<ImageBackground
source={require("./assets/undraw_Friends_online_re_r7pq.png")}
resizeMode="contain"
style={styles.image}
imageStyle={styles.image_imageStyle}
>
{checkInput1 && checkInput2 ?
<TouchableOpacity onPress={() =>
{this.props.navigation.navigate('Page2')}}>
<Icon name="chevron-left" style={styles.icon1}></Icon>
</TouchableOpacity>
:
null
}
</ImageBackground>
</View>
<TouchableOpacity
title="submit"
onPress={() => {
this.props.navigation.navigate('Page4');
}}>
<Text style={styles.next}>
Next
</Text>
</TouchableOpacity>
<MaterialHelperTextBox
style={styles.materialHelperTextBox}
onChangeText={value => {this.setstate({inputValue1:value});
_checkInputIsValid1();}}
defaultValue={this.state.inputValue1}
></MaterialHelperTextBox>
<MaterialHelperTextBox1
style={styles.materialHelperTextBox1}
onChangeText={value => {this.setstate({inputValue2:value});
_checkInputIsValid2();}}
defaultValue={this.state.inputValue1}
></MaterialHelperTextBox1>
</View>
);
}
}
_checkInputIsValid1(){
//check your rules for input1 there if true set state checkInput1 as true
}
_checkInputIsValid2(){
//check your rules for input2 there if true set state checkInput2 as true
}

React Native class component accordion into functional component

The following code displays an accordion but I need to make use of useContext which I understand only works with functional components and I'm having a hard time understand how to convert it into a functional component.
class AnnualFeeScreen extends Component {
state = {
activeSections: [],
};
_renderHeader(section, index, isActive) {
let meanValStatus = '#39D0B5';
if (section.meanDiffVal >= 1.05) {
meanValStatus = '#FCD561';
} else if (section.meanDiffVal < 0.95) {
meanValStatus = '#bbb';
}
return (
<View style={styles.flexRow}>
<View style={{flex:2}}>
<Text style={styles.h6}>{!isActive ? '+' : '-'} {section.title}</Text>
</View>
<View style={styles.flex}>
<Text style={[styles.h6, {textAlign: 'right'}]}>{Math.round((section.titleValue) / 12, 0)} kr/mån</Text>
</View>
<View style={[styles.circleStatus, {backgroundColor: meanValStatus}]}></View>
</View>
);
};
_renderContent = section => {
return (
<View>
{section.content.map((item, key) =>
<View style={styles.accordionContent} key={key}>
<View style={styles.flexRow}>
<View style={{flex: 2}}>
<Text style={[styles.p, {fontWeight: 'normal'}]}>{item.title}</Text>
</View>
<View style={styles.flex}>
<Text style={[styles.p, {fontWeight: 'normal', textAlign: 'right'}]}>{Math.round((item.value * aptPercentage) / 12, 0)} kr/mån</Text>
</View>
</View>
</View>
)}
</View>
);
};
_updateSections = activeSections => {
this.setState({ activeSections });
};
render () {
return (
<ScrollView style={styles.backgroundPrimary} showsVerticalScrollIndicator='false'>
<View style={styles.backgroundSecondary}>
<View style={styles.container}>
<Accordion
sections={brfCostRows}
activeSections={this.state.activeSections}
renderHeader={this._renderHeader}
renderContent={this._renderContent}
onChange={this._updateSections}
sectionContainerStyle={styles.accordion}
underlayColor='transparent'
/>
</View>
</View>
</ScrollView>
);
};
};
To fetch data with axios I use the following in other screens but it's not working with the class component:
const { state, fetchUser } = useContext(UserContext);
And in the view:
<NavigationEvents onWillFocus={fetchUser} />
I've tried to remove the render part change the first part to the following but it's complaining about the first function _renderHeader (Unexpected token):
const AnnualFeeScreen = () => {
First of all while converting, class component to functional component with hooks, you must note that, there won't be a this variable inside functional component. Secondly the state inside a functional component would need to be implemented using useState hook
const AnnualFeeScreen () => {
const [activeSections, setActiveSections] = useState([]);
const _renderHeader(section, index, isActive) {
let meanValStatus = '#39D0B5';
if (section.meanDiffVal >= 1.05) {
meanValStatus = '#FCD561';
} else if (section.meanDiffVal < 0.95) {
meanValStatus = '#bbb';
}
return (
<View style={styles.flexRow}>
<View style={{flex:2}}>
<Text style={styles.h6}>{!isActive ? '+' : '-'} {section.title}</Text>
</View>
<View style={styles.flex}>
<Text style={[styles.h6, {textAlign: 'right'}]}>{Math.round((section.titleValue) / 12, 0)} kr/mån</Text>
</View>
<View style={[styles.circleStatus, {backgroundColor: meanValStatus}]}></View>
</View>
);
};
const _renderContent = section => {
return (
<View>
{section.content.map((item, key) =>
<View style={styles.accordionContent} key={key}>
<View style={styles.flexRow}>
<View style={{flex: 2}}>
<Text style={[styles.p, {fontWeight: 'normal'}]}>{item.title}</Text>
</View>
<View style={styles.flex}>
<Text style={[styles.p, {fontWeight: 'normal', textAlign: 'right'}]}>{Math.round((item.value * aptPercentage) / 12, 0)} kr/mån</Text>
</View>
</View>
</View>
)}
</View>
);
};
const _updateSections = activeSections => {
setActiveSections(activeSections);
};
return (
<ScrollView style={styles.backgroundPrimary} showsVerticalScrollIndicator='false'>
<View style={styles.backgroundSecondary}>
<View style={styles.container}>
<Accordion
sections={brfCostRows}
activeSections={activeSections}
renderHeader={_renderHeader}
renderContent={_renderContent}
onChange={_updateSections}
sectionContainerStyle={styles.accordion}
underlayColor='transparent'
/>
</View>
</View>
</ScrollView>
);
};

How to pass Parent Flatlist's Index value to Child Flatlist's onPress function?

How to pass parent Flatlist's index value to child Flatlist's function so I can use it in function and change value by setState.
So how to pass parent Flatlist's index value to child Flatlist's function so I can use it in function and change value by setState.
It starts with a list that built-in state
constructor(props) {
super(props);
this.state = {
list: [
{
question: 'il veicolo ha la gomma forata?',
answers: [
{
radioButtonValue: false,
text: strings.CONFIRMVIEW_yes,
},
{
radioButtonValue: true,
text: strings.CONFIRMVIEW_no,
},
],
},
]
}
}
checkBoxSelection = (item, index) => {
// if (item.radioButtonValue == index) {
// this.list.answers[index].radioButtonValue = true
// }
console.log(this.state.list[0].answers[index].radioButtonValue)
}
_renderItem = ({ item, index }) => {
return (
<View style={styles.cellView}>
<Text style={styles.questionText}>
{item.question.toUpperCase()}
</Text>
<FlatList
data={item.answers}
horizontal={true}
showsHorizontalScrollIndicator={true}
scrollEnabled={true}
bounces={false}
renderItem={this._renderItemOptions}
/>
{/* <View style={styles.yesNoRadioButtonView}>
<View style={styles.yesChekboxView}>
<TouchableOpacity onPress={ () => {console.log("TEST1"); this.checkBoxSelection()} }>
<Image source={(item.answers == index) ? AppImages.radioOn : AppImages.radioOff} style={styles.radioButton} />
</TouchableOpacity>
<Text style={styles.yesNoText}>
{strings.CONFIRMVIEW_yes}
</Text>
</View>
<View style={styles.noRadioButtonView}>
<TouchableOpacity onPress={() => { this.checkBoxSelection.bind(this) }}>
<Image source={(item.answers == 0) ? AppImages.radioOn : AppImages.radioOff} style={styles.radioButton} />
</TouchableOpacity>
<Text style={styles.yesNoText}>
{strings.CONFIRMVIEW_no}
</Text>
</View>
</View> */}
</View>
)
}
_renderItemOptions = ({ item, index }) => {
return (
<View>
<View style={styles.yesNoRadioButtonView}>
<TouchableOpacity onPress={() => { this.checkBoxSelection(item, index) }}>
<View style={styles.yesChekboxView}>
<Image
source={item.radioButtonValue ? AppImages.radioOn : AppImages.radioOff}
style={styles.radioButton}
/>
<Text style={styles.yesNoText}>
{item.text}
</Text>
</View>
</TouchableOpacity>
</View>
</View>
)
}
render() {
return (
<Container>
<Content style={styles.container}>
<FlatList
data={this.state.list}
showsVerticalScrollIndicator={false}
scrollEnabled={false}
bounces={false}
renderItem={this._renderItem}
/>
</Content>
<SafeAreaView>
<TouchableOpacity
style={[LayoutStyle.appBtn1]}
>
<Text style={[LayoutStyle.appBtnText1]}>
{strings.DAMAGE_SURVEY_comeOn.toUpperCase()}
</Text>
</TouchableOpacity>
</SafeAreaView>
</Container>
)
}
In Parent Flatlist's renderItem function
replace
renderItem = { () => {this.renderItemOptions(item, index)}}
with
renderItem={(childData) => this._renderItemOptions(childData, index)}
and then in child Flatlist's renderItemOption function need to get parentIndex with another name.
_renderItemOptions = ({ item, index }, parentIndex) => {
console.log("hello")
return (
<View>
<View style={styles.yesNoRadioButtonView}>
<TouchableOpacity onPress={() => { this.checkBoxSelection(item, index, parentIndex) }}>
<View style={styles.yesChekboxView}>
<Image
source={item.radioButtonValue ? AppImages.radioOn : AppImages.radioOff}
style={styles.radioButton}
/>
<Text style={styles.yesNoText}>
{item.text}
</Text>
</View>
</TouchableOpacity>
</View>
</View>
)
}
Try updating your renderItem prop in your FlatList to explicitly pass the index
renderItem={({item, index}) => this._renderItemOptions(item, index)}

React Native child component not rendering on onPress event

I am trying to render the child component on onPressevent, the console.log works fine but components in the return function doesn't works.
Parent component:
onPress = (img,title,content) => {
this.setState({
show:true,
img:img,
title:title,
content:content
})
render() {
return (
<View style={styles.container}>
<FlatList
data={this.state.data}
renderItem={({item}) => (
<TouchableOpacity
onPress={() => this.onPress(item.urlToImage,item.title,item.content)}
>
<View style={styles.picsCont}>
<Image style={styles.pics} source={{uri: item.urlToImage}}/>
<Text style={{color:'white', fontSize: 15, fontWeight: '700', paddingTop:10}}>
{item.title}
</Text>
</View>
</TouchableOpacity>
)}
keyExtractor={item => item.title}
/>
{this.state.show ?
<NewsView
img={this.state.img}
title={this.state.title}
content={this.state.content}
/> :
null
}
</View>
);
}
}
Child Component:
export default class NewsView extends Component {
render() {
console.log(this.props.img)
return (
<View style={styles.container}>
<Image style={styles.picsCont} source={{uri:this.props.img}} />
<Text style={styles.news}>{this.props.title}</Text>
<Text style={styles.news}>{this.props.content}</Text>
</View>
)
}
}
Thanks for the help...!
It might be the styles. If your child component has position:'absolute', it´s probably under your parent component, you can try to put on your child component view zIndex:10

react navigation: navigate outside component | map function

Hello I'm new to RN and I have little problem with my react navigation. I would like to write boxes from an array. Every box has same navigation path. I guess it's problem with navigating outside component but I have no idea how to fix it.
CODE:
WriteTeams() {
return teams_array.map(function(Teams, i){
return(
<View key={i}>
<TouchableHighlight onPress={() =>
this.props.navigation.navigate('TeamDetail')}>
<Text>{Teams.TeamName}</Text>
</TouchableHighlight>
</View>
);
});
}
render(){
<ScrollView>
<View>
<View>
{this.WriteTeams()}
</View>
</View>
</ScrollView>
}
}
const teams_array = [
{ TeamName: "Some team"},
{ TeamName: "Some team2"}
]
Error screen
Thanks for every answer.
The error message indicates that the navigation object is not being passed into the component as a property.
You will need to pass the navigation object from the render function to the WriteTeams component to make it available in your code.
I have copied and amended your code to help explain
WriteTeams(navigation) {
return teams_array.map(function(Teams, i){
return(
<View key={i}>
<TouchableHighlight
onPress={() => navigation.navigate('TeamDetail')}
>
<Text>{Teams.TeamName}</Text>
</TouchableHighlight>
</View>
);
});
}
render(){
return (
<ScrollView>
<View>
<View>
{this.WriteTeams(this.props.navigation)}
</View>
</View>
</ScrollView>
)
}
const teams_array = [
{ TeamName: "Some team" },
{ TeamName: "Some team2" }
]

Resources