AsyncStorage issue in react native expo - react-native

I can't even tell you how many variations I have tried, tutorials and documentations that I have watched and read, I cannot transfer data from one page to another. Am using react native expo. I have this included in both pages: import AsyncStorage from '#react-native-async-storage/async-storage';.
This is the page I'm trying to set the data:
const ToyDetails = () => {
const [savedName, setSavedName] = useState('')
const addCart = async() => {
setButtonText('ADDED TO CART!')
try {
await AsyncStorage.setItem('saved_name', savedName)
}catch(error){
console.log(error)
}
}
return(
<View>
<Text value={savedName}>{name}</Text>
#{name} is because I am importing the name from a FlatList item
</View>
)
}
And getting that data from another page:
const Cart = () => {
const [savedName, setSavedName] = useState('')
useEffect(()=>{
getData()
}, [])
const getData = () => {
try {
AsyncStorage.getItem('saved_name')
.then((value)=>{
if(value!=null){
setSavedName(value)
}
})
}catch(error){
console.log(error)
}
}
return (
<View>
<Text value={savedName} onChangeText={(value)=>setSavedName(value)}>{savedName}</Text>
</View>
)
}
I can post other variations I have tried if asked, I've tried adding it into a list and importing the list in the second page, I've tried to JSON.stringify the value savedName first (and JSON.parse it), I even tried doing it in the same way I did for FlatList. I'm not even getting any error messages.

in your ToyDetails.js while saving savedName is empty. i changed to name and able to get it on CartScreen
https://snack.expo.dev/7ozHrsOBT check ToyDetails.j file
const addCart = async() => {
setButtonText('ADDED TO CART!')
try {
console.log("savedName",savedName) //saved name is empty here
await AsyncStorage.setItem('saved_name', name)
}catch(error){
console.log('setitem didnt work')
}
}

Related

React Native component not re-rendering when state is updated

I have a component in my React Native app that displays a list of pending friends. This component makes a GET request to an API to retrieve the list of pending friends and then uses a useEffect hook to map over the list and render each friend as a Pressable component. I'm also using the useFocusEffect hook to make the get request when the screen renders.
Here is the relevant code for the component:
const Pending = () => {
const [pendingFriends, setPendingFriends] = useState(null)
let pendingFriendsRender = []
useEffect(() => {
if (pendingFriends !== null) {
for(let i = 0; i < pendingFriends.length; i++) {
pendingFriendsRender.push(
<Pressable key={i} style={styles.friend}>
<Text style={styles.friendText}>{pendingFriends[i].username}</Text>
</Pressable>
)
}
}
}, [pendingFriends])
useFocusEffect(
useCallback(() => {
async function fetchData() {
const accessToken = await AsyncStorage.getItem('accessToken')
try {
const res = await instance.get('/pending_friends', {
headers: { authorization: `Bearer ${accessToken}`},
})
setPendingFriends(res.data)
} catch (error) {
console.log(error.response.status)
}
}
fetchData()
}, [])
)
return(
<View style={styles.friendsContainer}>
{pendingFriendsRender}
</View>
)
}
I have tried using an empty array as the second argument in the useEffect hook but that approach has not worked. I also tried removing the useEffect hook so the if statement with the for loop stands at the top of the component without the hook, that worked but I can't update it in this way after the component rendered. I checked the API and it is returning the correct data.
The first useEffect you have really isn't needed. You can map through your state inside of your JSX. Anytime the state changes, the component will be re-rendered:
// Need a default here, could also set some loading state when fetching your data
if(pendingFriends === null) {
return <>Loading...</>
}
return(
<View style={styles.friendsContainer}>
{pendingFriends.map((friend, i) => {
return (
<Pressable key={friend.id} style={styles.friend}>
<Text style={styles.friendText}>{friend.username}</Text>
</Pressable>
)
})}
</View>
)
Also keep in mind, it's not recommended to use the index as the key, it can lead to unexpected bugs and issues. Instead use a unique string key (as shown above).
React: using index as key for items in the list
pendingFriendsRender should be the state:
const [pendingFriendsRender, setPendingFriendsRender] = useState([])
Instead of
let pendingFriendsRender = []
Then just clone the array so you lose reference to the object and add the new element
const newPendingFriendsRender = [...pendingFriendsRender, newElement]
or you can use FlatList to make it easier.

react native restart application

I created a barcode scanner App using expo-barcode-scanner.
I have some problems.
The purpose of the scanner is to get the barcode number and send it to barcode.monster and get product details. It works, but I have two main problems which I dont know what should I look for and how to resolve.
After the scanner get a barcode, I want to send to a confirmation screen, where the User should add the product into a category.
const handleBarCodeScanned = ({ type, data }) => {
reqForProduct(data);
setScanned(true);
setText(data);
navigation.navigate('Confirmation');
};
The function above is executed when the barcode camera find a number.
const reqForProduct = async barcode => {
try {
const Product = await axios.get(`https://barcode.monster/api/${barcode}`);
console.log(Product.data);
} catch (error) {
console.log(error);
}
}
The function above is responsible to get the product data.
THE NAVIGATION WORKS, BUT IF I PRESS THE BACK BUTTON AFTER THE FUNCTION SEND ME TO THE CONFIRMATION SCREEN, I CANNOT RESCAN OTHER BARCODE UNLESS I PRESS R (RELOAD) IN THE CONSOLE... THIS IS MY FIRST PROBLEM. Moreover, after coming back to the screen, the console is stucked with the last product fetched from the api.
The second problem is is to transfer the data fetched to the confirmation screen. I tried with the navigation prop like navigation.navigate('Confirmation', {fetchedDataObj} but is not working....
<Stack.Screen
name='Confirmation'
component={AddToContainerScreen} />
THE FULL PAGE CODE BELLOW ----------------------------------------------------
import {View, Text, Button, StyleSheet} from 'react-native';
import {useState, useEffect} from 'react';
import { BarCodeScanner } from 'expo-barcode-scanner';
import axios from 'axios';
const Scanner = ({navigation}) => {
const [permission, setPermission] = useState(null);
const [scanned, setScanned] = useState(false);
const [text, setText] = useState('');
const permissionCamera = () => {
( async () => {
const {status} = await BarCodeScanner.requestPermissionsAsync();
setPermission(status == 'granted');
})()
}
const reqForProduct = async barcode => {
try {
const Product = await axios.get(`https://barcode.monster/api/${barcode}`);
console.log(Product.data);
} catch (error) {
console.log(error);
}
}
// Execute permission
useEffect(() => {
permissionCamera();
}, []);
const handleBarCodeScanned = ({ type, data }) => {
reqForProduct(data);
setScanned(true);
setText(data);
navigation.navigate('Confirmation');
};
if (!permission) {
return (
<View>
<Text>Requesting camera permission</Text>
</View>
)
}
return (
<View style={styles.wrapper}>
<BarCodeScanner
style={StyleSheet.absoluteFill}
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
/>
</View>
)
};
const styles = StyleSheet.create({
wrapper: {
flex: 1
}
})
export default Scanner;
Can someone please help me?
BTW THE PRODUCT DATA FROM tHE API COMES SLOWeR THAN the APP MOVES TO THE CONFIRMATION SCREEN...
Problem 1: I think you need to reinitialize it on a focus even listener or something.
useEffect(() => {
permissionCamera();
}, []);
since useEffect() is basically a componentOnMount and it only fires the first time you load the page. When you navigate back this is not gonna fire. Please check if this is the case. You can do a simple log to confirm this.
For the 2nd problem, I can't help you much since there is only very little data. If you really need help, you could dm me on skype. I'll be glad to help you out.

React Native Testing Library get by access role

I am really newReact Native Testing Library. My app basically works like this: it fetched the data and display on my to my as Text format, I used jsonplace holder api. This is app-demo. I have have created one Text where I define test role="header". I want to test the Text, does it work properly under role="header". I make a fake data and try to test it. I can able target the role from the component but I don't how to get the expected data. I tried with toBe, getByText but each time I am getting error: TypeError: toBe is not function.
This is my app component
const [state, setState] = React.useState([]);
React.useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/todos/")
.then((response) => response.json())
.then((json) => setState(json));
}, []);
return (
<View style={styles.container}>
{state.map((i) => (
<Text role:'header'>{i.title}</Text>
))}
</View>
);
}
This is my test suite
import React from 'react';
import { fireEvent, render, cleanup, act } from '#testing-library/react-native';
import Json from './Json';
describe('<Json/>', () => {
afterEach(cleanup);
test('get data properly', async () => {
const component = <Json/>;
const { getByA11yRole } = render(component);
const header = await getByA11yRole('header');
console.log(header);
expect(header).toBe(/delectus aut autem/);
});
});

Render Data in Cloud Firestore with a ReactJS Hook

How can I render data retrieved from Cloud Firestore using ReactJS native functional component?
I want to do something like this:
const result=[]
db.collection("collection").where("parameter", "==", "value")
.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
result.push(doc.data());
});
export default function foo(){
return(
<View>
{result.map((item) =>
<CustomComponent data=item />
)}
</View>
)
}
Obviously the code does not work because the rendering happens before the promise resolving. I have read a lot on the internet. A possible solution is to update the state in the ComponentDidMount, in which the request to db happens.
So my question is, how can I do it with a functional component? Is this the only and best solution? The data which I want to retrieve from Firestore and I want to display not change so fast.
Hers is a code snippet hope it works for you....
const useData = () => {
const [list, setList] = useState([]);
const [loading, setLoading] = useState(false);
const getData = () => {
const data = [];
db.collection('collection')
.where('parameter', '==', 'value')
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
data.push(doc.data());
});
setList(data);
});
};
return [loading, list, getData];
};
export default function App() {
const [loading, list, getData] = useData();
useEffect(() => {
getData();
}, []);
return (
<View style={styles.container}>
{loading ? (
<ActivityIndicator />
) : (
{
// renderList
}
)}
</View>
);
}
It might be because you are trying to render an array of data. I suggest taking a look at this good example here. I have added a few examples of ReactJS map that you might find helpful:
render() {
return (<div>
{this.state.people.map((person, index) => (
<p key={index}>Hello, {person.name} from {person.country}!</p>
))}
</div>);
}
this.state.data.map(function(item, i){
console.log('test');
return <li key={i}>Test</li>
})
You might find these tutorials and userguides helpful:
Getting started with Cloud Firestore and React Native.
Create React Native Firebase CRUD App with Firestore.

How to display data from a firebase firestore in React Native

For the Life of me, I cannot get this to render the data that I pull from the database.
Here is the code
function assetList(){
const [assetL, setAssetL] = useState([]);
const [errors, setErrors] = useState("");
const getAssets = async () =>{
try{
const list = [];
console.log("Break");
db.collection("assets").get().then(function(querySnapshot){
querySnapshot.forEach(function(doc){
list.push(doc.data());
});
});
//problem
setAssetL([...list]);
//problem
console.log("list");
console.log(list);
console.log("AssetL");
console.log
} catch (e) {
setErrors("Failed To Load Data");
}
};
useEffect(() => {
getAssets();
}, []);
console.log(assetL);
return(
<SafeAreaView style = {styles.Heading}>
<View style = {styles.Heading}>
<Text style = {styles.headText}>Asset List</Text>
</View>
<FlatList
data = {assetL}
renderItem={({item}) => <Text>{item.default}</Text>}
/>
</SafeAreaView>
);
}
I have narrowed down to at least the most pressing issue, other than my jank way of throwing this page of the app together, is that the setAssetL isnt actually setting the assetL const. Can anyone explain why this is happening and how to fix it?
For the getAssets function do something like this:
const getAssets = async () =>{
try {
const list = [];
console.log("Break");
db.collection("assets").get().then(function(querySnapshot){
querySnapshot.forEach(function(doc){
list.push(doc.data());
});
setAssetL(list);
});
...
} catch (e) {
setErrors("Failed To Load Data");
}
};
Your code did not work because db.collection("assets").get() function returns a promise and you handle it asynchronously while expecting it to be synchronous.
Here you can read more about async functions