Params not updating on state change ,react native - reactjs

i have a react native app with 2 screens ,the first one is supposed to receive updates from a server and updates its state , upon clicking a submit button the app navigates to another screen that is dependent on the state of the first , i have managed to pass the values via the params and retrieve them in the second screen however i can not seem to get the updated values from the first screen as they are being replaced periodically ,how can i go about it, should i pass a reference of the object to the second screen and if so how would i go about it ..
The first screen , note the state in this screen is updating well and fine i console logged it after each setState , its the second screen that's not receiving the updates
handleSubmit = () => {
this.props.navigation.navigate("Tracking Bus", this.state.bus);
};
render() {
return (
<TouchableNativeFeedback onPress={this.handleSubmit}>
<View
style={{
width: 330,
height: 40,
backgroundColor: "#6FCF97",
alignSelf: "center",
justifyContent: "center",
}}
>
<Text style={{ alignSelf: "center", fontSize: 20 }}>Submit</Text>
</View>
</TouchableNativeFeedback>
</View>
</View>
);
}
The Second Screen , render function i.e
render() {
return (
setInterval(() => {
console.log(this.props.route.params.bus.location[1]);
}, 2000);
})
am expecting to get updated values but i i just get the initial values

Maybe this can work :
handleSubmit = () => {
this.props.navigation.navigate("Tracking Bus", {bus:this.state.bus});
};
and in your second screen:
render() {
return (
setInterval(() => {
const bus= this.props.navigation.getParam('bus');
console.log(bus);
}, 2000);
})

I may be late, but you can use componentWillReceiveProps().
export default class Example extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
// If needed
this.componentWillReceiveProps(this.props);
}
componentWillReceiveProps(props) {
if(typeof props.route === "object" && typeof props.route.params === "object" && props.route.params.bus !== undefined) {
this.props.navigation.push("Tracking Bus", {bus:props.route.params.bus});
}
}
// ...
}

You are passing the params incorrectly from first screen. You can try this and your fetching logic seems fine as this.props.route.params.bus
handleSubmit = () => {
this.props.navigation.navigate("Tracking Bus", { params: {bus : this.state.bus} });
};

Related

How to fix 'TypeError: null is not an object (evaluating 'data[index]')

In a react-native project, when navigating to a specific screen, I get this error. but it isn't specific about where its location is. My best guess is that it has something to do with the FlatList thats being rendered in the screen component. Heres a screenshot
I execute a redux action inside componentDidMount, in this same screen, which ultimately populates the redux store that is used as FlatList's data prop. Here's the flatList:
<FlatList
data={this.props.messages}
style={{ paddingTop: 10, paddingBottom: 35 }}
ref={'list'}
onContentSizeChange={() => this.refs.list.scrollToEnd()}
keyExtractor={(item) => {
return (
item.toString() +
new Date().getTime().toString() +
Math.floor(Math.random() * Math.floor(new Date().getTime())).toString()
);
}}
renderItem={({ item, index }) => {
if (item.content.type == 'msg') {
if (this.props.role == 'a') {
return (
<Components.ChatMessage
isSender={item.sender == this.props.uid ? true : false}
message={item.content.data}
read={item.read}
time={item.time}
onPress={() =>
this.props.readMsg({
id: this.props.uid,
role: this.props.role,
convoId: this.props.navigation.state.params.convoId },
item.docId,
this.props.navigation.state.params.uid
)}
/>
);
}
}}
/>
the componentDidMount() looks like:
componentDidMount () {
this.props.getMessages(this.state.ownerConvoId).then((success) => {
if (this.state.ownerConvoId == this.props.messages[0]['userConvos'][0]) {
this.setState({ withConvoId: this.props.messages[0]['userConvos'][1] });
} else {
this.setState({ withConvoId: this.props.messages[0]['userConvos'][0] });
}
});
}
Not sure whats going on at all or where to look. Any advice is appreciated.
Based on the screenshot it seems this is the offending line:
onContentSizeChange={() => this.refs.list.scrollToEnd()}
I would first check when that function is being called. I suspect you will be surprised at when it's happening. e.g. perhaps it's firing before ref is defined. Perhaps it's firing before the list has any content.
Update:
Spun up a fresh app and pasted your flatlist code like so
import React, { Component } from "react";
import { FlatList } from "react-native";
export default class BadFlatList extends Component {
constructor(props) {
super(props)
};
render() {
return (
<FlatList
data={null}
style={{ paddingTop: 10, paddingBottom: 35 }}
ref={'list'}
onContentSizeChange={() => this.refs.list.scrollToEnd()}
keyExtractor={(item) => {
return (
item.toString() +
new Date().getTime().toString() +
Math.floor(Math.random() * Math.floor(new Date().getTime())).toString()
);
}}
renderItem={({ item, index }) => {
if (item.content.type == 'msg') {
if (this.props.role == 'a') {
return (
<Components.ChatMessage
isSender={item.sender == this.props.uid ? true : false}
message={item.content.data}
read={item.read}
time={item.time}
onPress={() =>
this.props.readMsg({
id: this.props.uid,
role: this.props.role,
convoId: this.props.navigation.state.params.convoId },
item.docId,
this.props.navigation.state.params.uid)} />
);
}
}
}} />
);
}
};
Notice I replaced your
<FlatList
data={this.props.messages}
with
<FlatList
data={null}
That reproduces what you are seeing. So I'd say your initial component state is passing in null data to your FlatList and that causes the problem. Just Check for missing data before rendering the FlatList and return null until the data is there.

Implementing a button on the header bar, react native

I am trying to make a button on the right of the header bar so that it will change the order of the list, and i was thinking to have a state of the order and flip it as the button will be pressed and check wether that is true and false and show the list in the requested order.
But i get an error saying Notes.flipState is not a function. (In 'Notes.flipState()', 'Notes.flipState is undefined)
Any idea why is this hapening and how to solve it, below is my function flipState() and the navigationOptions.
state = {
desc: true
}
flipState() {
this.setState({ desc: !this.state.desc})
}
static navigationOptions = ({ flipState }) => {
return {
title: 'Upcoming Movies',
headerStyle: {
backgroundColor: 'blue',
},
headerTintColor: 'white',
headerTitleStyle: {
fontWeight: 'bold',
},
headerRight: () => (
<BorderlessButton
onPress={() => this.flipState()}
style={{ marginRight: 20 }}>
<Icon
name={'sort-alpha-asc'}
size={25}
color='gray'
/>
</BorderlessButton>
),
}
};
You can't call a "public" method inside of static method. "Play" with navigation state to pass data between header and content.
See setParams https://reactnavigation.org/docs/en/navigation-prop.html
A static method won't be able to access this .you have to pass function through the navigation params.
like
componentDidMount() {
this.props.navigation.setParams({
flipStat: this.flipStat,
});
}
get from navigationOption
static navigationOptions = ({ navigation }) => {
const flipStat = navigation.state.params.flipStat;
return </>
}

Barcode scanner scans once and shows the same result when scanned for second time

I have a react native app in which i'm using react-native-camera and i can scan once and the scan result shows without any issue. But when i go for the second scan it shows the same result.I have my barcode scanner in one tab and after scanning it navigates to a different tab with the result.How can i scan and show results more than once?
Here's the barcode scanner tab component i have:
class BarScannerView extends Component {
constructor(props) {
super(props);
this.camera = null;
this.barcodeCodes = [];
this.state = {
changeScreen: false,
camera: {
type: RNCamera.Constants.Type.back,
flashMode: RNCamera.Constants.FlashMode.auto,
barcodeFinderVisible: true
},
focusedScreen: false
};
}
onBarCodeRead = (scanResult) => {
if (scanResult.data !== null) {
let bacodeScanResult = scanResult.data
AsyncStorage.setItem('barcodeValue', bacodeScanResult)
this.props.navigation.navigate('Stock')
}
return;
}
componentDidMount() {
console.log(' BarScanner componentDidMount was called...', this.props)
const { navigation } = this.props;
navigation.addListener('willFocus', () =>
this.setState({ focusedScreen: true })
);
navigation.addListener('willBlur', () =>
this.setState({ focusedScreen: false })
);
}
componentWillUnmount() {
console.log('BarScanner componentWillUnmount was called..')
this.onBarCodeRead()
}
render() {
const { focusedScreen } = this.state;
if (focusedScreen) {
return (
<View style={styles.container}>
<RNCamera
ref={ref => {
this.camera = ref;
}}
barcodeFinderVisible={this.state.camera.barcodeFinderVisible}
barcodeFinderWidth={280}
barcodeFinderHeight={220}
barcodeFinderBorderColor="white"
barcodeFinderBorderWidth={2}
defaultTouchToFocus
flashMode={this.state.camera.flashMode}
onBarCodeRead={this.onBarCodeRead}
onFocusChanged={() => { }}
onZoomChanged={() => { }}
permissionDialogTitle={'Permission to use camera'}
permissionDialogMessage={'We need your permission to use your camera phone'}
style={styles.preview}
type={this.state.camera.type}
/>
<View style={[styles.overlay, styles.topOverlay]}>
<Text style={styles.scanScreenMessage}>Please scan the barcode.</Text>
</View>
<View style={{ position: 'absolute', top: 150, left: '12%' }}>
<View
style={{
width: 300,
height: 300,
backgroundColor: 'transparent',
borderColor: 'white',
borderWidth: 1
}}
>
</View>
</View>
<View style={[styles.overlay, styles.bottomOverlay]}>
<Button
disabled
onPress={() => { console.log('scan clicked'); }}
style={styles.enterBarcodeManualButton}
title="Choose Barcode"
/>
</View>
</View>
);
} else {
return <Stock/>
}
}
}
export default BarScannerView
How can i remove previous result when user goes to this barcode scanner tab for the second time?
Additional info:
React Native version:"0.57.8"
React Navigation v3
React-Native-Camera "^1.6.4"
You need to use this.props.navigation.push instead of this.props.navigation.navigate in order to navigate to the same screen and show new content.
Navigation Push
React navigation just push a new screen to the stack without hesitation.
Navigation navigate
React navigation will search for the screen in the stack, if the screen is present it will navigate to it, otherwise it will add it to the stack and navigate.

accessing the state outside the class - Reactnative - this.setState is not a function

I am using custom floating text labels which are outside the class and I want to set the state within it. how can I do it please
I have two floating text fields when I click the button I want to set the state to the entered values in the input text field.
The error is get is
this.setState is not a function
const TextfieldWithFloatingLabel_Card = MKTextField.textfieldWithFloatingLabel()
.withPlaceholder(strings.nineDigitCardNumber)
.withStyle(styles.textfieldWithFloatingLabel)
.withTextInputStyle({ flex: 1 })
.withFloatingLabelFont({
fontSize: 12,
fontWeight: '200',
color: colors.primaryColor
})
.withKeyboardType('numeric')
.withOnEndEditing(e => {
this.setState({ cardDigits: e.nativeEvent.text });
console.log('EndEditing', e.nativeEvent.text);
})
.build();
const TextfieldWithFloatingLabel_NationalId = MKTextField.textfieldWithFloatingLabel()
.withPlaceholder(strings.nationalIdNumber)
.withStyle(styles.textfieldWithFloatingLabel)
.withTextInputStyle({ flex: 1 })
.withFloatingLabelFont({
fontSize: 12,
fontWeight: '200',
color: colors.primaryColor
})
.withKeyboardType('numeric')
.withOnEndEditing(e => {
this.setState({ nationalIdNumber: e.nativeEvent.text });
console.log('EndEditing', e.nativeEvent.text);
})
.build();
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
cardDigits: '',
IdNumber: ''
};
}
render() {
//console.log('rovers - ' + JSON.stringify(this.state.rovers))
return (
<ScrollView style={styles.mainContainer}>
<TextfieldWithFloatingLabel_Card ref="tiNumber" />
<TextfieldWithFloatingLabel_NationalId ref="tiNationalId" />
<TouchableOpacity
title="Transactions"
style={{
height: 60,
backgroundColor: '#673fb4',
marginTop: 20,
alignItems: 'center',
justifyContent: 'center'
}}
onPress={() => {
consoleLog(
'cardnum : ' +
this.state.cardDigits +
' national id - ' +
this.state.IdNumber
);
}}
>
<CommonText style={{ color: 'white' }}>
{strings.signInLower}
</CommonText>
</TouchableOpacity>
</ScrollView>
);
}
}
Thanks
R
Change
TextfieldWithFloatingLabel_Card
To a function instead of a variable and wherever you call TextfieldWithFloatingLabel_Card in the component pass this as a parameter to it so that when you do setState it will work
I will give you hint on how to do
Test.js
const test = this = {
this.setState({test: “working”});
}
export = test();
App.js component
import { test } from “./Test”;
callTest = () => {
test(this);
}
render(){
return(
<div onClick={this.callTest}>
</div>
)
}
So the point here is you need to pass component this to imported regular functions so that you can set the state of the component.
What you doing is old methodology. I would recommend you to handle the event handler functions with in components.
Please execuse me if there are any typo errors because I am answering on my mobile

Autocomplete with react native

can someone please help me debug this code, I'd like to use 'react-native-autocomplete-input' library to autocomplete a search text input, basically the api request return a list of stock symbols and company names and I'm thinking to store this file locally to make it faster, for right now I just want to make it work using fetch. The autocomplete must search in responseJson.symbol
Here's what I've done so far:
import Autocomplete from 'react-native-autocomplete-input';
import React, { Component } from 'react';
import { StyleSheet, Text, TouchableOpacity, Platform,
ScrollView, View, ActivityIndicator,
} from 'react-native';
class AutocompleteExample extends Component {
constructor(props) {
super(props);
this.state = {
symbols: [],
query: '',
};
}
searchSymbol(query) {
if (query === '') {
return [];
}
const { symbols } = this.state;
const url = `https://api.iextrading.com/1.0/ref-data/symbols`;
fetch(url)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
symbols:responseJson.symbol,
});
})
.catch((error) => {
console.error(error);
});
return symbols;
}
render() {
if (this.state.isLoading) {
return (
<View style={{flex: 1, paddingTop: 20}}>
<ActivityIndicator />
</View>
);
}
const { query } = this.state;
const symbols = this.searchSymbol(query);
return (
<ScrollView>
<View style={styles.MainContainer}>
<Text style={{fontSize: 12,marginBottom:10}}> Type your favorite stock</Text>
<Autocomplete
autoCapitalize={true}
containerStyle={styles.autocompleteContainer}
data={symbols}
defaultValue={query}
onChangeText={text => this.setState({ query: text })}
placeholder="Enter symbol"
renderItem={({ symbol }) => (
<TouchableOpacity onPress={() => this.setState({ query: symbol })}>
<Text style={styles.itemText}>
{symbol}
</Text>
</TouchableOpacity>
)}
/>
</View>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
MainContainer :{
justifyContent: 'center',
flex:1,
paddingTop: (Platform.OS === 'ios') ? 20 : 20,
padding: 5,
},
autocompleteContainer: {
borderWidth: 1,
zIndex:999,
borderColor: '#87ceeb',
},
itemText: {
fontSize: 17,
color:'#000000',
}
});
module.exports = AutocompleteExample
I don't see any error on the console and the api request is working correctly, I just can't access symbols const Do I have to render something like cloneWithRows(responseJson.symbols) ? Thanks
First of all, the searchSymbol method should be either binded in the component constructor, or declared as a class property. Otherwise, "this" will not point to the component instance.
Then, it seems that your state does not have an isLoading property, but you use it in your render function.
What you should probably do is call your asynchronous searchSymbol method in the componentDidMount lifecycle method. When the promise of the fetch resolves, you should put the result in the state as well as put the isLoading boolean to false. Then your component will re-render with the now available data.

Resources