In one of my components, I check the value of my props before deciding what component to return.
But the issue I am facing in testing is that the snapshot is not getting created properly for these components.
When I am testing a component on its own, the snapshot is created properly but not when my component checks prop value before returning a JSX.
This is my component:
import React, {Component} from 'react'
import {Button, Text, View, FlatList, StyleSheet, ActivityIndicator} from 'react-native'
import CategoryCell from '../Components/CategoryCell'
import { connect } from 'react-redux'
import { fetchQuotes } from '../actions/Quotes'
class QuotesCategories extends Component {
static navigationOptions = {
title: 'Categories',
}
render() {
return this.props.error ? (
<View style={styles.Container}>
<Text style={{color: 'red'}}>FAILED TO LOAD DATA</Text>
<Button
title='Reload'
onPress={this.props.fetchQuotes}
/>
</View>
) : this.props.loading ? (
<View style={styles.Container}>
<ActivityIndicator size="large"/>
</View>
) : (
<View style={styles.Container}>
<FlatList
style= {{flex:1, width: '100%'}}
data= {this.props.data}
renderItem = {({item,index}) => {
return (
<CategoryCell Category={item} navigation={this.props.navigation} id={index}/>
)
}}
keyExtractor = {(item, index) => item.category}
/>
<Text>Additions</Text>
</View>
)
}
}
const styles = StyleSheet.create({
Container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}
})
export const mapStateToProps = (state) => {
return {
loading: state.loading,
error: state.error,
data: state.data
}
}
export const mapDispatchToProps = (dispatch) => {
return {
fetchQuotes: () => {
dispatch(fetchQuotes())
}
}
}
export default connect(mapStateToProps,mapDispatchToProps)(QuotesCategories)
I am trying to test the three cases
When there is some error
When the data is loading
When the data has loaded
I am trying to test the three cases
error and loading is a boolean
data is an array of JSON objects
This is the test for the error case:
import React from 'react'
import {shallow} from 'enzyme'
import QuoteCategories from '../../Components/QuoteCategories'
import quotes from '../fixtures/quotes-fixture'
describe('Testing QuoteCategories component', () => {
it('should load error button when error loading', ( ) => {
const wrapper = shallow(
<QuoteCategories
loading = {false}
error = {true}
data = {undefined}
/>
)
expect(wrapper).toMatchSnapshot()
}
)
}
)
But in the QuoteCategories.test.js.snap file this is the snapshot I see:
exports[`Testing QuoteCategories component should load error button when error loading 1`] = `
<ContextConsumer>
<Component />
</ContextConsumer>
`;
Why am I seeing these tags <ContextConsumer>,<Component /> ?
In my other component test which directly returns a component, the snapshot is displaying properly:
My Component:
import React from 'react'
import { View, Text, TouchableHighlight, StyleSheet } from 'react-native'
const FavouriteQuoteCell = (props) => {
return (
<TouchableHighlight
onPress={() => props.navigation.navigate('Quotes',{id: props.item.parentId, category: props.item.category})}
style={styles.TableCell}
>
<View>
<Text style={styles.Quote}>{props.item.text}</Text>
<Text style={styles.Author}>-- {props.item.person}</Text>
<View style={styles.CategoryPill}>
<Text style={styles.Category}>
{props.item.category}
</Text>
</View>
</View>
</TouchableHighlight>
)
}
export default FavouriteQuoteCell
const styles = StyleSheet.create({
TableCell: {
backgroundColor: '#ff6347',
margin:5,
padding: 5,
justifyContent: 'space-around',
flexDirection: 'column',
flex: 1 ,
padding: 10,
margin: 5,
borderRadius: 15,
},
"Quote": {
fontWeight: 'bold',
color: 'white'
},
"Author": {
fontWeight:'200',
color:'white',
justifyContent: 'flex-end',
alignItems: 'flex-end',
height: 20
},
Category: {
color: '#ff6347',
fontWeight: 'bold',
fontSize: 12,
textTransform: 'capitalize',
margin: 'auto'
},
CategoryPill: {
marginTop: 10,
padding: 2,
height: 20,
borderRadius: 10,
backgroundColor: 'white',
width: 100,
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
})
The test:
import React from 'react'
import {shallow} from 'enzyme'
import FavouriteQuoteCell from '../../Components/FavouriteQuoteCell'
import {favouriteItem} from '../fixtures/favourites-fixture'
describe('testing FavouriteQuoteCell', () => {
let wrapper,navigation
beforeEach(() => {
navigation = {
navigate: jest.fn()
}
wrapper = shallow(<FavouriteQuoteCell navigation={navigation} item={favouriteItem}/>)
})
it('should render FavouriteQuoteCell correctly', () => {
expect(wrapper).toMatchSnapshot()
})
})
The snapshot:
exports[`testing FavouriteQuoteCell should render FavouriteQuoteCell correctly 1`] = `
<TouchableHighlight
activeOpacity={0.85}
delayPressOut={100}
onPress={[Function]}
style={
Object {
"backgroundColor": "#ff6347",
"borderRadius": 15,
"flex": 1,
"flexDirection": "column",
"justifyContent": "space-around",
"margin": 5,
"padding": 10,
}
}
underlayColor="black"
>
<View>
<Text
style={
Object {
"color": "white",
"fontWeight": "bold",
}
}
>
Believe you can and you"re halfway there
</Text>
<Text
style={
Object {
"alignItems": "flex-end",
"color": "white",
"fontWeight": "200",
"height": 20,
"justifyContent": "flex-end",
}
}
>
--
Theodore Roosevelt
</Text>
<View
style={
Object {
"alignItems": "center",
"backgroundColor": "white",
"borderRadius": 10,
"flex": 1,
"height": 20,
"justifyContent": "center",
"marginTop": 10,
"padding": 2,
"width": 100,
}
}
>
<Text
style={
Object {
"color": "#ff6347",
"fontSize": 12,
"fontWeight": "bold",
"margin": "auto",
"textTransform": "capitalize",
}
}
>
inspirational
</Text>
</View>
</View>
</TouchableHighlight>
`;
Your QuotesCategories component is connected to redux with:
export default connect(mapStateToProps,mapDispatchToProps)(QuotesCategories)
that is why when you are shallow rendering you see the redux wrapper component in the snapshot and not your QuotesCategories.
The usual why to fix this to also export your QuotesCategories and import it with its name in your tests:
So your component file should have two exports:
export class QuotesCategories extends Component {
...
}
export default connect(mapStateToProps,mapDispatchToProps)(QuotesCategories)
And in your test you should import QuotesCategories with:
import { QuoteCategories } from '../../Components/QuoteCategories'
Related
I am developing react-native app for quite sometimes. During my development, almost everyday i am facing some error/warning. Among them, the most comment error I've faced is this-> Warning: Can't perform a React state update on an unmounted component. I've searched everywhere, but couldn't find a proper solution. And this log also not explaining that much. So is there anyone who can explain this which will be much more understandable, or point me out where should I dig into to solve this error or what more should I study to understand the situation. Here is the full screenshot of this error.
. And Here is the some code of one of my component:
//packages
import React, { useContext, useEffect, useState } from 'react';
import { ActivityIndicator, ImageBackground, Pressable, StyleSheet, Text, View } from 'react-native';
// third pirty packages
import { Button, HamburgerIcon, Menu, NativeBaseProvider, useToast } from 'native-base';
import Feather from 'react-native-vector-icons/Feather';
import Foundation from "react-native-vector-icons/Foundation";
import NetInfo from "#react-native-community/netinfo";
import {
widthPercentageToDP as wp,
heightPercentageToDP as hp,
listenOrientationChange as lor,
removeOrientationListener as rol
} from 'react-native-responsive-screen';
//assets and components
import { AuthContext } from './../context';
const MainBody = (props) => {
const { signOut } = useContext(AuthContext);
const [isLoading, setIsLoading] = useState(false);
const toast = useToast();
useEffect(() => {
lor();
return () => {
rol();
};
}, []);
const styles = StyleSheet.create({
wrapperView: {
height: hp('87%'),
paddingTop: hp('7%'),
alignItems: 'center',
backgroundColor: '#fff'
},
dashboardView: {
width: '80%',
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: hp('3%')
},
dashboardCategory: {
width: '45%',
height: hp('20%'),
borderRadius: 5,
elevation: 5,
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
backgroundColor: '#FFFFFF'
},
iconStyle: {
color: 'grey',
fontSize: hp('5%'),
alignSelf: 'center'
},
buttonText: {
marginTop: 10,
color: '#4b2e80',
width: '100%',
textAlign: 'center',
fontSize: hp('2.7%')
},
headerButtonView: {
position: 'absolute',
top: hp('3%'),
right: wp('5%')
}
});
return (
<View>
<View style={styles.wrapperView}>
<View style={styles.dashboardView}>
<Button light style={styles.dashboardCategory}>
<Feather style={styles.iconStyle} name="users" />
<Text style={styles.buttonText}> Clip </Text>
</Button>
<Button light style={styles.dashboardCategory}>
<Foundation style={styles.iconStyle} name='pound' />
<Text style={styles.buttonText}> Balancing </Text>
</Button>
</View>
</View>
<View style={styles.headerButtonView}>
<Menu
trigger={(triggerProps) => {
return (
<Pressable accessibilityLabel="More options menu" {...triggerProps}>
<HamburgerIcon color="#fff" />
</Pressable>
)
}}
>
<Menu.Item onPress={() => signOut()}>Logout</Menu.Item>
</Menu>
</View>
</View>
);
}
export const DashboardScreen = ({ navigation }) => {
return (
<NativeBaseProvider>
<MainBody navigate={navigation.navigate} />
</NativeBaseProvider>
);
}
we need to unsubscribe the particular subscription before our components unmounts. it's a workaround but will get rid of the error.
useEffect(() => {
let mounted = true
if(mounted){
lor();
}
return () => {
rol();
mounted= false
};
}, []);
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 want to update initialParams of react native navigation on clicking of the menu item
import React, { useState } from 'react';
import {createStackNavigator} from '#react-navigation/stack'
import { NavigationContainer } from '#react-navigation/native';
import userMain from './../../components/users/userMain';
import { View, Icon } from 'native-base';
export const UserStack = () => {
const Stack = new createStackNavigator()
const [toggleSearch, setToggleSearch] = useState(true)
const changeStyleToggle = () => {
setToggleSearch( !toggleSearch )
// console.log('toggle search here ==== ', toggleSearch)
}
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="User Homepage"
component={userMain}
initialParams={{ toggleSearch: toggleSearch}}
options = {{
title: 'My home',
headerStyle: {
backgroundColor: '#f4511e',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
headerRight: () => (
<View style={{flexDirection:'row', justifyContent:'space-evenly', width:120}}>
<Icon name='home' onPress={() => changeStyleToggle()} />
<Icon name='home' />
<Icon name='home' />
</View>
),
}}
/>
</Stack.Navigator>
</NavigationContainer>
)
}
the component i am calling is userMain where i am calling initialParams value and on behalf of that i want to change style
import React from 'react'
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'
import { Input } from 'native-base';
const userMain = (props) => {
const {toggleSearch} = props.route.params;
console.log(toggleSearch)
const check = toggleSearch == true ? 'true!!' : 'false!!'
return (
<View style={styles.container}>
<Input
placeholder="search"
style={styles.searchInput}
/>
<Text>user homepage here</Text>
<Text>{check}</Text>
<TouchableOpacity style={styles.btn}>
<Text style={styles.btnText}> + </Text>
</TouchableOpacity>
</View>
)
}
const styles = StyleSheet.create({
container: {
alignItems: 'center', flex: 1, justifyContent: 'center',
},
btn: {
position: 'absolute', justifyContent: 'center', alignItems: 'center',
bottom:10, right:10, width:60, height: 60,
backgroundColor: '#fff', borderRadius: 50, borderColor: '#000',borderWidth:1,
},
btnText: {
fontSize: 50, color: 'black',
},
searchInput: {
position:'absolute', top: 0, borderWidth: 1, width: 300, opacity:0
}
})
export default userMain
i have checked on clicking the icon state which is toggleSearch is updating but it is not updating the initialParams hence it is not working on userMain component as well .
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} />
I have to render headerRight conditionally in navigation options.
Right now
static navigationOptions = ({ navigation }) => ({
title: i18N.t('atmbranchpickHeader'),
headerRight: (
<TouchableHighlight
underlayColor="#E22F39"
onPress={() => {
navigation.navigate("home");
}}
>
<Image
style={{ marginRight: 20 }}
source={require('../../resources/toolbar/home_white.png')}
/>
</TouchableHighlight>
),
headerTintColor: "white",
headerStyle: {
backgroundColor: "#E22F39"
// top: 30
}
});
My Component
import React, { Component } from "react";
import {
View,
TextInput,
Text,
TouchableOpacity,
TouchableHighlight,
StyleSheet,
AsyncStorage,
BackHandler,
Image,
FlatList,
Dimensions,
TouchableWithoutFeedback
} from "react-native";
import i18n from "../../i18n/i18n.js";
import { colors } from "../../constants/colors.js";
import Storage from "../../utils/AsyncStorage.js";
class AtmBranchTypeSelect extends Component {
// Render callBack
constructor(props) {
super(props);
this.state = {
data: [
],
stBool: false,
}
}
async componentWillMount() {
BackHandler.addEventListener('hardwareBackPress', () => this.props.navigation.goBack());
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', () => this.props.navigation.goBack());
}
static navigationOptions = ({ navigation }) => ({
title: i18n.t('atmbranchpickHeader'),
headerRight: (
<TouchableHighlight onPress={() => {
navigation.navigate('home');
}}>
<Image style={{ marginRight: 20 }} source={require('../../resources/toolbar/home_white.png')} />
</TouchableHighlight>),
headerTintColor: 'white',
headerStyle: {
backgroundColor: colors.themeColor,
// top: 30
}
});
_renderList = ({ item }) => {
return (
<TouchableWithoutFeedback onPress={(event) => this._selectedItem(item.key)}>
<View style={styles.listRowContainer}>
<View style={styles.listinside1Container}>
<Image style={styles.listImage} source={item.icon} />
<View style={styles.listContainer} onPress={(event) => this._selectedItem(item.text)} >
<Text style={styles.listHeader} >{item.header}</Text>
<Text style={styles.listValue} >{item.value}</Text>
</View>
</View>
<Image style={styles.listimgArrow} source={require('../../resources/toolbar/chevron_right_grey.png')} />
</View>
</TouchableWithoutFeedback>
);
}
// Render callBack
render() {
return (
<View style={styles.mainWrapper} >
<FlatList data={this.state.data} renderItem={this._renderList} />
</View>
);
}
}
const styles = StyleSheet.create({
mainWrapper: {
flex: 1,
height: Dimensions.get('window').height,
width: Dimensions.get('window').width,
flexDirection: 'column',
justifyContent: 'flex-start'
},
listRowContainer: {
flexDirection: 'row',
marginTop: 10,
height: 80,
backgroundColor: '#FFFFFF',
justifyContent: 'space-between',
borderBottomWidth: 1,
borderColor: 'lightgray'
},
listinside1Container: {
flexDirection: 'row',
justifyContent: 'flex-start',
alignItems: 'center'
},
listContainer: {
alignItems: 'flex-start',
justifyContent: 'center',
flexDirection: 'column',
backgroundColor: '#FFFFFF',
// borderBottomWidth: 1,
// borderColor: 'lightgray'
},
listHeader: {
color: 'black',
fontFamily: 'Roboto-Medium',
marginLeft: 10,
fontSize: 18,
},
listValue: {
fontFamily: 'Roboto-Regular',
marginTop: 4,
color: 'black',
marginLeft: 10,
fontSize: 14,
},
listImage: {
alignSelf: 'center',
height: 25,
width: 25,
margin: 10
},
listimgArrow: {
// flex: 1,
// flexDirection:'row',
alignSelf: 'center',
height: 25,
width: 25,
margin: 10
},
listVal: {
borderWidth: 1,
borderRadius: 10,
color: 'darkgreen',
borderColor: 'white',
backgroundColor: 'white',
fontWeight: 'bold'
},
});
export default AtmBranchTypeSelect;
From the code I have, headerRight will be displayed in all scenarios. consider I have a scenario like based on state value I have to enable/disable headerRight Button .
for example this.state.stBool? headerRight:(.....) : null
I have to render in this way.Please guide me to achieve this.
You could nest the navigation options inside the render and toggle it based on the state value. Haven't tested and not positively on performace. Hope it helps.
import React, { Component } from "react";
import {
View,
TextInput,
Text,
TouchableOpacity,
TouchableHighlight,
StyleSheet,
AsyncStorage,
BackHandler,
Image,
FlatList,
Dimensions,
TouchableWithoutFeedback
} from "react-native";
import i18n from "../../i18n/i18n.js";
import { colors } from "../../constants/colors.js";
import Storage from "../../utils/AsyncStorage.js";
class AtmBranchTypeSelect extends Component {
// Render callBack
constructor(props) {
super(props);
this.state = {
data: [],
stBool: false
};
}
async componentWillMount() {
BackHandler.addEventListener("hardwareBackPress", () =>
this.props.navigation.goBack()
);
}
componentWillUnmount() {
BackHandler.removeEventListener("hardwareBackPress", () =>
this.props.navigation.goBack()
);
}
_renderList = ({ item }) => {
return (
<TouchableWithoutFeedback onPress={event => this._selectedItem(item.key)}>
<View style={styles.listRowContainer}>
<View style={styles.listinside1Container}>
<Image style={styles.listImage} source={item.icon} />
<View
style={styles.listContainer}
onPress={event => this._selectedItem(item.text)}
>
<Text style={styles.listHeader}>{item.header}</Text>
<Text style={styles.listValue}>{item.value}</Text>
</View>
</View>
<Image
style={styles.listimgArrow}
source={require("../../resources/toolbar/chevron_right_grey.png")}
/>
</View>
</TouchableWithoutFeedback>
);
};
// Render callBack
render() {
const { stBool } = this.state;
const navigationOptions = ({ navigation }) => ({
title: i18n.t("atmbranchpickHeader"),
headerRight: stBool ? (
<TouchableHighlight
onPress={() => {
navigation.navigate("home");
}}
>
<Image
style={{ marginRight: 20 }}
source={require("../../resources/toolbar/home_white.png")}
/>
</TouchableHighlight>
) : null,
headerTintColor: "white",
headerStyle: {
backgroundColor: colors.themeColor
// top: 30
}
});
return (
<View style={styles.mainWrapper}>
<FlatList data={this.state.data} renderItem={this._renderList} />
</View>
);
}
}