react native - DatePickerIOS with AsyncStorage - react-native

I have tried storing DatePickerIOS dates with redux.
Is there a way to use AsyncStorage? I have been trying with no luck so far. Are there any suggestions on how to use AsyncStorage with a simple DatePickerIOS component?
<DatePickerIOS
style={{ paddingTop: 110 }}
mode='date'
date={this.state.d}
onDateChange={(d) => this.onDateChange(d)}
/>
constructor(props) {
this.state = { date: newDate() };
}
onDateChange(d) {
this.setState({
d: d
});

You set the state for the d variable in onDateChange but you use the startDate variable in the DatePickerIOS.
Take a look at this, didn't tested in app but should work.
export class PickerIOS extends React.Component {
constructor() {
super();
this.state = {
pickedDate: null
}
}
componentWillMount() {
getData('date')
.then((date) => {
if (date != null)
this.setState({pickedDate: date})
else
this.setState({pickedDate: new Date()})
})
}
onDateChange(date) {
setData('date', date)
this.setState({pickedDate: date})
}
render() {
return (
<DatePickerIOS
mode='date'
date={this.state.pickedDate}
onDateChange={(date) => this.onDateChange(date)}
/>
);
}
}
and then, for code organisation, in another file:
setData(key, data) {
try {
await AsyncStorage.setItem(key, data);
} catch (error) {
// Error saving data
}
}
getData(key) {
try {
const value = await AsyncStorage.getItem(key);
if (value !== null){
return value
}
} catch (error) {
// Error retrieving data
}
}

Related

React Native: Getting data from Firebase

I'm simply trying to retrieve data from the database in Firebase, and here's what I've got
var userList = [];
firebase.database()
.ref('/users/')
.once('value')
.then(snapshot => {
snapshot.forEach((doc) => {
userList.push(doc.val());
});
});
console.log(userList);
Even though I copy and pasted this code from a tutorial, the userList is empty outside of the snapshot. Can you tell me why that is?
The request to firebase is asynchronous so console.log(userList); is called before userList.push(doc.val()); gets called.
You should make userList a component state variable so that when you update it your component will re render.
Something like the following should work:
class UserListComponent extends Component {
constructor(props) {
super(props);
this.state = {
userList: [],
};
}
componentDidMount() {
this.getUsers();
}
getUsers() {
firebase
.database()
.ref('/users/')
.once('value')
.then((snapshot) => {
snapshot.forEach((doc) => {
this.setState({
userList: [...this.state.userList, doc.val()],
});
});
});
}
render() {
return (
<View>
{this.state.userList.map((item) => {
return (
<View>
<Text>{item.name}</Text>
</View>
);
})}
</View>
);
}
}

react native setInterval cannot read property apply

I am new in react native I am trying to render the count of unread notification for that I called my API in HOC it is working fine for initial few seconds but after that, I started to get the below error
func.apply is not a function
below is my code
import React, { Component } from "react";
import PropTypes from "prop-types";
import { Modal, View } from "react-native";
import { themes } from "./constants";
import { AsyncStorage } from "react-native";
export default (OriginalComponent, animationType) =>
class extends Component {
static propTypes = {
handleFail: PropTypes.func,
theme: PropTypes.string,
visible: PropTypes.bool
};
state = {
modalVisible: true
};
static getDerivedStateFromProps({ visible }) {
if (typeof visible === "undefined") {
setInterval(
AsyncStorage.getItem("loginJWT").then(result => {
if (result !== null) {
result = JSON.parse(result);
fetch(serverUrl + "/api/getUnreadNotificationsCount", {
method: "GET",
headers: {
Authorization: "Bearer " + result.data.jwt
}
})
.then(e => e.json())
.then(function(response) {
if (response.status === "1") {
if (response.msg > 0) {
AsyncStorage.setItem(
"unreadNotification",
JSON.stringify(response.msg)
);
} else {
AsyncStorage.setItem("unreadNotification", 0);
}
}
})
.catch(error => {
alert(error);
// console.error(error, "ERRRRRORRR");
});
} else {
AsyncStorage.setItem("unreadNotification", 0);
}
}),
5000
);
return null;
}
return { modalVisible: visible };
}
handleOpenModal = () => {
this.setState({ modalVisible: true });
};
handleCloseModal = () => {
const { handleFail } = this.props;
this.setState({ modalVisible: false }, handleFail);
};
render() {
const { modalVisible } = this.state;
const { theme } = this.props;
return (
<View>
<Modal
animationType={animationType ? animationType : "fade"}
transparent={true}
visible={modalVisible}
onRequestClose={this.handleCloseModal}
>
<View style={themes[theme] ? themes[theme] : themes.transparent}>
<OriginalComponent
handleCloseModal={this.handleCloseModal}
{...this.props}
/>
</View>
</Modal>
</View>
);
}
};
I have not used getDerivedStateFromProps but, according to the docs, it is called on initial component mount and before each render update.
Thus your code is creating a new interval timer on each update without clearing any of the earlier timers, which could be causing a race condition of some sort.
You may want to consider using the simpler alternatives listed in the docs, or at a minimum, insure that you cancel an interval before creating a new one.

'Warning: Can\'t perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application

im building a react native application ,still i have 2 screens
1.Enter mobile
2.Verify Otp
EnterUserInfo.js
class UserInfoInput extends Component {
constructor(props) {
super(props);
this.state = { formValid:true,
validMobileNumber:false,
.
.}}
componentWillReceiveProps(nextProps) {
if(nextProps.common.isFetching===false) {
this.props.navigation.navigate('VerifyOtpScreen')
.
.
} else {
this.setState({isLoading:true})
}}
onPressNext=()=> {
this.props.sendOtp(payload)}
render() {
return (<View/>)
}
}
}
function mapStateToProps(state) {
return {
common: state.common
}
}
function mapDispatchToProps(dispatch) {
return {
...bindActionCreators({ sendOtp }, dispatch)
}
}
export default connect(mapStateToProps,mapDispatchToProps)(UserInfoInput);
Here user enter the phone number ,and trigger an action sendOtp,response will be in the reducer and it will be available in the componentwillrecieveprops() lifecycle.
VerifyOtp.js
class VerifyOtp extends Component {
constructor(props) {
super(props);
this.state = { oneTimePIN: '' ,
.};
}
componentDidMount(){
this.setState({ phoneNumber:this.props.common.phone});
}
componentWillMount() {
setTimeout(() => {
this.setState({ isResendDisabled: false, opacity: 1 });
}, 30000);
}
componentWillReceiveProps(nextProps){
//do operation
}
onPressNext=()=>{
if(this.state.oneTimePIN=='') {
this.setState({showNotification:true})
}
else {
this.onSubmit()
}
}
onSubmit=()=>{
this.props.verifyOtp(payload)
}
onResendOtp=()=>{
this.props.sendOtp(payload,locationData)
this.setState({ isResendDisabled: true, opacity: 0.5 });
setTimeout(() => {
this.setState({ isResendDisabled: false, opacity: 1 });
}, 30000);
}
render() {
return (<View><Elements></View>)
}
}
function mapStateToProps(state) {
return {
common: state.common
}
}
function mapDispatchToProps(dispatch) {
return {
...bindActionCreators({ verifyOtp,sendOtp }, dispatch)
}
}
export default connect(mapStateToProps,mapDispatchToProps)(VerifyOtp);
VerifyOtp screen used to verify the otp.
The problem is,If i move back to enterUserinfo screen and move again to the verifyOtp screen im getting the warning message
'Warning: Can\'t perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application
What is the reason for the warning , and how tackle the issue?
This happens when you call an async function followed by setstate.
A simple work around would be like this:
constructor(props) {
this.state = {
...
this.isCancelled: false
}
}
componentWillMount() {
setTimeout(() => {
!this.state.isCancelled && this.setState({ isResendDisabled: false,
opacity: 1 });
}, 30000);
}
and in componentWillUnmount
componentWillUnmount() {
// setting it true to avoid setState waring since componentWillMount is async
this.state.isCancelled = true;
}

Get promisse response with fetch()

I'm trying to get the json result of my API using fetch() to get data and I'm using async/await to wait promisse resolving, but when my function fetchArticlesList() returns in return responseJson I'm getting the promisse, like this: Promise {_40: 0, _65: 0, _55: null, _72: null} instead the json.
How can I get the json on my <Flatlist> component ?
<FlatList
data={(()=> {
if (this.props.data)
return this.props.data
const response = APIRequest.fetchArticlesList()
return response
})()
}
renderItem={(article) => (
...
)}
/>
The APIRequest:
async fetchArticlesList() {
try {
const response = await fetch(`${apiBackoffice}/articles`)
const responseJson = await response.json();
return responseJson; //this returns the promisse, instead of the json. I want to get the json
} catch (error) {
console.error(error)
}
}
APIRequest.fetchArticlesList() is async function, that's why it is returning a Promise object (take a look the document here). Load the API response into the state and when it is loaded pass it to the FlatList. Consider the following sample
class SomeComp extends React.Component {
constructor(props) {
super(props);
this.state = { data: [], isLoading: true }
}
componentDidMount() {
APIRequest.fetchArticlesList().then(data => {
this.setState({
data,
isLoading: false
});
});
}
render() {
const { isLoading, data } = this.state;
if (isLoading) {
return <ActivityIndicator .../>
}
return (
<FlatList
data={data}
...
/>
);
}
}
Hope this will help!

How to detect first launch in react-native

What is a good way to detect the first and initial launch of an react-native app, in order to show an on-boarding/introductory screen ?
Your logic should follow this:
class MyStartingComponent extends React.Component {
constructor(){
super();
this.state = {firstLaunch: null};
}
componentDidMount(){
AsyncStorage.getItem("alreadyLaunched").then(value => {
if(value === null){
AsyncStorage.setItem('alreadyLaunched', 'true'); // No need to wait for `setItem` to finish, although you might want to handle errors
this.setState({firstLaunch: true});
}
else{
this.setState({firstLaunch: false});
}}) // Add some error handling, also you can simply do this.setState({fistLaunch: value === null})
}
render(){
if(this.state.firstLaunch === null){
return null; // This is the 'tricky' part: The query to AsyncStorage is not finished, but we have to present something to the user. Null will just render nothing, so you can also put a placeholder of some sort, but effectively the interval between the first mount and AsyncStorage retrieving your data won't be noticeable to the user.
}else if(this.state.firstLaunch === 'true'){
return <FirstLaunchComponent/>
}else{
return <NotFirstLaunchComponent/>
}
}
Hope it helps.
I made some adjustments to martinarroyo's suggestion. AsyncStorage.setItem should set a string value and not a bool.
import { AsyncStorage } from 'react-native';
const HAS_LAUNCHED = 'hasLaunched';
function setAppLaunched() {
AsyncStorage.setItem(HAS_LAUNCHED, 'true');
}
export default async function checkIfFirstLaunch() {
try {
const hasLaunched = await AsyncStorage.getItem(HAS_LAUNCHED);
if (hasLaunched === null) {
setAppLaunched();
return true;
}
return false;
} catch (error) {
return false;
}
}
This function can then be imported wherever you need it. Note that you should render null (or something else clever) while waiting for the async function to check AsyncStorage.
import React from 'react';
import { Text } from 'react-native';
import checkIfFirstLaunch from './utils/checkIfFirstLaunch';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isFirstLaunch: false,
hasCheckedAsyncStorage: false,
};
}
async componentWillMount() {
const isFirstLaunch = await checkIfFirstLaunch();
this.setState({ isFirstLaunch, hasCheckedAsyncStorage: true });
}
render() {
const { hasCheckedAsyncStorage, isFirstLaunch } = this.state;
if (!hasCheckedAsyncStorage) {
return null;
}
return isFirstLaunch ?
<Text>This is the first launch</Text> :
<Text>Has launched before</Text>
;
}
}
In 2022: Note that AsyncStorage from react-native is already deprecated.
Use react-native-async-storage/async-storage instead.
Custom hook:
import React, { useState } from "react";
import AsyncStorage from "#react-native-async-storage/async-storage";
async function checkIfFirstLaunch() {
try {
const hasFirstLaunched = await AsyncStorage.getItem("#usesr_onboarded");
if (hasFirstLaunched === null) {
return true;
}
return false;
} catch (error) {
return false;
}
}
const useGetOnboardingStatus = () => {
const [isFirstLaunch, setIsFirstLaunch] = useState(false);
const [isFirstLaunchIsLoading, setIsFirstLaunchIsLoading] = useState(true);
React.useEffect(async () => {
const firstLaunch = await checkIfFirstLaunch();
setIsFirstLaunch(firstLaunch);
setIsFirstLaunchIsLoading(false);
}, []);
return {
isFirstLaunch: isFirstLaunch,
isLoading: isFirstLaunchIsLoading,
};
};
export default useGetOnboardingStatus;
Usage:
import useGetOnboardingStatus from "../../utils/useGetOnboardingStatus";
const { isFirstLaunch, isLoading: onboardingIsLoading } =
useGetOnboardingStatus();
As Eirik Fosse mentioned: You can use onboardingIsLoading to return null while waiting for the response.