React Native save change switch with Async Storage - reactjs

Im new in React Native, I have a problem with Switch, I want to save changes, dark mode and Switch, when I turn off the app and come back my changes should be saved. When I close the app, my switch came back to first position and dark mode does not work. I know that Im doing something wrong, but I did not mobile app and this is my first time and I dont know how to use AsyncStorage in this App to work this. Can somebody help me solve this problem?
import React, { createContext, useState, useEffect } from 'react';
import { AsyncStorage } from 'react-native';
export const DarkModeContext = createContext();
export default function DarkModeContextProvider(props) {
const [switchMode, setSwitchMode] = useState(false);
useEffect(() => {
let switch1 = switchMode;
AsyncStorage.setItem('switch1', JSON.stringify(switch1));
});
const SwitchThis = () => {
setSwitchMode(!switchMode);
};
return (
<DarkModeContext.Provider
value={{
switchMode,
SwitchThis
}}
>
{props.children}
</DarkModeContext.Provider>
);
}
and next component:
import React, { useState, useContext } from 'react';
import { View, ScrollView, TouchableOpacity, Text, AsyncStorage } from 'react-native';
import { List } from 'react-native-paper';
import BackgroundImage from './BackgroundImage';
import Clock from './Clock';
import TabIcon from './TabIcon';
import AddButton from './AddButton';
import { DarkModeContext } from './app-context';
const HomeScreen = () => {
const { switchMode } = useContext(DarkModeContext);
displayData = async () => {
try {
let switch1 = await AsyncStorage.getItem('switch1', function (err, switch1) {
JSON.parse(switch1)
}
)
return switch1
}
catch (error) {
return error
}
}
return (
<View
style={{
flex: 1,
backgroundColor: !switchMode ? 'white' : '#353535'
}}
>
<BackgroundImage fabButton={<AddButton/>}>
<Clock />
</BackgroundImage>
<ScrollView>
<List.Section>
<List.Subheader style={{ color: !switchMode ? 'black' : 'white' }}>
Task List
</List.Subheader>
<TouchableOpacity onPress={displayData}>
<Text>Click displayData</Text>
</TouchableOpacity>
</List.Section>
</ScrollView>
</View>
);
};

You are importing AsyncStorage from 'react-native' which is deprecated
use #react-native-community/react-native-async-storage
npm i #react-native-community/react-native-async-storage
And on your home screen you are not calling the function displayData() so how is data supposed to be displayed without function call.
and i do suggest making separate functions for writing and reading from async storage, it will help you reduce your code and time.
Like this:
let storeData=(name, obj)=> {
return new Promise((resolve,reject)=>{
let jsonOfItem = JSON.stringify(obj)
AsyncStorage.setItem(name, jsonOfItem).then(r=>resolve(jsonOfItem))
.catch(e=>reject(e))
})
}
let readData=(name)=> {
return new Promise((resolve,reject)=>{
//we want to wait for the Promise returned by AsyncStorage.setItem()
//to be resolved to the actual value before returning the value
AsyncStorage.getItem(name).then(r=> resolve(JSON.parse(r)) )
.catch(e=>reject(e))
})
}
//Now you can just read write easily in async function like this:
let fetchedData = await readData("key")
//and for storing data.
storeData("key",object)

Related

Use hook in export function - React Native

I want to write 1 common function that I will use in different functional components.
This generic function uses hooks and I get the error: Error: Invalid hook call. Hooks can only be called inside the body of a functional component.
Example of my code:
App.js
import React from 'react';
import {
Text,
TouchableOpacity,
} from 'react-native';
import { Change } from 'static/Change';
export default function App() {
return (
<TouchableOpacity
onPress={() => {
Change();
}}
>
<Text>Click Me!</Text>
</TouchableOpacity>
);
}
Change.js
import React from 'react';
export const Change = () => {
const [State, setState] = React.useState(0);
// Other hook work.
// The function returns nothing
};
What is my mistake and how can I fix it?
Hooks has some rules to follow - https://reactjs.org/docs/hooks-rules.html
Refactor code as below
import React from "react";
import { Text, TouchableOpacity } from "react-native";
function useChange() {
const [state, setState] = React.useState(0);
function change(value) {
setState(value);
}
return { change, state };
}
export default function App() {
const { change, state } = useChange();
return (
<TouchableOpacity
onPress={() => {
// Update state value on press
change(state + 1);
}}
>
<Text>Click Me!{state}</Text>
</TouchableOpacity>
);
}

React Native : Rendered more hooks than during the previous render

I am getting this error after implementing the gethomeid useState and cant understand I tried different things but nothing worked
import React, {useEffect, useState} from 'react';
import {
StyleSheet,
View,
Image,
Text,
TouchableOpacity,
Button,
} from 'react-native';
import {windowWidth, windowHeight} from '../utils/Dimentions';
//components
import EmptyContainer from '../components/EmptyContainer';
import Header from '../components/Header';
import TopTabs from '../components/Tabs';
//redux
import {getHomeData} from '../action/homedata';
import {connect} from 'react-redux';
import propTypes from 'prop-types';
// to render empty container
const Home = ({getHomeData, homeState, userDetails}) => {
const [gethomeid, setGethomeid] = useState(null);
if (userDetails) {
useEffect(() => {
getHomeData('123456');
}, []);
}
if (homeState.data != null && userDetails && gethomeid != null) {
console.log(homeState.data);
return (
<View style={{flex: 1}}>
<Header username={userDetails.name.split(' ')[0]} />
<TopTabs roomdata={homeState.data.roomMapper} />
</View>
);
} else {
return (
<View>
<EmptyContainer />
</View>
);
}
};
const mapStateToProps = state => ({
homeState: state.data,
userDetails: state.auth.user,
});
const mapDispatchToProps = {
getHomeData,
};
Home.propTypes = {
getHomeData: propTypes.func.isRequired,
homeState: propTypes.object.isRequired,
userDetails: propTypes.object,
};
export default connect(mapStateToProps, mapDispatchToProps)(Home);
I am trying to load 2 buttons that will pass the data on clicked to the gethomeid but this error i am not able to solve
THE SNAPSHOT OF THE ERROR:-
You are conditionally calling the useEffect hook. This breaks the rules of hooks. You can likely move the conditional into the hook callback.
useEffect(() => {
if (userDetails) {
getHomeData('123456');
}
}, []);
You should probably also add any missing dependencies the linter may complain about, like userDetails and getHomeData.
useEffect(() => {
if (userDetails) {
getHomeData('123456');
}
}, [getHomeData, userDetails]);

React Native Navigation: Check if drawer is opened or not

i have a component that is outside the Drawer tag but it's inside a NavigationContainer.
So i used useRef from react to get navigate method .
So i use this example: Navigating without navigation prop
But now, i can't get the state of the drawer (is it opened or closed).
Here's my App component:
import 'react-native-gesture-handler';
import React from 'react';
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { Provider } from "react-redux";
import GetInformationByLocation from "./reducers/GetInformationByLocation";
import Wrapper from './components/Wrapper';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { NavigationContainer } from '#react-navigation/native';
import GeoDisplay from "./components/GeoDisplay";
import { Text } from "react-native";
import { navigationRef } from "./helpers/Navigator";
const store = createStore(GetInformationByLocation, applyMiddleware(thunk));
/*
* TODO:
* 1. Wrap everything with parent component
* 2. Search bar
* 3. Save searched cities.
* 4. Check if the city is undefined and suggest nearest city.
* 5. Animated background
* 6. Fancy and animated font
* 7. Setup menu преди показване на приложението
*/
const Drawer = createDrawerNavigator();
const App = () => {
return (
<Provider store={store}>
<NavigationContainer ref={navigationRef}>
<Wrapper />
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={GeoDisplay} />
</Drawer.Navigator>
</NavigationContainer>
</Provider >
);
}
export default App;
and my Wrapper.js Component:
import React, { useEffect, useState } from 'react';
import { StyleSheet, View, StatusBar } from 'react-native';
import { SearchBar, Header } from 'react-native-elements';
import { MainUI } from '../styling/UI';
import * as Navigator from "../helpers/Navigator";
import { DrawerActions } from "#react-navigation/native";
const Wrapper = (props) => {
const [search, setSearch] = useState(true);
const openDrawer = () => {
Navigator.navigationRef.current.dispatch(DrawerActions.openDrawer())
setSearch(false);
}
useEffect(() => {
}, []);
return (
<>
<StatusBar backgroundColor={"#011E25"} />
<Header
leftComponent={{ icon: 'menu', color: '#fff', onPress: () => { openDrawer() } }}
centerComponent={{ text: 'COVID Bulgaria', style: { color: '#fff' } }}
rightComponent={{ icon: 'home', color: '#fff' }}
backgroundColor={'#011E25'}
/>
{search &&
<View>
<SearchBar placeholder="Търси град" containerStyle={MainUI.searchContainer} />
</View>
}
</>
)
}
export default Wrapper;
I dispatch openDrawer() action with navigationRef
Navigator.navigationRef.current.dispatch(DrawerActions.openDrawer())
but can't get status of the drawer.
I've tried many ways but not work.
Thanks in advance.
You can check if drawer is open by getting the navigation state:
const state = navigationRef.current.getRootState();
const isDrawerOpen = state.history.some((it) => it.type === 'drawer');
The above code assumes that drawer is at root. If it's nested, you'll need to traverse the state to find the state object with type: 'drawer'.
It's not clear why you need to check it from the question. Normally you shouldn't need to check it. If you dispatch DrawerActions.openDrawer() if drawer is already open, nothing will happen. So the check is unnecessary. If you want to close the drawer if it was open, you can dispatch DrawerActions.toggleDrawer() instead.
You can create a helper class like:
class DrawerHandler() {
openDrawer() {
this.drawerOpened = true;
}
closeDrawer() {
this.drawerOpened = false;
}
getDrawerStatus() {
return this.drawerOpened;
}
}
export default new DrawerHandler();
Use open and close functions on drawer opening and closing and get function to get status of the drawer

Using activeStorage with i18n on expo not working

Trying to set an app using Typescript React on expo that can show a different language based on a user selection, but doesn't seem to be working.
I've made it store the selected language in asyncStorage and called await AsyncStorage.getItem('language') to get the language, my code is below. If console.log is run on asyncStorage, I was able to retrieve the value.
Interestingly, language works without any flaws when I use Localization.locale, so wondering if it's being asyncStorage is taking too long to get a result?
import I18n from 'i18n-js'
import AsyncStorage from '#react-native-community/async-storage'
import en from './en.json'
import zh from './zh.json'
I18n.translations = {
en,
zh
}
async function getLanguage() {
try {
const value = await AsyncStorage.getItem('language')
if (value != null) {
I18n.locale = value.slice(0,2)
I18n.fallbacks = true
}
} catch(e) {
console.log(e)
}
}
getLanguage()
export function t(name: string) {
return I18n.t(name)
}
Language selector (stores the selected language in asyncStorage)
import AsyncStorage from '#react-native-community/async-storage'
import { Updates } from 'expo';
export default function LanguageChange() {
const onPress = async (language: string) => {
await AsyncStorage.setItem('language', language);
Updates.reload();
}
return (
<View style={styles.languageContainer}>
<TouchableOpacity onPress={() => onPress("en")}>
<Text>English</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => onPress('zh-CN')}>
<Text>Chinese</Text>
</TouchableOpacity>
</View>
);
}
Edit 1
I've tried using .then as well to see if there was a different result, but it's still the same.
AsyncStorage.getItem('language').then((result) => {
I18n.locale = result.slice(0,2)
})

Not able to use map function react-native

I am new to react native and I am using Async storage to store objects and then parse, by storing them in favorites array. I am trying to map each element in array and display it.
I get this error: this.state.favorites.map is not a function.
I think it's a state error but as I'm new to these concepts, i don't know how to fix it. Please help.
import React, {Component} from 'react';
import {StyleSheet, Text, View, Image, FlatList, Button, AsyncStorage,
TouchableOpacity} from 'react-native';
import ajax from './ajax.js';
import PropTypes from 'prop-types';
import InspirationList from './InspirationList';
import FavoriteItem from './FavoriteItem';
import ProductItem from './ProductItem';
class Favorites extends Component{
state = {
favorites:[],
};
componentDidMount(){
this.showProduct();
}
async showProduct() {
let k=0;
AsyncStorage.getAllKeys()
.then(keys => AsyncStorage.multiGet(keys)
.then((result) => {
result.map(req => req.forEach((element) => {
k++;
if(element!=null && k%2==0){
this.setState({favorites: JSON.parse(element) });
console.log(this.state.favorites.nume);
}
}));
}));
};
render(){
return(
<View style='styles.fav'>
{this.state.favorites.map((fav)=>
<Text>{fav.nume}</Text>
)}
</View>
);
}
}
const styles = StyleSheet.create({
fav:{
backgroundColor:'#999',
flex:1,
width:'100%',
paddingTop:150,
}
});
export default Favorites;
On your showProduct function you are overriding what your state originally was. When assigning the JSON.parse(element) you are changing your state instead of having an array your are converting it into an object, that's why you are getting that error. map exists only on array-like.
So maybe you can do something like:
async showProduct() {
let k=0;
AsyncStorage.getAllKeys()
.then(keys => AsyncStorage.multiGet(keys)
.then((result) => {
result.map(req => {
let result = [];
req.forEach((element) => {
k++;
if(element!=null && k%2==0){
result.push(JSON.parse(element))
}
});
this.setState({favorites: result });}
);
}));
};

Resources