I'm developing application about travelling, so I have a map with places to travel and I want to implement adding to favourites functionality. When the user clicks on the marker on the map I show him modal window with all the information about the place and there I have icon-button "add-to-favorites". When user clicked on this "add-to-favourites" icon-button I want to save the place to the AsyncStorage, rerender icon to "remove-from-favourites" then if the place in favouries and user clicks on "remove-from-favorites" then remove place from AsyncStorage and rerender icon the add-to-favorites. I don't really understand how to do it in a right way in functional component. Give me an example please.
What I tried:
My ModalWindow code:
const [favourite, setFavourite] = useState(false);
const onHeartPress = async () => {
setFavourite(prev => !prev);
await storage.save({
key: 'markers',
data: {
marker: marker
},
expires: null
})
<TouchableOpacity onPress={() => onHeartPress()}>
<View style={{flexDirection: "row", alignItems: "center"}}>
{favourite ? <Ionicons size={height/20} name={'heart-dislike'} /> : <Ionicons size={height/20} name={'heart'} />}
</View>
</TouchableOpacity>
It's not difficult to understand how to add to the storage and then load from the storage but how to render the icon correctly like heart/dislike heart because now with this code when I press on button icon in one place in the other places it also changes, because favourite becomes true so I want to understand how in the correct way change icon only for place where user clicked on icon.
You can follow this code to achieve this functionality:
const STORAGE_KEY = '#icon_name'
// Store data
const saveData = async () => {
try {
await AsyncStorage.setItem(STORAGE_KEY, age)
alert('Data successfully saved')
} catch (e) {
alert('Failed to save the data to the storage')
}
}
// fetch data
const readData = async () => {
try {
const userAge = await AsyncStorage.getItem(STORAGE_KEY)
if (userAge !== null) {
setAge(userAge)
}
} catch (e) {
alert('Failed to fetch the data from storage')
}
}
//remove data
try {
await AsyncStorage.removeItem(STORAGE_KEY);
console.log('Data removed')
}
catch(exception) {
console.log(exception)
}
Related
I'm trying to open a simple page with React Native WebView.
It's a single page web, and when you do a search, it prints out some information about your search.
After that, if you want to search again, press the back button on the device to move to the search box.
Because it is a single page, I cannot use goBack, so I created a function called cancel.
The problem is that when I click the device's back button, the function called cancel defined on the web is not executed.
The cancel function deletes the searched information and returns to the search window.
I will upload my code.
Please advise.
export default function App() {
const webviewRef = useRef(null);
const backAction = () => {
setBackTapping((prev) => prev += 1);
webviewRef.current.injectJavaScript('window.cancel()')
return true;
}
useEffect(() => {
const timer = setInterval(() => {
setBackTapping(0)
}, 1000)
return () => clearInterval(timer);
}, [])
useEffect(() => {
const backHandler = BackHandler.addEventListener('hardwareBackPress',backAction);
return () => backHandler.remove()
}, [])
useEffect(() => {
if(backTapping >= 2){
return BackHandler.exitApp();
}
},[backTapping])
return (
<KeyboardAvoidingView style={{ flex: 1 }}>
<StatusBar hidden />
<WebView
ref={webviewRef}
textZoom={100}
originWhitelist={['*']}
javaScriptEnabled
source={{ uri: 'myhome.com'}}
startInLoadingState={true}
/>
</KeyboardAvoidingView>
);
}
Expected behavior:
The cancel function is executed, all open windows are closed, and you are returned to the search window.
in my case, calling is wrong.
instead of :
webviewRef.current.injectJavaScript('window.cancel()')
use :
const generateOnMessageFunction = (data) => `
(function(){
window.dispatchEvent(new MessageEvent('message',{data: ${JSON.stringify(data)}}));
})()
`;
webviewRef.current.injectJavaScript(generateOnMessageFunction('cancel'));
detail referance :
https://github.com/react-native-webview/react-native-webview/issues/809
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')
}
}
I have two screens. Approve List and Approve Detail. When data approved in Approve Detail, page navigate to Approve List. Then approved data should disapear from FLatList. How to remove FlatList item when data approved? or how to re render FlatList when data change? Here is my code:
Approve List:
const Approve = ({ navigation }) => {
const [rekomendasi, setRekomendasi] = useState({})
// other code
const getRekomendasi = async (token, bagian) => {
try {
const response = await sippApi.get(`/penjaminan?bagian=${bagian}`, {
headers: {
Auth: token
}
});
setRekomendasi(response.data.data)
console.log(rekomendasi)
} catch (error) {
console.log(error)
}
}
useEffect(() => {
getToken();
getUserData()
getRekomendasi(token, userData.bagian);
}, [setToken, setUserData, rekomendasi]); // if I pass rekomendasi here, make infinite loop on api request
return (
<FlatList
onRefresh={() => onRefresh()}
refreshing={isFetching}
removeClippedSubviews
style={{ marginTop: 2 }}
data={rekomendasi}
keyExtractor={rekom => rekom.penjaminan.nomor_rekomendasi}
renderItem={({ item }) => {
return (
<TouchableOpacity onPress={() => navigation.navigate("ApproveDetail", { id: item.penjaminan.nomor_rekomendasi, bagian: userData.bagian })}>
<ApproveList
plafond={item.value}
kantor={item.nama_kantor}
nomor_rekomendasi={item.nomor_rekomendasi}
produk={item.skim}
/>
</TouchableOpacity>
)
}}
showsHorizontalScrollIndicator={false}
/>
)
}
If I pass value on second argument on UseEffect, it cause infinite loop on API request. If not, my FlatList cant re render when data change. What should I do?
Thanks for help
You have to remove the rekomendasi dependency in the useEffect to avoid infinite loop, it's only for init data :)
What is the purpose of onRefresh function in the FlatList ? Instead you could put the getRekomendasi function to trigger a new call and your data will be updated
try to separate the functions to two useEffects
useEffect(() => {
//<-- write your getToken() and getUserDate() here
getToken();
getUserData()
}, []);
useEffect(() => {
const getRekomendasi = async (token, bagian) => {
try {
const response = await sippApi.get(`/penjaminan?bagian=${bagian}`, {
headers: {
Auth: token
}
});
setRekomendasi(response.data.data)
console.log(rekomendasi)
} catch (error) {
console.log(error)
}
}
getRekomendasi(token, userData.bagian);
},[token,userData.bagian]);
Problem solved by using useFocusEffect
useFocusEffect(
React.useCallback(() => {
getRekomendasi(token, userData.bagian)
}, [token, userData.bagian])
);
I am trying to pass the selected data i'm fetching from my rest api back to the previous screen. I followed the docs, but it's not working. Not sure what I am doing wrong.
I am updating the state w/ the selected data, and know it's working bc i am printing it out in console successfully.
This is the button that calls the function to navigate back, passing the state w/ selected data in Screen B:
<Pressable onPress={() => postSelected(selectedData)} >
<Text style={{fontSize: 13, color: 'white', fontWeight: '700', paddingRight: 5}}>{'Select'}</Text>
</Pressable>
This is the function (Screen B):
const postSelected = (selectedData) => {
navigation.navigate({
name: 'CreatePost',
params: { postData: selectedData },
merge: true
});
}
In Screen A, I have a useEffect that listens for the selected data from Screen B:
useEffect(() => {
if (route.params?.postData) {
console.log('Sent');
}
}, [route.params?.postData]);
But it's not receiving the data.
I was following these docs: https://reactnavigation.org/docs/params/
Appreciate any help!
You can use props.navigation.getParam("postData") Method to get params from navigation
The solution that worked for me and follows the best practices set by react-navigation is as follows
const MainScreen = ({ navigation }) => {
// Here is where the returned data is detected
React.useEffect(() => {
if (route.params?.post) {
// Do something with the data
}
}, [route.params?.post])
const openChildScreen = () => {
navigation.navigate('NewPostScreen')
}
}
// ---------------------
const NewPostScreen = ({ navigation }) => {
const goBackToMain = () => {
// This is the data to be returned to the main screen
const post = {
// ...
}
navigation.navigate('MainScreen', { post });
}
}
First of all,i tried to make a watchlist for movies,a page that contains movies that you add.I can't get understand async storage and how it works within my code.
I've tried different tutorials,but I still don't get to make it work.I tried with the code that official react-native page has on their site,but with no results.
item2ADD = this.props.navigation.getParam('movie');
_retrieveData = async () => {
try {
await AsyncStorage.setItem('#MySuperStore:key', 'I like to save it.');
} catch (error) {
console.warn("data has not been saved");
}
try {
const value = await AsyncStorage.getItem('TASKS');
if (value !== null) {
console.warn("data has been loaded");
console.warn(value);
}
} catch (error) {
console.warn("data has not been loaded");
}
};
constructor(props) {
super(props);
this.state = {
data : [] ,
};
}
async componentDidMount() {
const data = this._retrieveData();
this.setState({ data });
}
render() {
if(this.state.data!==null) {
return (
<View style={styles.container}>
<Text style={styles.title}>Watchlist</Text>
<TouchableOpacity style={styles.backBtn} onPress={() => {
this.props.navigation.goBack()
}}>
<Image style={styles.backIMG} source={menuImages.back}/>
</TouchableOpacity>
<FlatList
data={this.state.data}
numColumns={2}
renderItem={({item}) =>
<View style={styles.fList}>
<TouchableOpacity onPress={() => this.props.navigation.navigate('Details', {movie: item})}>
<Image style={styles.img} source={{uri: item.coverUrl}}/>
</TouchableOpacity>
<Text style={styles.movieTitle}>{item.title}</Text>
<Text
style={styles.movieDate}>{moment.unix(Math.floor(parseInt(item.releaseDate)) / 1000).format("DD/MM/YYYY")}</Text>
</View>
} keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
else
{
return <Text>FAILED TO LOAD</Text>
}
}
}
I just want to know how can I implement AsyncStorage to store my added movies from the item2ADD,it contains only 1 item that i need to store in order to make a watchlist.(the item which contains all details that i need for a movie).
I want to save all the movies with asyncstorage then show them in that flatlist.
I keep getting :
invariant violation tried to get frame for out of range index nan(on android simulator) when i click to add a movie to the list.
I'm not sure what is the problem you are asking but there is a few things that maybe causing the problem.
1- In your function _retrieveData you don't return anything from it.
I'm not sure what you want to return but what you could do is return the value from the AsyncStorage.
_retrieveData = async () => {
try {
await AsyncStorage.setItem('#MySuperStore:key', 'I like to save it.');
} catch (error) {
console.warn("data has not been saved");
}
try {
const value = await AsyncStorage.getItem('TASKS');
if (value !== null) {
console.warn("data has been loaded");
console.warn(value);
}
// returning the value.
return value
} catch (error) {
console.warn("data has not been loaded");
}
};
2- You are setting state with the data from _retrieveData without waiting for it.
In you componentDidMount you set the data from _retrieveData but because it's a promise, you need to use await to get the data
async componentDidMount() {
// added await
const data = await this._retrieveData();
this.setState({ data });
}
3- The data you provide to FlatList needs to be an array.
When you call pass data={this.state.data} to FlatList, it needs to be an array, if it's not an array, you will have several problems.
Make sure that when you return the value from _retrieveData, it's an array.
// value needs to be an array
const value = await AsyncStorage.getItem('TASKS');
You should also notice that to setItem, it need to be parsed to a json, this means doing something like AsyncStorage.setItem('NAME_OF_YOUR_KEY', JSON.stringify(DATA_YOU_WANT_TO_STORE); and to getItem it need to be parsed to a javascript object, this means doing something like
const jsonValue = await AsyncStorage.getItem('NAME_OF_YOUR_KEY');
const value = JSON.parse(jsonValue);