React call function and setstate when go back to a previous screen - react-native

I'm new in React's world
I have 2 screens : Stock and Barcode.
In Stock, i navigate to Barcode's screen.
When i scan a barcode, i go back to the previous screen I would like to set the input text with the barcode and call a function. In my example joinData();
The problem is to set the input text and call a function.
I tried examples and answers but i don't find or don't understand how to to that.
I tried something in componentDidUpdate() but it fails
Invariant Violation:Maximum update depth exceeded
Stock.js
import React, {useState} from "react";
import { ScrollView, TouchableWithoutFeedback, Dimensions, StyleSheet, FlatList, View, Alert, TouchableOpacity, TextInput } from 'react-native';
//galio
import { Block, Text, theme } from "galio-framework";
import { Button, Icon, Input } from "../components/";
export default class Stock extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
this.array = [];
this.state = {
arrayHolder: [],
Input_ITMREF: ''
};
}
// I tried this but it fails
componentDidUpdate() {
if (this.props.navigation.getParam('itmref') != 'undefined') {
this.setState({ Input_ITMREF: this.props.navigation.getParam('itmref')});
}
}
componentDidMount() {
this.setState({ arrayHolder: [...this.array] }) // RafraƮchit la liste
}
joinData = () => {
vxml = this.state.Input_ITMREF+" I do something";
}
Render() {
return (
<Block flex>
<Block row space="evenly">
<Block center>
<Input
placeholder='Code article'
onChangeText={data => this.setState({ Input_ITMREF: data })}
ref={this.myRef}
/>
</Block>
</Block>
<Block center>
<Button style={styles.button} onPress={() => this.props.navigation.navigate('Barcode')}>Barcode</Button>
<Text style={{ margin: 10 }}>Post: {this.props.navigation.getParam('itmref')}</Text>
</Block>
</Block>
);
}
}
And Barcode.js
import React, {} from 'react';
import { ScrollView, TouchableWithoutFeedback, Dimensions, StyleSheet, FlatList, View, Alert, TouchableOpacity, TextInput } from 'react-native';
import { BarCodeScanner } from 'expo-barcode-scanner';
import { Button } from "../components/";
export default class Barcode extends React.Component {
static navigationOptions = {
header: null //hide the header bar
};
handleBarCodeScanned = ({ type, data }) => {
this.props.navigation.navigate("Stock", {
itmref: data
});
};
render() {
return (
<BarCodeScanner
onBarCodeScanned={this.handleBarCodeScanned}
style={styles.barcodeScanner}
/>
);
}
}

You can pass a state handler function as prop to Barcode screen and use that to set value for textInput in state.
in Stock(in state)
state = {
inputValue: ''
}
....
const setInputTextValue= (newValue) => {
this.setState({
inputValue: newValue
})
you pass this function as prop to Barcode scene and call it whenever you wanna set a new value(considering Stock scene is still mounted).
UPDATE: What is the proper way to update a previous StackNavigator screen?
also another solution i just saw: Updating State of Another Screen in React Navigation

You need to use WillFocus method(Included in react-navigation) when you comeback from Barcodepage to stockPage
componentDidMount(){
console.log("willFocus runs") initial start
const {navigation} = this.props;
navigation.addListener ('willFocus', async () =>{
console.log("willFocus runs") // calling it here to make sure it is logged at every time screen is focused after initial start
});
}
For More Information read this document
https://reactnavigation.org/docs/function-after-focusing-screen/

Related

React Native Async Storage - Cant render value on screen

Hey struggling with this one for a day now.
I am trying to store game data just the gameId and the Level for example Game 1 Level 12
Here is my screen
import React, { Component } from 'react';
import AsyncStorage from '#react-native-async-storage/async-storage';
import { Text, StyleSheet, Button, View, ImageBackground, Pressable } from 'react- native';
import bg from "../assets/images/1.jpg";
import styles from '../assets/style';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
const setScore = async (gameId, level) => {
//// SETS THE SCORE
try {
await AsyncStorage.setItem(scoreKey, level);
console.log(value)
} catch (error) {
console.log(error)
}
};
const getScore = async (gameId) => {
try {
let value = await AsyncStorage.getItem(JSON.stringify(gameId))
if(value !== null) {
// value previously stored
return JSON.stringify(value)
} else {
return "not started"
}
} catch(e) {
// error reading value
}
};
/// This would add game 1 and level 12
setScore('1','12') /// This part works
const theLevel = getScore(1)
export default function Home({navigation, route}) {
return (
<ImageBackground source={bg} resizeMode="cover" style={styles.imageBG}>
<View style={styles.GameList}>
<Text style={styles.mainTitle}>Current Level is {theLevel}</Text>
</View>
</ImageBackground>
);
}
At the bottom of the above code I want to display the level but I get the error
Error: Objects are not valid as a React child (found: object with keys {_U, _V, _W, _X}). If you meant to render a collection of children, use an array instead.
However If I alert(theLevel) it works fine can someone tell me what I am doing wrong please
Call getScore function from within useEffect hook of your Home component.
export default function Home({ navigation, route }) {
const [level, setLevel] = useState(0);
useEffect(() => {
async function getMyLevel() {
const lvl = await getScore(1);
setLevel(lvl);
}
getMyLevel();
}, []);
const onPress = async () => {
await setScore('1','12');
};
return (
<ImageBackground source={bg} resizeMode="cover" style={styles.imageBG}>
<View style={styles.GameList}>
<Text style={styles.mainTitle}>Current Level is {level}</Text>
</View>
<Button title="Set Score" onPress={onPress} />
</ImageBackground>
);
}

Unable to load provider from react-redux module in react native

I am creating a slide bar, In that, I have used the react-redux library. When I call the class which contains the redux-code, it works fine. I want to show this slide bar after login. Therefore, with conditions (I set a state variable if user login successfully then only this page should get rendered), I tried to call the same file which shows a blank page. I printed the console log. I am able to print all the logs. But with conditions, I am not able to load the data.
I don't know much about react-redux.Can you assist me to resolve this?
My code is,
main.js,
import React, {Component} from 'react';
import {
StyleSheet,
Dimensions,
Platform,
View,
StatusBar,
DrawerLayoutAndroid,
} from 'react-native';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import reducer from '../Redux/reducers';
import { setNavigator, setActiveRoute } from "../Redux/actions";
import DrawerContent from '../Navigation/DrawerContent';
import Toolbar from '../Navigation/Toolbar';
import AppNavigation from '../Navigation/AppNavigation';
import { bgStatusBar, bgDrawer } from '../global.styles';
let store = createStore(reducer);
/* getDrawerWidth Default drawer width is screen width - header width
* https://material.io/guidelines/patterns/navigation-drawer.html
*/
const getDrawerWidth = () => Dimensions.get('window').width - (Platform.OS === 'android' ? 56 : 64);
export default class Main extends Component {
constructor() {
super();
this.drawer = React.createRef();
this.navigator = React.createRef();
}
componentDidMount() {
store.dispatch(setNavigator(this.navigator.current));
}
openDrawer = () => {
this.drawer.current.openDrawer();
};
closeDrawer = () => {
this.drawer.current.closeDrawer();
};
getActiveRouteName = navigationState => {
if (!navigationState) {
return null;
}
const route = navigationState.routes[navigationState.index];
// dive into nested navigators
if (route.routes) {
return getActiveRouteName(route);
}
return route.routeName;
};
render() {
return (
<Provider store={store}>
<DrawerLayoutAndroid
drawerWidth={getDrawerWidth()}
drawerPosition={DrawerLayoutAndroid.positions.Left}
renderNavigationView={
() => <DrawerContent closeDrawer={this.closeDrawer} />
}
ref={this.drawer}
>
<View style={styles.container}>
<StatusBar
translucent
animated
/>
<Toolbar showMenu={this.openDrawer} />
<AppNavigation
onNavigationStateChange={(prevState, currentState) => {
const currentScreen = this.getActiveRouteName(currentState);
store.dispatch(setActiveRoute(currentScreen));
}}
ref={this.navigator}
/>
</View>
</DrawerLayoutAndroid>
</Provider>
);
}
}
Login.js
import Main from './main';
render() {
return (
<View>
{this.state.isLoggedIn ?
<Main/>
:
<ChangePassword isUpdatePassword={this.state.isUpdatePassword} callLogin={this.callLogin}/>
);
}
}
If I just call Main class inside render method it works. But It does not work with conditions.

creating a TextInput component that can output normal and output a ****/ secureTextEntry={true}

So I have a problem creating a text input component just like when IGNITE CLI created a component named RoundedButton (code shown below). I want to create a similar component but a TextInput that can output a normal output and output a **** character whenever pressed and needed with simple tweaking. How can I do this?
This is the code for RoundedButton :
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { TouchableOpacity, Text } from 'react-native'
import styles from './Styles/RoundedButtonStyles'
import ExamplesRegistry from '../Services/ExamplesRegistry'
// Note that this file (App/Components/RoundedButton) needs to be
// imported in your app somewhere, otherwise your component won't be
// compiled and added to the examples dev screen.
// Ignore in coverage report
/* istanbul ignore next */
ExamplesRegistry.addComponentExample('Rounded Button', () =>
<RoundedButton
text='real buttons have curves'
onPress={() => window.alert('Rounded Button Pressed!')}
/>
)
export default class RoundedButton extends Component {
static propTypes = {
onPress: PropTypes.func,
text: PropTypes.string,
children: PropTypes.string,
navigator: PropTypes.object
}
getText () {
const buttonText = this.props.text || this.props.children || ''
return buttonText.toUpperCase()
}
render () {
return (
<TouchableOpacity style={styles.button} onPress={this.props.onPress}>
<Text style={styles.buttonText}>{this.getText()}</Text>
</TouchableOpacity>
)
}
}
Have the secureTextEntry linked to component state/prop and change it on button press.
So like this:
secureTextEntry={this.state.showDots}
and then onPress of button
onPress = () => {
this.setState({ showDots: true/false})
}

React Native - OnPress button not working

I have a button with a onPress event i am trying to simply console log that the button was successfully pressed.
The button is within my template file i have extracted this portion of code to a separate file, ive done the same for my tyles.
The add() function associated with the press does not fire when i press the add button, so the console.log does not show.
But the console.log does does show on initial load of the screen ( i dont know why this is )
items.screen.js
import React, { Component } from 'react';
import { StyleSheet, Text, View, TextInput } from 'react-native';
import style from './items.style'
import template from './items.template';
export default class ItemsScreen extends Component {
static navigationOptions = {
title: "Items"
}
constructor(props) {
super(props);
this.state = { text: '' };
}
add() {
console.log("pressed add button");
}
render() {
return template(this, style);
}
}
items.template.js
import React, { Component } from 'react';
import { Text, View, TextInput, Button } from 'react-native';
import { style } from './items.style'
export default (template, style) => {
return (
<View style={style.container}>
<TextInput
style={{ width: 300 }}
value={template.state.text}
onChangeText={(text) => template.setState({ text })}
/>
<Button
onPress={template.add()}
title="Add"
/>
</View>
);
}
You are not assigning template.add to the on press event, instead your code is executing the add function and attempting to assign the result of that to the onPress event.
I find the following two options to be cleaner than the other suggestions:
Create a local onPress Handler (in items.template)
onPress = () => {
template.add();
}
and then
onPress={this.onPress} //note the lack of parentheses `()`
or
create an inline function
onPress={() => template.add()}
You should pass function in attribute,not function call, so it should be
<Button
onPress={template.add.bind(template)}
title="Add"
/>

Accessing navigation props - react-navigation

I just have a question regarding react-navigation.
I understand that the navigation props becomes accessible when a screen is rendered from the stacknavigator.
But how do you access the navigation props if the screen is not rendered by the stacknavigator?
Like this:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
StyleSheet,
Text,
View,
} from 'react-native';
import Swiper from 'react-native-swiper';
import Menu from './Menu';
class HomeSwiper extends Component {
static propTypes = {
navigation: PropTypes.object,
};
render() {
return (
<Swiper showsButtons>
<View>
<Menu
navigationProps={this.props.navigation}
/>
</View>
<View>
<Text>Hello Swiper</Text>
</View>
</Swiper>
);
}
}
export default HomeSwiper;
Wherein Menu is:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { AsyncStorage, TouchableOpacity, Text, BackHandler, Alert } from 'react-native';
import { StandardContainerIOS } from '../components/Container';
import { StandardButton } from '../components/Buttons/';
class Menu extends Component {
static propTypes = {
navigation: PropTypes.object,
};
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
const headerLeft = (
<TouchableOpacity
onPress={params.handleRedirect ? params.handleRedirect : () => null}
>
<Text>Logout</Text>
</TouchableOpacity>
);
return {
headerLeft,
};
};
constructor(props) {
super(props);
this.state = {
email: '',
petName: [],
numberOfPets: '',
};
}
getInitialState() {
return {
petName: ['No Name'],
};
}
async componentWillMount() {
try {
const value = await AsyncStorage.getItem('email');
if (value !== null) {
// We have data!!
}
} catch (error) {
// Error retrieving data
}
this.props.navigation.setParams({
handleRedirect: this.handlePressLogout,
});
this.getEmail();
BackHandler.addEventListener('hardwareBackPress', this.handleBackButton);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
}
render() {
return (
<StandardContainerIOS>
<StandardButton backgroundColor="#6D4C41" title="View Pet" onPress={this.handleIndexView} />
<StandardButton backgroundColor="#6D4C41" title="Register Pet" onPress={this.handlePressRegisterPets} />
<StandardButton backgroundColor="#6D4C41" title="Logout" onPress={this.handlePressLogout} />
</StandardContainerIOS>
);
}
}
export default Menu;
I've removed the other function definition to cut the post a little shorter. The Menu screen was working fine when it was rendered from the stacknavigator. Im trying to incorporate swiping in my app.
Any suggestions?