React Navigation passing data with function call on navigate - react-native

I'm trying to pass a variable (i can do this) and run a function with that variable as soon as navigation happened.
Function takes value of a input field. But because i assigned input field to my variable i'm having some trouble on render method because it overwrites the data after navigation or not even write it. It works when manually input but i need to pass this variable from camera also(this also works fine).
Main Page
onChange(obj) {
const value = obj;
if (value) {
this.setState({
buttonDisabled: false,
text: value,
});
} else {
this.setState({
buttonDisabled: true,
text: ''
});
}
};
render() {
const { navigation } = this.props;
const scanCode = navigation.getParam('text', '');
return (
<View style={{padding: 30, paddingTop: 40}}>
<TextInput autoFocus
ref='queryInput'
style={{height: 40}}
placeholder="placeholder.."
value={scanCode === '' ? this.state.text : scanCode}
onChangeText={this.onChange.bind(this)}
onSubmitEditing={this.runQuery}
/>
<Button
onPress={this.runQuery}
title="run"
color="#841584"
accessibilityLabel="query"
disabled={this.state.buttonDisabled}
/>
<Button title='Camera' style={{position: 'absolute', bottom: 0, left: 0}} onPress={() => this.gotoCamera()}></Button>
</View>
);
}
};
Camera Page
gotoMainPage = () => {
this.props.navigation.navigate('Home', {
text: this.state.qrcode
})
}
onBarCodeRead = (e) => this.setState({qrcode: e.data}, () => this.gotoMainPage());
is it possible to setState of a value in Main page from Camera Page or what should i do here?
Thanks

Related

Creating a FlatList Grid from props data

Creating a screen that contains a FlatList grid of photos that I fetch from my rails API. Having trouble displaying the data - https://ibb.co/XkfSvxm. I wish for the images, regardless of how many there are, to be in a square format.
Currently, I can fetch and display the data in square photos except whenever there is 1 or 2 photos on a row. They fill the row rather than take the formatting from the formatItems method (which essentially pushes two empty items into the array forcing the data into a grid).
I've tried a bunch of things including returning the data straight from formatItems and plugging the method straight into the data of the flatlist, but then no data is loading. I also had it working well with a local SQL DB package.
How would I go around arranging so that I can format the data from props and display it correctly in the FlatList?
Here is the code:
class VaultScreen extends React.Component {
state = {
searchQuery: '',
refreshing: false,
setRefreshing: false
};
constructor(props) {
super(props);
this.state = {
items: {},
itemSelected: {}
};
this.props.fetchPosts()
}
componentDidMount() {
let itemData = this.props.posts
this.setState({
items: itemData,
});
// this.formatItems(itemData)
}
formatItems = () => {
const { items } = this.state
const newItems = []
const numberOfFullRows = Math.floor(items.length / 3);
// const numberOfFullRows = Math.floor(itemData.length / 3);
let numberOfElementsLastRow = 1
// items.length - (numberOfFullRows * 3);
while (numberOfElementsLastRow !== 3 && numberOfElementsLastRow !== 0) {
newItems.push({ key: `blank-${numberOfElementsLastRow}`, empty: true });
numberOfElementsLastRow++;
}
return this.setState({ items: newItems })
};
renderItem = ({ item, type }) => {
const { items } = this.state;
if (item.empty === true) {
return <View style={[styles.item, styles.itemInvisible]} />;
} else {
return (
<TouchableOpacity style={styles.item} onPressIn={() => this.setState({ itemSelected: item.id })} onPress={this.viewPhoto} key={item.id}>
<Image source={{ uri: item.image }} style={{ flex: 1, width: '100%', height: undefined }} />
</TouchableOpacity>
);
}
};
render() {
return (
<SafeAreaView style={{ flex: 1, backgroundColor: 'white' }}>
<FlatList
data={this.state.items}
renderItem={this.renderItem}
numColumns={3}
keyExtractor={(item, index) => index.toString()}
refreshControl={<RefreshControl refreshing={this.state.refreshing} onRefresh={() => this.onRefresh()} />}
/>
</SafeAreaView>
);
}
}
const mapStateToProps = state => ({
posts: state.vault.posts
})
const mapDispatchToProps = {
fetchPosts: () => fetchPosts(),
}
export default connect(mapStateToProps, mapDispatchToProps)(VaultScreen)
hi you can add minWidth and maxWidth styling to the parant View of the render method (in your case give style to TouchableOpacity ) value should be total_width / 3 (+-margin if you want to give) . and then add width:total_width to columnWrapperStyle of FlatList.
NOTE:
-- total_width = 100%
-- keeping minWidth and maxWidth values same will give you perfect view as per your expectation (In your case if total_width : 100% then minWidth and maxWidth values will be 30%)

How to refresh/re-render flatlist on react-native?

im trying to refresh my flatlist from some page without going back to the principal menu, but it doesnt work.
I've already readed about extraData, but it doesnt work either.
Basiclly my program is like that:
I have a page called "passwords" and i add some passwords there from another page called "add passwords". When i click to add a password, i want to refresh the flatlist from the page "passwords" to show me the password that i just added.
This is my code from the page "add passwords"
...
state = {
arr: [],
local: '',
password: '',
obj: {
local: '',
password: ''
},
count: 1,
texto: ''
};
componentDidMount() {
//Here is the Trick
const { navigation } = this.props;
//Adding an event listner om focus
//So whenever the screen will have focus it will set the state to zero
this.focusListener = navigation.addListener('didFocus', () => {
this.setState({ count: 0 });
});
}
storeItem(item) {
try {
//we want to wait for the Promise returned by AsyncStorage.setItem()
//to be resolved to the actual value before returning the value~
console.log(item)
var joined = this.state.arr.concat(item);
console.log(joined)
this.setState({ arr: joined })
AsyncStorage.setItem('array', JSON.stringify(joined));
console.log(this.state.arr)
} catch (error) {
console.log(error.message);
}
}
componentWillMount() {
AsyncStorage.getItem('array').then(array => {
item = JSON.parse(array)
array ? this.setState({ arr: item }) : null;
console.log(item)
})
}
render() {
return (
<View style={styles.container}>
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={(text) => this.setState({ local: text })}
value={this.state.local}
/>
<TextInput
secureTextEntry={true}
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={(text) => this.setState({ password: text })}
value={this.state.password}
/>
<Button title='Adicionar'
onPress={() => this.storeItem({ local: this.state.local, password: this.state.password }) + alert("Adicionado com sucesso!") + this.props.navigation.navigate('Passwords')}
></Button>
</View>
);
}
}
And this is my page "passwords" where i want to refresh
componentWillMount() {
const { navigation } = this.props;
this.willFocusListener = navigation.addListener(
'willFocus',
() => {
this.setState({ count: 10 })
}
)
AsyncStorage.getItem('array').then(array => {
item = JSON.parse(array)
item ? this.setState({ arr: item }) : null;
console.log(this.state.arr)
})
}
renderItem = ({ item }) => (
<View style={{ flexDirection: 'row' }} style={styles.passwordContainer}>
<Text> {item.local} </Text>
<Text> {item.password} </Text>
</View>
)
render() {
return (
<View style={styles.container}>
<FlatList
data={this.state.arr}
renderItem={this.renderItem}
extraData={this.state} //this is what i tryied
/>
</View>
);
You can use your listener to update the state.
componentWillMount() {
this.willFocusListener = navigation.addListener(
'willFocus',
() => this.updateData()
}
updateData = () => {
this.setState({ count: 10 });
AsyncStorage.getItem('array').then(array => {
item = JSON.parse(array)
item ? this.setState({ arr: item }) : null;
console.log(this.state.arr)
});
}
Any state changes will rerender items.

React-Native-Component not rendering when state is changed

I am making the show more and show less functionality inside a flat list but the state pressed is not working as expected .When I am setting the state value component is not being rendered when the state changes its value.
My constructor is set like below
this.state = {
accList: [],
expanded: false,
expandedText: "Show More"
}
In componentdidmount() I am updating the value of accList value like below
componentDidMount = () => {
this.setState({
accList:[{
"customer_name": "Shubhangi J Thakur",
"message":"Hello"
},
{
"customer_name": "Arthur S Campbell",
"message":"Hello_World"
},
{
"customer_name": "Susan R Brill",
"message":"hellow"
}]
});
}
I have defined the flatlist in render() like below
<FlatList
onScroll={this.handleScroll}
data={this.state.accList}
renderItem={this.renderItem}
keyExtractor={this._keyExtractor}
/>
renderItem = ({ item, index }) => (
<Card style={style.cardLayout} key={index}>
<CardItem header>
<Text>{item.customer_name}</Text>
</CardItem>
{this.seemorefunctionality(item)}
</Card>
);
seemorefunctionality = (item) => {
return <View>
{this.state.expanded ? this.expandedView(item) :null}
//To view Show More and Show Less Text
<TouchableOpacity onPress={this.expandedText}>
<Text> {this.state.expandedText}</Text>
</TouchableOpacity>
</View>
}
}
expandedText = () => {
console.log('Setting the expanded text value', this.state.expanded)
if (this.state.expanded) {
this.setState({
expandedText: "Show More"
});
}
else {
this.setState({
expandedText: "Show Less"
});
}
value=!this.state.expanded
this.setState({
expanded: value
});
}
expandedView = (item) => {
return <View>
{item.map((obj, index) => {
return (
<View key={index} >
<Text>{obj.message}</Text>
</View>
)
})}
</View>
When I am clicking on the this.state.expandedText value is getting changed when we see in the console but its not reflecting in the View also expandedView is not being rendered when this.state.expanded is set to true.
In View the value of this.state.expandedText is always showing Show More while I can see In console that the value is getting changed to Show more and Show Less on click
for re-rendering flatlist you have to add extraData={this.state} as mention on https://facebook.github.io/react-native/docs/flatlist

react native modal does not open

I am creating signup form and trying to sign in with phone number which returns verification code which i wanted to type in modal windrow. I wanted to open modal window after signup button has been click.I have sent visible property of modal to true but it is not being displayed on the screen. i have check the console value for the visibility property which is showing me "true" , which i could understand my modal will opn successfully but it is not doing anything. There is no errors also.
My code:
signIn = () => {
const { name, password, confirmPassword, mobile } = this.props;
const object = { confirmPassword, password };
const error = Validator('name', name)
|| Validator('password', password)
|| PasswordValidator(object)
|| Validator('mobile', mobile);
const mobileNo = '+91'+mobile;
if (error != null) {
Alert.alert('Error', error);
} else {
console.log('else');
const mobile = this.props.mobile;
const userRef = firebase.database().ref(`/users/`);
userRef.once('value', function(snapshot) {
//If user is existed redirect to login page
if(snapshot.val().mobile == mobile){
Alert.alert('Error', 'Phone number is already registered! Please login with your credentials');
NavigationService.navigate('Login');
}
});
}
// If user is not exist signup
firebase.auth().signInWithPhoneNumber(mobileNo)
.then(confirmResult => this.setState({ confirmResult, message: 'Code has been sent!' },
this.renderVerificationCodeInput()
))
.catch(error => this.setState({ message: `Sign In With Phone Number Error: ${error.message}` }));
};
renderVerificationCodeInput() {
console.log('code');
const { codeInput } = this.state;
this.setModalVisible(!this.state.modalVisible);
console.log(this.state.modalVisible);
<View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}>
<Modal
animationType="slide"
transparent={false}
visible={this.state.modalVisible}
onRequestClose={() => {
console.log('Modal has been closed');
}}
>
{console.log(this.state.modalVisible)}
<View >
<Text>Enter verification code below:</Text>
<Input
autoFocus
style={{ height: 40, marginTop: 15, marginBottom: 15 }}
onChangeText={value => this.setState({ codeInput: value })}
placeholder={'Code ... '}
value={codeInput}
/>
<Button title="Confirm Code" color="#841584" onPress={this.confirmCode} />
</View>
</Modal>
</View>
}
I don't know if its all the code,
if not you need to add state
state = {modalVisible: false}
and replace this.setModalVisible(!this.state.modalVisible);
to this.setState({modalVisible:!this.state.modalVisible)

Highlight a selected item in React-Native FlatList

I put together a simple React-native application to gets data from a remote service, loads it in a FlatList. When a user taps on an item, it should be highlighted and selection should be retained. I am sure such a trivial operation should not be difficult. I am not sure what I am missing.
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
FlatList,
ActivityIndicator,
Image,
TouchableOpacity,
} from 'react-native';
export default class BasicFlatList extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
page: 1,
seed: 1,
error: null,
refreshing: false,
selectedItem:'null',
};
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const {page, seed} = this.state;
const url = `https://randomuser.me/api/?seed=${seed}&page=${page}&results=20`;
this.setState({loading: true});
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: page === 1 ? res.results : [...this.state.data, ...res.results],
error: res.error || null,
loading: false,
refreshing: false
});
})
.catch(error => {
this.setState({error, loading: false});
});
};
onPressAction = (rowItem) => {
console.log('ListItem was selected');
console.dir(rowItem);
this.setState({
selectedItem: rowItem.id.value
});
}
renderRow = (item) => {
const isSelectedUser = this.state.selectedItem === item.id.value;
console.log(`Rendered item - ${item.id.value} for ${isSelectedUser}`);
const viewStyle = isSelectedUser ? styles.selectedButton : styles.normalButton;
return(
<TouchableOpacity style={viewStyle} onPress={() => this.onPressAction(item)} underlayColor='#dddddd'>
<View style={styles.listItemContainer}>
<View>
<Image source={{ uri: item.picture.large}} style={styles.photo} />
</View>
<View style={{flexDirection: 'column'}}>
<View style={{flexDirection: 'row', alignItems: 'flex-start',}}>
{isSelectedUser ?
<Text style={styles.selectedText}>{item.name.first} {item.name.last}</Text>
: <Text style={styles.text}>{item.name.first} {item.name.last}</Text>
}
</View>
<View style={{flexDirection: 'row', alignItems: 'flex-start',}}>
<Text style={styles.text}>{item.email}</Text>
</View>
</View>
</View>
</TouchableOpacity>
);
}
render() {
return(
<FlatList style={styles.container}
data={this.state.data}
renderItem={({ item }) => (
this.renderRow(item)
)}
/>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 50,
},
selectedButton: {
backgroundColor: 'lightgray',
},
normalButton: {
backgroundColor: 'white',
},
listItemContainer: {
flex: 1,
padding: 12,
flexDirection: 'row',
alignItems: 'flex-start',
},
text: {
marginLeft: 12,
fontSize: 16,
},
selectedText: {
marginLeft: 12,
fontSize: 20,
},
photo: {
height: 40,
width: 40,
borderRadius: 20,
},
});
When user taps on an item in the list, "onPress" method is invoked with the information on selected item. But the next step of highlight item in Flatlist does not happen. 'UnderlayColor' is of no help either.
Any help/advice will be much appreciated.
You can do something like:
For the renderItem, use something like a TouchableOpacity with an onPress event passing the index or id of the renderedItem;
Function to add the selected item to a state:
handleSelection = (id) => {
var selectedId = this.state.selectedId
if(selectedId === id)
this.setState({selectedItem: null})
else
this.setState({selectedItem: id})
}
handleSelectionMultiple = (id) => {
var selectedIds = [...this.state.selectedIds] // clone state
if(selectedIds.includes(id))
selectedIds = selectedIds.filter(_id => _id !== id)
else
selectedIds.push(id)
this.setState({selectedIds})
}
FlatList:
<FlatList
data={data}
extraData={
this.state.selectedId // for single item
this.state.selectedIds // for multiple items
}
renderItem={(item) =>
<TouchableOpacity
// for single item
onPress={() => this.handleSelection(item.id)}
style={item.id === this.state.selectedId ? styles.selected : null}
// for multiple items
onPress={() => this.handleSelectionMultiple(item.id)}
style={this.state.selectedIds.includes(item.id) ? styles.selected : null}
>
<Text>{item.name}</Text>
</TouchableOpacity>
}
/>
Make a style for the selected item and that's it!
In place of this.state.selectedItem and setting with/checking for a rowItem.id.value, I would recommend using a Map object with key:value pairs as shown in the RN FlatList docs example: https://facebook.github.io/react-native/docs/flatlist.html. Take a look at the js Map docs as well: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map.
The extraData prop recommended by #j.I-V will ensure re-rendering occurs when this.state.selected changes on selection.
Your onPressAction will obviously change a bit from example below depending on if you want to limit the number of selections at any given time or not allow user to toggle selection, etc.
Additionally, though not necessary by any means, I like to use another class or pure component for the renderItem component; ends up looking something like the following:
export default class BasicFlatList extends Component {
state = {
otherStateStuff: ...,
selected: (new Map(): Map<string, boolean>) //iterable object with string:boolean key:value pairs
}
onPressAction = (key: string) => {
this.setState((state) => {
//create new Map object, maintaining state immutability
const selected = new Map(state.selected);
//remove key if selected, add key if not selected
this.state.selected.has(key) ? selected.delete(key) : selected.set(key, !selected.get(key));
return {selected};
});
}
renderRow = (item) => {
return (
<RowItem
{...otherProps}
item={item}
onPressItem={this.onPressAction}
selected={!!this.state.selected.get(item.key)} />
);
}
render() {
return(
<FlatList style={styles.container}
data={this.state.data}
renderItem={({ item }) => (
this.renderRow(item)
)}
extraData={this.state}
/>
);
}
}
class RowItem extends Component {
render(){
//render styles and components conditionally using this.props.selected ? _ : _
return (
<TouchableOpacity onPress={this.props.onPressItem}>
...
</TouchableOpacity>
)
}
}
You should pass an extraData prop to your FlatList so that it will rerender your items based on your selection
Here :
<FlatList style={styles.container}
data={this.state.data}
extraData={this.state.selectedItem}
renderItem={({ item }) => (
this.renderRow(item)
)}
/>
Source : https://facebook.github.io/react-native/docs/flatlist
Make sure that everything your renderItem function depends on is passed as a prop (e.g. extraData) that is not === after updates, otherwise your UI may not update on changes
First
constructor() {
super();
this.state = {
selectedIds:[]
};
}
Second
handleSelectionMultiple = async (id) => {
var selectedIds = [...this.state.selectedIds] // clone state
if(selectedIds.includes(id))
selectedIds = selectedIds.filter(_id => _id !== id)
else
selectedIds.push(id)
await this.setState({selectedIds})
}
Third
<CheckBox
checked={this.state.selectedIds.includes(item.expense_detail_id) ? true : false}
onPress={()=>this.handleSelectionMultiple(item.expense_detail_id)}
/>
Finally i got the solution to my problem from the answer given by Maicon Gilton