Does anyone know how to implement SecureStorage in React Native? - javascript

I am using JavaScript + React Native to make an app for my university project. I am using expo to manage my packaging and for viewing the app in the iOS simulator. I am also new to both JavaScript and React Native.
I have been having trouble with implementing a React Native module known as Secure Storage.
https://docs.expo.io/versions/latest/sdk/securestore/
I would like to store user data for the app. Specifically I would like to store a JavaScript object such as:
User{ name:"Bob", age:"20" }
And recall it later.
Having read the documentation, I have made a helper module called "StorageHelper" as seen below:
import * as SecureStore from 'expo-secure-store';
/**
* Stores data
* #param {} key
* #param {*} value
*/
export async function storeObject(key, value)
{
try
{
const jsonValue = JSON.stringify(value);
await SecureStore.setItemAsync(key, jsonValue);
}
catch(e)
{
console.log(e.message);
}
}
/**
* Retrieves data
* #param {} key
*/
export async function getObject(key)
{
let result = await SecureStore.getItemAsync(key);
if (result != null)
{
return result;
}
else
{
alert('No values stored under that key.');
}
On this screen in the app flow, I would like to generate the userObject object and store it in the SecureStorage. This following is
import React from 'react';
import { Text, View, TouchableOpacity, Image } from 'react-native';
import ProjStyleSheet from "../styles/ProjStyleSheet.js";
import * as StorageHelper from "../resources/StorageHelper.js";
const HomeImage = require('../art/HomeImage.png');
const styles = ProjStyleSheet;
function HomeScreen({ navigation }) {
var userData = "Bob"; // instead of the object, I'm just using a simple variable.
StorageHelper.storeObject("userName",userData);
return (
<View style={{
backgroundColor: "#fff",
flexDirection: "column",
justifyContent: "flex-start",
flex:1,}}>
<View style = {{
paddingTop: 100,
paddingBottom: 20,
paddingLeft: 10,
paddingRight: 10,
}}>
<Text style={styles.titleText}>University Money Management App</Text>
</View>
<View style = {{
justifyContent: "space-evenly",
alignItems: "center",
}}>
<Image
style={{width: 250, height: 250, paddingBottom:20}}
source={HomeImage}/>
<TouchableOpacity style={styles.defaultButton}
onPress={() => navigation.navigate('IntroName')}>
<Text style={styles.text}> Get Started </Text>
</TouchableOpacity>
</View>
</View>
);
}
export default HomeScreen;
In the next screen, I am trying to get the data from the secure storage and show it on the screen. This can be seen below:
import React, { useState } from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import ProjStyleSheet from "../styles/ProjStyleSheet.js";
import { TextInput } from 'react-native-gesture-handler';
import * as StorageHelper from "../resources/StorageHelper.js";
const styles = ProjStyleSheet;
function IntroName({ navigation })
{
var name = StorageHelper.getObject("userName"); // retrieve the variable here.
return (
<View style={{
flex: 1,
backgroundColor: "#fff",
alignItems: 'center',
justifyContent: "space-around",}}>
<Text style={styles.titleText}>Please Enter Your Name</Text>
<Text> test = {name} </Text> // show the name here
<TextInput
style={{
borderColor: "#000",
borderRadius: 10,
borderWidth: 2,
padding: 20,
fontSize: 20,
width: "75%",
}}
placeholder="Your name here.">
</TextInput>
<TouchableOpacity
style={styles.defaultButton}
onPress={() => navigation.navigate("IntroIncome")}>
<Text style={styles.text}>Next</Text>
</TouchableOpacity>
</View>
);
}
export default IntroName;
Unfortunately, no name appears in the text where I intend it to. I have gone through the Secure Storage documentation and searched through the internet, however I cannot find any answers that can help me. Does anyone have any good ideas on what I am missing to fully implement SecureStorage?

the solution is quite simple. Thanks to the advice of #Tony, I converted all my function components to class components. Within those class components, I wrote an async method which calls the SecureStore method as such: await SecureStore.setItemAsync("user",this.name).
The async method updates the state of the screen, i.e
class Scren extends React.Component
{
constructor()
{
super();
this.state={
data:0,
};
}
async getObject()
{
let result = JSON.parse(await SecureStore.getItemAsync("data");
this.setState({
data: result,
};
}
}

Related

How to pass dynamic data between two screen in React native?

I have referred official documentation of react native(https://reactnavigation.org/docs/params).In that I observed that they are passing the static data between screens. But I want to pass data taken from user.
If someone knows how to share data then please help. I have taken help of context api also but I failed to pass the data. Any source or material will also be helpful.
Your issue may be related to your input. It seems you are not capturing your inputs into a state variable.
Check this example from ReactNative input:
https://reactnative.dev/docs/0.65/textinput
import React from "react";
import { SafeAreaView, StyleSheet, TextInput } from "react-native";
const Screen1 = () => {
const [text, onChangeText] = React.useState("Hello world");
return (
<SafeAreaView>
<TextInput
style={styles.input}
onChangeText={onChangeText}
value={text}
/>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
input: {
height: 40,
margin: 12,
borderWidth: 1,
padding: 10,
},
});
export default Screen1;
Then you could use navigate:
navigation.navigate('Details', {
param1: text,
});
In the other screen you could read the param1 like this:
route.params.param1
Don't forget
Don't forget to pass route as a parameter in fuction( {route) }{ ... }
Checkout this code snippet.
React.useEffect(() => {
if (route.params?.post) {
// Post updated, do something with `route.params.post`
// For example, send the post to the server
}
}, [route.params?.post]);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title="Create post"
onPress={() => navigation.navigate('CreatePost')}
/>
<Text style={{ margin: 10 }}>Post: {route.params?.post}</Text>
</View>
);
}
function CreatePostScreen({ navigation, route }) {
const [postText, setPostText] = React.useState('');
return (
<>
<TextInput
multiline
placeholder="What's on your mind?"
style={{ height: 200, padding: 10, backgroundColor: 'white' }}
value={postText}
onChangeText={setPostText}
/>
<Button
title="Done"
onPress={() => {
// Pass and merge params back to home screen
navigation.navigate({
name: 'Home',
params: { post: postText },
merge: true,
});
}}
/>
</>
);
}
to pass a param to a screen
navigation.navigate('screenName', {
paramName: 'valueYouwantToPass',
});
because you mentioned context API, try
Async Storage
import {AsyncStorage} from 'react-native';
or
Device Event Emitter
import { DeviceEventEmitter} from 'react-native';

React Native not showing the updated array [duplicate]

This question already has answers here:
Firebase data not defined when used inside react useState
(2 answers)
Initiate React components with data from Firebase database?
(1 answer)
Closed 1 year ago.
I used the blow code to get data from the firebase:
import firebase from 'firebase';
import 'firebase/database'
firebase.database().ref().child('users').on('value',(snapshot)=>{
if(snapshot.exists()){
snapshot.forEach((datasnapshot)=>{
data.push({key: datasnapshot.key.toString()})
})
} else {
data.push({key: 'No one has written yet'})
}
});
var data = [];
export default data;
Later i tried to import the variable data to display it as below:
import React from 'react';
import {StyleSheet, Text, StatusBar, View, TouchableOpacity, ScrollView } from 'react-native';
import firebase from 'firebase';
import 'firebase/database'
import data from './getdata';
export default function Index({navigation}) {
return (
<View style={styles.container}>
<Text style={{color: '#000000', fontSize: 30, fontWeight: 'bold', marginTop: StatusBar.currentHeight}}>Index</Text>
<ScrollView>
{data.map((item,key)=>{
if(item.key == "No one has written yet"){return(<Text key={key} style={styles.item}>{item.key}</Text>)}
else{
return(
<TouchableOpacity key={key} onPress={()=>navigation.navigate('Details',item.key)}>
<Text style={styles.item}>{item.key}</Text>
</TouchableOpacity>
)
}
})}
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
item: {
marginTop: 10,
fontSize: 20,
fontWeight: 'bold',
color: '#000000',
textAlign: 'center'
}
});
the problem is it does not show the data stored after the 'data' array is updated by 'push()' function until i save it once again and the code is refreshed in 'expo go' app.
Here is the image of how it is displayed at first when the app is opened:
Click for the image
I think the problem is that your component is not re-rendering after getting data thus the new data is not shown.
I would use Flatlist as it has extraData prop that will make Flatlist re-render when data set to it changes:
"By passing extraData={selected} to FlatList we make sure FlatList itself will re-render when the state changes. Without setting this prop, FlatList would not know it needs to re-render any items because it is a PureComponent and the prop comparison will not show any changes."
https://docs.expo.dev/versions/latest/react-native/flatlist/
So changing your SrollView to...
<FlatList
data={data}
extraData={data}
renderItem={({ item, key }) => {
if(item.key == "No one has written yet"){
return(<Text key={key} style={styles.item}>{item.key}</Text>)
}else{
return(
<TouchableOpacity key={key} onPress={()=>navigation.navigate('Details',item.key)}>
<Text style={styles.item}>{item.key}</Text>
</TouchableOpacity>
)
}}
/>

Why is the react text component not displaying updates to my array state

I am trying to familiarize myself with React Native. At the moment I am working on an app but came across an issue when trying to display changes to the individual elements of an array. For example:
function MyApp() {
const [array, setArray] = useState([1,2,3]);
const onPress = () => {
let temp = [3,2,1];
setArray(temp);
}
return(
<View>
<TouchableHighlight onPress={onPress}>
<View>
<Text>{array[0]}</Text>
</View>
</TouchableHighlight>
</View>
);
}
With the above code, I expect '1' to be displayed in the text component and to change to '3' upon being pressed. console.log shows the state being changed but what is being displayed in the text component inside the actual app never updates. I then tried this using individual integer states like so:
const [int, setInt] = useState(0);
const onPress = () => {
setInt(1);
}
Using an integer state such as the one above works totally fine as expected. Can anyone show me what I am doing wrong with my array state? Thank you.
Your code looks perfect and should work without any issue.
Here is the slightly modified example where the first element is generated randomly and is being shown properly in the Text component.
Working Example: Expo Snack
import React, { useState } from 'react';
import { Text, View, StyleSheet, TouchableHighlight } from 'react-native';
import Constants from 'expo-constants';
export default function MyApp() {
const [array, setArray] = useState([1, 2, 3]);
const onPress = () => {
let temp = [Math.floor(Math.random() * 10), 2, 1];
setArray(temp);
};
return (
<View style={styles.container}>
<TouchableHighlight onPress={onPress}>
<View style={styles.btn}>
<Text
style={{ alignSelf: 'center', fontSize: 28, fontWeight: 'bold' }}>
{array[0]}
</Text>
</View>
</TouchableHighlight>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
btn: {
width: 100,
height: 100,
borderColor: 'purple',
borderWidth: 5,
justifyContent: 'center',
},
});
Your code looks to be fine, I tried the below following your code and can see that state and UI getting updated successfully when the button is being clicked.
Could you please check if your event handler function onPress is getting called, and if you are getting any error in the console when you click on it.
function App() {
const [array, setArray] = React.useState([1, 2, 3]);
const handleBtnClick = () => {
const temp = [3, 2, 1];
setArray(temp);
};
return (
<React.Fragment>
<button onClick={handleBtnClick}>Click</button>
{array.map((el) => (
<p>{el}</p>
))}
<hr />
{array[0]}
</React.Fragment>
);
}

react native - Passing navigation props from parent to child

I'm trying to create a simple-ish mobile app, but I'm pretty new to this. I've spent some time searching about the errors I'm getting. It seems like a common problem, but they all had the same/similar solutions but the solutions didn't work for me.
What is I'm trying to do? Right now the app is two pages, the home screen (Overview Cards) and the Add Card screen.
There's a button on the Overview Cards that takes you to Add Card.
Add Card allows you to fill out some TextInput boxes and
Add Card should allow you to press the save button and be taken back to the Overview Card screen and see the data you entered in the form.
However, I am getting stuck at Step 3. I am trying to make the Save button navigate the user back to Overview Cards, but there are simply errors instead.
Below is my code, the errors I'm getting, and then what I've tried.
App.js
import React from 'react';
import { StyleSheet, Text, TextInput, View, Button, TouchableOpacity, ShadowPropTypesIOS } from 'react-native';
import AddCard from './components/AddCard.js';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { useNavigation } from '#react-navigation/native';
function HomeScreen({ navigation }) {
return (
<View style={styles.homeContainer}>
<Button title="Add Card" onPress={() => navigation.navigate('Add Card')}/>
{/* <Text value={this.props.inputValFT}/> */}
<Text style={styles.textStyle} >VISA xxxx</Text>
<Text style={styles.textStyle}>MASTERCARD xxxx</Text>
<Text style={styles.textStyle}>AMEX xxxx</Text>
</View>
);
}
function AddCardScreen() {
return (
<View style={styles.addCardContainer}>
<AddCard navigation={this.props.navigation} /> // **HERE**
</View>
);
}
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} options={{ title: 'Overview Cards' }} />
<Stack.Screen name="Add Card" component={AddCardScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
// function AddCardButton(){
// return (
// <View style={styles.buttonContainer}>
// <TouchableOpacity>
// <Text style={styles.button}>Add Card</Text>
// </TouchableOpacity>
// </View>
// );
// }
export default App;
const styles = StyleSheet.create({
homeContainer: {
flex: 1,
backgroundColor: '#ef95b1',
alignItems: 'center',
justifyContent: 'flex-start',
},
addCardContainer: {
flex: 1,
backgroundColor: '#28cdf0',
justifyContent: 'flex-start',
},
buttonContainer: {
flexDirection: 'row',
alignSelf: 'flex-end',
marginTop: 15,
},
button: {
flexDirection: 'row',
alignSelf: 'flex-end',
marginTop: 15,
right: 10,
backgroundColor: '#2565ae',
borderWidth: 1,
borderRadius: 12,
color: 'white',
fontSize: 15,
fontWeight: 'bold',
overflow: 'hidden',
padding: 10,
textAlign:'center',
},
textStyle: {
padding: 10,
}
});
Navigation.js
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import AddCardScreen from './AddCard';
const RootStack = createStackNavigator(
{
Home: HomeScreen,
AddCard: AddCardScreen,
},
{
initialRouteName: 'Home',
}
);
const AppContainer = createAppContainer(RootStack);
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#ef95b1',
alignItems: 'center',
justifyContent: 'flex-start',
},
textStyle: {
padding: 10,
}
});
export default createAppContainer(Navigation);
AddCard.js
import React, { Component } from 'react';
import { StyleSheet, View, Text, TextInput, TouchableOpacity } from 'react-native';
import { Input } from 'react-native-elements'
import { ScrollView } from 'react-native-gesture-handler';
// import { loadSettings, saveSettings } from '../storage/settingsStorage';
class AddCardScreen extends Component {
constructor(props) {
super(props);
this.state = {
firstTwo : '',
lastFour : '',
recentAmt : ''
};
this.addFT = this.addFT.bind(this)
this.addLF = this.addLF.bind(this)
this.addRecAmt = this.addRecAmt.bind(this)
}
static navigationOptions = {
title: 'Add Card'
};
addFT(firstTwo) {
this.setState(Object.assign({}, this.state.firstTwo, { firstTwo }));
}
addLF(lastFour) {
this.setState(Object.assign({}, this.state.lastFour, { lastFour }));
}
addRecAmt(recentAmt) {
this.setState(Object.assign({}, this.state.recentAmt, { recentAmt }));
}
// handleSubmit() {
// alert('New card saved. Returning to Home to view addition.');
// navigation.navigate('Home')
// } // firstTwo, lastFour, recentAmt
render() {
const {navigation} = this.props;
return (
<ScrollView>
<View style={styles.inputContainer}>
<Text h1> "Add a new card!" </Text>
<TextInput
style={styles.textInput}
placeholder="First two digits of card"
placeholderTextColor="#000000"
keyboardType={'number-pad'}
maxLength = {2}
onChangeText={this.addFT}
inputValFT={this.state.firstTwo}
/>
<TextInput
style={styles.textInput}
placeholder="Last four digits of card"
placeholderTextColor="#000000"
keyboardType={'number-pad'}
maxLength = {4}
onChangeText={this.addLF}
inputValLF={this.state.lastFour}
/>
<TextInput
style={styles.textInput}
placeholder="Most recent dollar amount"
placeholderTextColor="#000000"
keyboardType={'decimal-pad'}
onChangeText={this.addRecAmt}
inputValRA={this.state.recentAmt}
/>
</View>
<View style={styles.inputContainer}>
<TouchableOpacity
style={styles.saveButton}
onPress={() => navigation.navigate('Home')}> // ** HERE 2 **
<Text style={styles.saveButtonText}>Save</Text>
</TouchableOpacity>
</View>
</ScrollView>
);
}
}
// this.handleSubmit.bind(this)
export default AddCardScreen;
const styles = StyleSheet.create({
inputContainer: {
paddingTop: 15
},
textInput: {
borderColor: '#FFFFFF',
textAlign: 'center',
borderTopWidth: 1,
borderBottomWidth: 1,
height: 50,
fontSize: 17,
paddingLeft: 20,
paddingRight: 20
},
saveButton: {
borderWidth: 1,
borderColor: '#007BFF',
backgroundColor: '#007BFF',
padding: 15,
margin: 5
},
saveButtonText: {
color: '#FFFFFF',
fontSize: 20,
textAlign: 'center'
}
});
The errors I'm getting:
In App.js you can see the ** HERE ** that I put in. When I try to run this, the app loads fine until I click the "Add Card" button. I get this error: undefined is not an object (evaluating 'this.props.navigation').
If I take the navigate={this.props.navigation} part out from App.js, the app loads as it's meant to again, but this time I can click the "Add Card" button and reach the next screen with no issue. I fill out the form (TextInput parts in AddCard.js), but when I click the "Save" button, the app crashes. The error is: TypeError: undefined is not an object (evaluating 'navigation.navigate'). Most likely because of what I'm doing with onPress where it says ** HERE 2 ** in AddCard.js. handleSubmit() is currently commented out, but it used to be inside the onPress.
What I've tried:
Some of the answers I saw were that I need to pass in navigation from the parent to the child and that will make it work. By trying that, I get the errors I mentioned earlier. I also saw that someone mentioned using "withNavigation" or "useNavigation" which was supposed to allow the child to access navigation, but that didn't work for me either. Below are some of the links that I was trying to follow.
How do I pass navigation props from parent component to header?
Pass navigation.navigate to Child Component
https://reactnavigation.org/docs/use-navigation/
Thank you for reading, hopefully my explanation is clear enough.
I think your problem is somewhere here:
function AddCardScreen({ navigation }) {
return (
<View style={styles.addCardContainer}>
<AddCard navigation={navigation} />
</View>
);
}
There is no this, you're not in a class component, therefore this doesn't exists
The prop you are trying to pass should be called navigation and not navigate, since that's how you try to access it in the child component.
The navigation prop needs to be destructured inside the function argument function AddCardScreen({ navigation }), same as you already do for HomeScreen.

React Native: Single View with Dynamic Content

I'm learning to programming in React-Native (and also in Javascript) and I have a question.
Basically, I have created a Login for 2 categories of users: "Regular" and "Plus".
After the login they are redirect to an HomeUser page.
My problem is that in this "HomeUser" page I should create dynamic Content depending on the type of user.
This is the HomeUser
class HomeUser extends Component {
constructor(props){
super(props);
}
render() {
const FirstName = global.utente.data.Person.FirstName;
const LastName = global.utente.data.Person.LastName;
const Username = global.utente.data.Person.FiscalCode;
const Roles = global.utente.data.Roles
console.log(Roles)
return (
<View style={style.container}>
<View style={style.page}>
<Icon name="user-circle" color="#64c7c0" size={70} onPress={() => Actions.viewprofile()} />
<Text style={{paddingBottom: 15, textAlign: 'center', fontSize: 15, color: '#64c7c0', fontWeight: 'bold'}}>View Profile</Text>
<Text style={{textAlign: 'center', fontSize: 20,}}>{"Welcome"}</Text>
<Text style={{textAlign: 'center', fontSize: 20, color: '#64c7c0', fontWeight: 'bold'}}>{FirstName} {LastName}</Text>
<Text>{Roles}</Text>
//Add here "EDIT PROFILE" button to link a the page to edit profile, only for Regular User.
</View>
</View>
)
}
}
export default HomeUser;
So I should insert content dynamically for the users. Can you explain how can I do for example to the edit profile? I should create a new page and link to this page with a If condition? ( I don't think so xD )
My thought is that if I have to do more checks on the role, the efficiency of the application slows down. Is it possible that this problem occurs?
If i understand your question correctly you want to render a component when the user role is a specific one.
In this case you can use conditional rendering using:
{condition && <Component>}
inside you render return function.
In your code something like:
{Roles==="SpecificRole"&&<Button></Button>}
should do the trick
First Edit profile when you login success you can save user fix information on local storage then you can open new page name UserEditProfile it's a good way for efficiency.
If wanna show 1 page 2 different role stuff component you must create 2 component like that
//it's different .jsx file
<RegularUserComponent /*you can add props here*/ />
<SpecificRoleUserComponent />
then you can call like that
import RegularUserComponent from './components/RegularUserComponent.js'
import SpecificRoleUserComponent from './components/RegularUserComponent.js';
and use like that
// in render
{Roles==="Reqular" ? <RegularUserComponent/> :<SpecificRoleUserComponent/> }
localstorage about the information you must check this link
and An Example for a compornt
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View,Button,Image} from 'react-native';
export default class NameOfYouComponent extends Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
const {RequestResponse} = this.state;
return (
<View style={styles.container}>
{/*Here is Component include*/}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
you can call like that
import NameOfYouComponent from './../afolder/Component/NameOfYouComponent'

Categories