Async storage doesnt display when i reload - react-native

I am VERY VERY new to React Native. i have never posted on a forum, sorry if formatting is incorrect. Please bear with me. When i store a value using Async, i write to the console, so i know it is storing the value, then i read back the value and i know it is there when i read from the console but it wont set my radio button to the value stored using Async. The first line of these 4 does NOT work even though i KNOW the data is set but the 3 commented out lines each work fine if i test them separately but it is the first line i want to work:
const [checked, setChecked] = useState(getData());
// const [checked, setChecked] = useState('first');
//const [checked, setChecked] = useState('second');
//const [checked, setChecked] = useState(params.startvalue);
Here is full code:
import React, { useState , useEffect} from 'react';
import { Text, View, StyleSheet, Button } from 'react-native';
import Constants from 'expo-constants';
import AsyncStorage from '#react-native-async-storage/async-storage';
// or any pure javascript modules available in npm
import { RadioButton } from 'react-native-paper';
const storeData = async (value) => {
try {
console.log("in storeData value is-->" + value.toString());
await AsyncStorage.setItem('#storage_Key', value.toString())
} catch (e) {
console.log("didntwork in StoreData");
}
}
const getData = async () => {
try {
const value = await AsyncStorage.getItem('#storage_Key');
console.log("value in getData is:", value.toString());
return value != null ? value.toString() : null;
} catch (e) {
console.log("error in getData", { e });
}
}
const MyRadioButton = params => {
const [checked, setChecked] = useState(getData());
// const [checked, setChecked] = useState('first');
//const [checked, setChecked] = useState('second');
//const [checked, setChecked] = useState(params.startvalue);
return (
<View>
<Text>{params.startvalue}</Text>
<Text>{params.message1}
<RadioButton
value="first"
status={checked == 'first' ? 'checked' : 'unchecked'}
onPress={() => {
setChecked('first');
params.setxDisabled(true);
storeData('first');
}
}
/></Text>
<Text>{params.message2}
<RadioButton
value="second"
status={checked == 'second' ? 'checked' : 'unchecked'}
onPress={() => {
setChecked('second');
params.setxDisabled(false);
storeData('second');
}
}
/></Text>
</View>
);
};
export default function App() {
const [xdisabled, setxDisabled] = useState(true);
return (
<View style={styles.container}>
<Text style={styles.auto} >Welcome </Text>
<Text style={styles.auto} >Blah Blahhhh</Text>
<MyRadioButton message1="I do NOT agree to the terms" message2="I agree to the terms" setxDisabled={setxDisabled} startvalue="first" />
<Button title="continue" disabled={xdisabled} onPress={() => navigation.navigate('Home')} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});

Okay so if I understand you correctly you want to load the current state from AsyncStorage. You are never actually getting the value from AsyncStorage on loading the page because it is an async function. You need to await the return value.
I recommend using useEffect() for this task to immitate a component mount. You should import it from from react.
Try something like this:
const [checked, setChecked] = useState('default value');
useEffect(() => {
const asyncWrap = async () => {
const value = await getData();
setChecked(value);
}
asyncWrap() // you can't make async calls directly in useEffect
}, []); // <-- empty [] is very important!
You can now use the state as usual.

Related

Customize Action Sheet Message (Expo)

I'm using #expo/react-native-action-sheet, and i want to show a button in the props message.
e.g
import { useActionSheet } from "#expo/react-native-action-sheet"
const { showActionSheetWithOptions } = useActionSheet()
const onPress = () => {
// Here
**const message = '<TouchableOpacity><Text>fsdf</Text></TouchableOpacity>'**
showActionSheetWithOptions(
{
message
},
(buttonIndex) => {
}
)
}
But it is not showing the button as i want
My purpose is to add a date picker in the action sheet.
Expecting answer:
In this case, you can use another library https://gorhom.github.io/react-native-bottom-sheet/ because Action Sheet is about the list of actions.
You can place any content you need for react-native-bottom-sheet and it also supports Expo
import React, { useCallback, useMemo, useRef } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import BottomSheet from '#gorhom/bottom-sheet';
const App = () => {
// ref
const bottomSheetRef = useRef<BottomSheet>(null);
// variables
const snapPoints = useMemo(() => ['25%', '50%'], []);
// callbacks
const handleSheetChanges = useCallback((index: number) => {
console.log('handleSheetChanges', index);
}, []);
// renders
return (
<View style={styles.container}>
<BottomSheet
ref={bottomSheetRef}
index={1}
snapPoints={snapPoints}
onChange={handleSheetChanges}
>
<View style={styles.contentContainer}>
<Text>Awesome 🎉</Text>
</View>
</BottomSheet>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 24,
backgroundColor: 'grey',
},
contentContainer: {
flex: 1,
alignItems: 'center',
},
});
export default App;

invalid hook call in mobx+react native

I'm new to mobx,
I was told that I can't use directly rootStore from rootStore.tsx directly, and I have to replace it with hook, so I've tried to call hook useStore from rootStore.tsx
but in this case I've got an error "invalid hook call. Hooks can be called inside of the body"
my files are:
rootStore.tsx
import { createContext, useContext } from 'react'
import { makeAutoObservable } from 'mobx'
import { AsyncTrunk } from 'mobx-sync'
import AsyncStorage from '#react-native-async-storage/async-storage'
import { DayStyle, firstDayStyle } from '../styles/markedDayStyle'
const period: Record<string, DayStyle> = {
'2022-02-16': firstDayStyle,
}
export const rootStore = makeAutoObservable({
periods: period,
})
export const trunk = new AsyncTrunk(rootStore, {
storage: AsyncStorage,
})
export const StoreContext = createContext(rootStore)
export const StoreProvider = StoreContext.Provider
export const useStore = () => useContext(StoreContext)
App.tsx
const App = observer(({}) => {
const store = useStore()
const [isStoreLoaded, setIsStoreLoaded] = useState(false)
useEffect(() => {
const rehydrate = async () => {
await trunk.init()
setIsStoreLoaded(true)
}
rehydrate().catch(() => console.log('problems with localStorage'))
}, [store])
if (!isStoreLoaded) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<ActivityIndicator size="large" />
</View>
)
} else {
return (
<StoreProvider value={store}>
<PaperProvider theme={store.isDarkMode ? darkTheme : lightTheme}>
<View style={styles.container}>
<CalendarScreen/>
</View>
</PaperProvider>
</StoreProvider>
)
}
})
CalendarScreen.tsx
const CalendarScreen = observer(({}) => {
const store = useStore()
const handleDayPress = (day: DateData) => {
setModalVisible(true)
setPressedDay(day.dateString)
}
return (
<SafeAreaView style={styles.screenContainer}>
<Calendar
onDayPress={day => {handleDayPress(day)}}
/>
<View>
<ModalConfirmDay modalVisible={modalVisible} setModalVisible={setModalVisible} pressedDay={pressedDay} />
</View>
</SafeAreaView>
)
)}
ModalConfirmDay.tsx
import { fillMarkedDays } from '../functions/fillMarkedDays'
const ModalConfirmDay = observer(({ modalVisible, setModalVisible, pressedDay }: ModalConfirmDayProps) => {
const handlePeriodStarts = () => {
fillMarkedDays(pressedDay)
setModalVisible(false)
}
return (
<View style={styles.centeredView}>
<Modal
visible={modalVisible}
>
<View style={styles.modalView}>
<TouchableOpacity onPress={() => handlePeriodStarts()}>
<Text>Period starts</Text>
</TouchableOpacity>
</View>
</Modal>
</View>
)
})
fillMarkedDays.tsx
import { rootStore, useStore} from '../store/rootStore'
import { firstDayStyle} from '../styles/markedDayStyle'
const fillMarkedDays = (selectedDay: string) => {
const store = useStore()
if (selectedDay) {
store.periods[selectedDay] = firstDayStyle
}
}
when I try to add a new key-value (in fillMarkedDays.tsx) to store.periods I'm getting this
how can I fix this or select a better approach to call the store? Thanks everyone
By the rules of hooks you can't use hooks outside of the body of the function (component), so basically you can only use them before return statement, and also you can't use any conditions and so on. fillMarkedDays is just a function, not a component, it has no access to React context, hooks or whatever.
What you can do is first get the store with hook, then pass it as an argument into the fillMarkedDays function:
const ModalConfirmDay = observer(({ modalVisible, setModalVisible, pressedDay }: ModalConfirmDayProps) => {
const store = useStore()
const handlePeriodStarts = () => {
fillMarkedDays(store, pressedDay)
setModalVisible(false)
}
// ...
}

React Native Calendars : How to change the background color of only one item using Redux

I'm trying to change the background of only one item in this agenda but with my code, when I click on an item, it changes the background color of all of them and not just the one I clicked : Before clicked after clicked. I want also to store the value of my state in redux so that I can display the screen elsewhere in my application while conserving the green color.
Here is my code from my calendar:
import React, {useState, useCallback, useEffect} from 'react';
import {
Text,
View,
TouchableOpacity,
} from 'react-native';
import { useSelector, useDispatch } from "react-redux";
import { Avatar, Card } from 'react-native-paper';
import {Agenda} from "react-native-calendars";
import { toggleActive} from "../store/actions/calendar";
import * as firebase from "firebase";
const activeItemText = 'Réservé';
const activeItemStyles = {
backgroundColor: 'lightgreen',
backgroundColor2: 'white',
};
const inactiveItemText = 'Disponible';
const inactiveItemStyles = {
backgroundColorPrimary: 'white',
backgroundColorSecondary: 'white',
};
const timeToString = (time) => {
const date = new Date(time);
return date.toISOString().split('T')[0];
};
const CalendarItem = ({item, firstItemInDay}) => {
// const [active, setActive] = useState(false);
const dispatch = useDispatch();
const active = useSelector(state => state.calendar.active);
const toggleActiveHandler = useCallback(() => {
dispatch(toggleActive())
}, [dispatch]);
// const [active1, setActive1] = React.useState(false);
// console.log(active);
/* const changeColor = () => {
setActive(!active)
};
*/
return (
<TouchableOpacity
style={{marginTop: 17, marginRight: 10}}
onPress={toggleActiveHandler}>
<Card style={active ? activeItemStyles : inactiveItemStyles}>
<Card.Content>
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
}}>
<Text>{active ? activeItemText : inactiveItemText}</Text>
<Avatar.Text label="J" />
</View>
</Card.Content>
</Card>
</TouchableOpacity>
);
};
const renderItem = (item, firstItemInDay) => {
return <CalendarItem item={item} firstItemInDay={firstItemInDay} />;
};
const calendarScreen = () => {
const [items, setItems] = useState({});
const loadItems = (day) => {
setTimeout(() => {
for (let i = -15; i < 85; i++) {
const time = day.timestamp + i * 24 * 60 * 60 * 1000;
const strTime = timeToString(time);
if (!items[strTime]) {
items[strTime] = [];
const numItems = 1;
for (let j = 0; j < numItems; j++) {
items[strTime].push({
name: inactiveItemStyles.texte,
height: Math.max(50, Math.floor(Math.random() * 150)),
});
}
}
}
const newItems = {};
Object.keys(items).forEach((key) => {
newItems[key] = items[key];
});
setItems(newItems);
}, 1000);
};
return (
<View style={{flex: 1}}>
<Agenda
items={items}
loadItemsForMonth={loadItems}
selected={'2020-09-23'}
renderItem={renderItem}
/>
</View>
);
};
export default calendarScreen;
Here is my reducer :
import { TOGGLE_ACTIVE } from "../actions/calendar";
const initialState = {
active: false
};
const calendarReducer = (state = initialState, action) => {
switch (action.type) {
case TOGGLE_ACTIVE :
console.log(state);
console.log(!!state);
return {
active: !!state};
default:
return state
}
};
export default calendarReducer;
And here is my action :
export const TOGGLE_ACTIVE = 'TOGGLE_ACTIVE';
export const toggleActive = () => {
return { type: TOGGLE_ACTIVE }
};
Thanks a lot in advance for your help !
You are just saving active state, that's why its changing background for all. Cause, it can't differentiate for which you have changed the state . So, keep some unique data from the item that you have clicked to compare it with others. If the redux data match change background otherwise not.

Change screen without a click event using navigation stack react native

Well what I'm trying to do is when he finishes reading the qr code is to move to the next screen as soon as this event ends. I tried to do this by declaring:
const handleBarCodeScanned = ({ type, data }) => {
{this.props.navigation.navigate ('testScreen', {data1, data2})}
}
Usually, the documentation always shows accompanied by an onClick () function associated with a button.
import React, { useState, useEffect } from 'react';
import { Text, View, StyleSheet, Button, PermissionsAndroid } from 'react-native';
import { BarCodeScanner } from 'expo-barcode-scanner';
import wifi from 'react-native-android-wifi';
export default function QrCodeScreen() {
const [hasPermission, setHasPermission] = useState(null);
const [scanned, setScanned] = useState(false);
useEffect(() => {
(async () => {
const { status } = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
const handleBarCodeScanned = ({ type, data }) => {
{this.props.navigation.navigate('nextScreen', { data1, data2 })}//Change screen
})}
};
if (hasPermission === null) {
return <Text>Requesting for camera permission</Text>;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
return (
<View
style={{
flex: 1,
flexDirection: 'column',
justifyContent: 'flex-end',
}}>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style={StyleSheet.absoluteFillObject}
/>
{scanned && <Button title={'Tap to Scan Again'} onPress={() => setScanned(false)} />}
</View>
);
}
Seems like you're using functional components so there is no this context.
You forget to import and init the navigation hook
import { useNavigation } from '#react-navigation/native';
And
export default function QrCodeScreen() {
const navigation = useNavigation();
...
Then
const handleBarCodeScanned = ({ type, data }) => {
navigation.navigate('nextScreen', { data1, data2 })
})}
I managed to solve the error by passing as the navigation parameter in the function declaration.
Before
export default function QrCodeScreen() {
}
After
export default function QrCodeScreen({navigation}) {
}
Change screen
navigation.navigate('SetupConnectionScreen');

How to hide components when keyboard is up?

Is there a way to hide components when the keyboard shows, aside from installing packages?
Using the code sample from the Keyboard documentation, I would do something like this:
class Example extends Component {
state = {keyboardOpen: false};
componentDidMount() {
this.keyboardDidShowListener = Keyboard.addListener(
'keyboardDidShow',
this._keyboardDidShow,
);
this.keyboardDidHideListener = Keyboard.addListener(
'keyboardDidHide',
this._keyboardDidHide,
);
}
componentWillUnmount() {
this.keyboardDidShowListener.remove();
this.keyboardDidHideListener.remove();
}
_keyboardDidShow() {
this.setState({
keyboardOpen: true
});
}
_keyboardDidHide() {
this.setState({
keyboardOpen: false
});
}
render() {
return (
{!this.state.keyboardOpen && <MyComponent />}
);
}
}
Basically, in componentDidMount you subscribe to the show and hide keyboard events. You keep track of the Keyboard current state in your Component state thanks to this.state.keyboardOpen and you conditionally display MyComponent based on the value of this.state.keyboardOpen in your render method.
For those using functional components, here is a hook that you could use to detect when the keyboard is opened and closed.
import { useEffect, useState, useMemo } from "react";
import { Keyboard } from "react-native";
const useKeyboardOpen = () => {
const [isKeyboardOpen, setIsKeyboardOpen] = useState(false);
useEffect(() => {
const keyboardOpenListener = Keyboard.addListener("keyboardDidShow", () =>
setIsKeyboardOpen(true)
);
const keyboardCloseListener = Keyboard.addListener("keyboardDidHide", () =>
setIsKeyboardOpen(false)
);
return () => {
if (keyboardOpenListener) keyboardOpenListener.remove();
if (keyboardCloseListener) keyboardCloseListener.remove();
};
}, []);
return isKeyboardOpen;
};
export default useKeyboardOpen;
And this is how I use it in my projects:
import { useTheme } from "react-native-paper";
import useKeyboardOpen from "hooks/useKeyboardOpen";
export const Login = () => {
const theme = useTheme();
const isKeyboardOpen = useKeyboardOpen();
const styles = useStyles(theme, isKeyboardOpen);
return (
<View style = {styles.container}>
<View style = {styles.logo}>
<Logo />
</View>
<View style = {styles.form}>
....
</View>
</View>
);
};
const useStyles = (theme, isKeyboardOpen) = (
StyleSheet.create({
container: {
flex: 1,
},
logo: {
flex: 1,
marginTop: 20,
justifyContent: "center",
alignItems: "center",
...(isKeyboardOpen && {
display: "none",
}),
},
form: {
flex: 1,
}
})
);
I made this into a npm package if anyone is interested.
https://github.com/TIKramer/react-native-hide-onkeyboard
You can hide the view by using HideOnKeyboard> </HideOnKeyboard