How to use 'await/async' in react native? - react-native

I am getting isValidate value from sqlite and routing user to MainTabs or ValidateUser components. But I can not do it because it is not working async/await (Or I couldn't.).
initialRouteName will be assigned the name of the component to which users will be routed.
Now react native routing to "ValidateUser".
Not: When I try flag async the MainNavigationContainer and I write await directly inside it is throwing error.
Not: I am using expo-sqlite-orm for getting data from database
https://github.com/dflourusso/expo-sqlite-orm
//imports truncated...
const Stack = createStackNavigator()
//truncated...
const MainNavigationContainer = (props) => {
console.log("MainNavigationContainer props", props)
var initialRouteName = "ValidateUser"
async function validateUserFunc () {
const validatedUser = await Users.findBy({isAdmin_eq: 1})
console.log('validatedUser in validateUserFunc: ', validatedUser)
props.setValidatedUser(validatedUser)
let userIsValidated = validatedUser.isValidated
let validatedTelNumber = validatedUser.telNo
initialRouteName = userIsValidated === 1 ? "MainTabs" : "ValidateUser"
console.log('initialRouteName in validateUserFunc: ', initialRouteName)
}
validateUserFunc()
console.log('initialRouteName after validateUserFunc: ', initialRouteName)
//truncated...
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={screenOptions}
initialRouteName={initialRouteName}
>
//truncated Stack.Screen...
</Stack.Navigator>
</NavigationContainer>
)
}
const mapStateToProps = state => {
return {
...state
}
}
export default connect(mapStateToProps, {validateUser, listenMessages, setValidatedUser})(MainNavigationContainer)
Users.js entity:
import * as SQLite from 'expo-sqlite'
import { BaseModel, types } from 'expo-sqlite-orm'
import * as FileSystem from "expo-file-system";
export default class Users extends BaseModel {
constructor(obj) {
super(obj)
}
static get database() {
/*return async () => SQLite.openDatabase({
name:"ulak.db",
location:"default"
})*/
return async () => SQLite.openDatabase("ulak.db")
//return async () => SQLite.openDatabase(`${FileSystem.documentDirectory}SQLite/ulak.db`)
}
static get tableName() {
return 'users'
}
static get columnMapping() {
return {
id: { type: types.INTEGER, primary_key: true }, // For while only supports id as primary key
userName: { type: types.TEXT, not_null: true },
userSurname: { type: types.TEXT, not_null: true },
telNo: { type: types.TEXT, not_null: true },
deviceId: { type: types.TEXT },
isValidated: { type: types.INTEGER, default: 0, not_null: true },
isAdmin: { type: types.INTEGER, not_null: false, default: 0 },//if isAdmin=1 phone owner, it will authentication.
profilePicture: { type: types.TEXT, default: null },
registerDate: { type: types.DATETIME, not_null: true, default: () => Date.now() }
}
}
}

Related

Test if imported method is called

I'm trying to test if a composable's method is called from my store. Here's what I have:
/stores/ui:
import { defineStore } from 'pinia';
import { useUtils } from '#/composables/utils';
const { sendEvent } = useUtils();
interface State {
tab: string,
}
export const useUIStore = defineStore('ui', {
state: (): State => ({
tab: 'Home',
}),
actions: {
setTab(tabName: string) {
this.tab = tabName;
sendEvent('navigation', `click_${tabName}`);
},
}
})
#/composables/utils:
export function useUtils() {
const sendEvent = (name: string, value: string) => {
// do stuff
};
return {
sendEvent
};
}
And here's my test file:
import { setActivePinia, createPinia } from 'pinia'
import { useUIStore } from '#/stores/ui';
import { useUtils } from '#/composables/utils';
describe('', () => {
let uiStore;
beforeEach(() => {
setActivePinia(createPinia());
uiStore = useUIStore();
})
it('Sets the active tab', () => {
let sendEvent = jest.spyOn(useUtils(), 'sendEvent');
expect(uiStore.tab).toBe('Home');
uiStore.setTab('help');
expect(uiStore.tab).toBe('help');
expect(sendEvent).toHaveBeenCalledTimes(1);
});
})
I've also tried mocking the import:
jest.mock(
'#/composables/utils',
() => {
function useUtils() {
return {
sendEvent: jest.fn(),
}
}
return {
useUtils
};
},
{ virtual: true },
);
import { useUtils } from '#/composables/utils';
expect(jest.fn()).toHaveBeenCalledTimes(expected)
Expected number of calls: 1
Received number of calls: 0
What's the correct way of adding a spy to sendEvent ?
In your test file - mock the utils:
const mockSendEvent = jest.fn();
jest.mock("#/composables/utils", () => ({
useUtils: () => ({
sendEvent: mockSendEvent,
}),
}));
and then update your test to use the mock:
it('Sets the active tab', () => {
expect(uiStore.tab).toBe('Home');
uiStore.setTab('help');
expect(uiStore.tab).toBe('help');
expect(mockSendEvent).toHaveBeenCalledTimes(1);
});

No reducer provided for key reducer (reducer underfine)

I got an error when user authReduce. When log this reducer and it show "underfine"
I'm using:
Redux persist store auth state
Redux toolkit
Sometime I try hot reload it work. So I don't understand why?
The problem appears when I store reducer and actions in the same file (In one file have too many exports) so I resolved this problem by the way split reducer(authReducer.js) and actions(authSlice.js) are two files like this:
// authReducer.js
import { createSlice } from '#reduxjs/toolkit';
export const authSlice = createSlice({
name: 'auth',
initialState: {
isLoading: true,
userToken: null,
isUserGuest: true,
isJailBroken: false,
isSubscription: true,
isShowCheckPassCode: false,
passCodeMessage: 'Đây là phiên bản nội bộ, Vui lòng nhập pass để sử dụng.',
passCode: '',
isShowPopupPassword: false,
userEmail:'',
typeLogin:'',
isShowLoginGGFB:true
},
reducers: {
signIn: {
reducer(state, action) {
const { token } = action.payload;
state.userToken = token
state.isLoading = false
},
prepare(token, userInfo) {
return { payload: { token, userInfo } }
}
},
skipAuth(state, action) {
state.isUserGuest = true;
state.userToken = null;
},
signout(state, action) {
state.userToken = null
state.isUserGuest = false;
},
setIsJailBroken(state, action) {
state.isJailBroken = true
},
setSubscriptionNoti(state, action) {
state.isSubscription = action.payload
},
setIsShowCheckPassCode(state, action) {
state.isShowCheckPassCode = action.payload
},
setDataPasscode(state, action) {
state.isShowCheckPassCode = action.payload.isShowCheckPassCode
state.passCodeMessage = action.payload.passCodeMessage
state.passCode = action.payload.passCode
},
setShowLoginGGFB(state, action){
state.isShowLoginGGFB = action.payload
},
setActivePopupPassword(state, action) {
state.isShowPopupPassword = action.payload
},
setUserEmail(state,action){
state.userEmail = action.payload
},
setTypeLogin(state,action){
state.typeLogin = action.payload
}
}
})
export const authReducer = authSlice.reducer
// authSlice.js
export const {
signIn,
signout,
restore,
skipAuth,
setIsJailBroken,
setSubscriptionNoti,
setIsShowCheckPassCode,
setDataPasscode,
setActivePopupPassword,
setUserEmail,
setTypeLogin,
setShowLoginGGFB,
} = authSlice.actions;
export const callActionSignOut = (isForce = true) => async dispatch => {
try {
if (isForce) {
await signOut();
}
dispatch(signout());
dispatch(
setFavorite({
dataFavorite: [],
objectDataFavorite: {},
}),
);
dispatch(fetchCartSuccess(null));
dispatch(resetUserInternal());
dispatch(clearUser());
} catch (error) {
console.log('callActionSignOut -> error', error);
}
};

store.getstate() evaluate to undefined

in a react-native project,I keep hitting in this error:
state is undefined, evaluating store.getstate()
//store.js
const composeEnhancers =
typeof window === "object" && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
: compose;
const enhancer = composeEnhancers(applyMiddleware(thunk));
const Store = createStore(
combineReducers(
{
form: formReducer,
appointmentsReducer
},
enhancer
)
);
console.log(Store.getState());
export default Store;`
//reducer.js
import {
FETCH_APPOINTMENTS_BEGIN,
FETCH_APPOINTMENTS_SUCCESS,
FETCH_APPOINTMENTS_FAILURE
} from '../actions/appointmentsAction';
const initialState = {
data: [],
loading: false,
error: null
};
export default function appointmentsReducer(state = initialState, action) {
switch(action.type) {
case FETCH_APPOINTMENTS_BEGIN:
return {
...state,
loading: true,
error: null
};
case FETCH_APPOINTMENTS_SUCCESS:
return {
...state,
loading: false,
data: action.payload.appointments
};
case FETCH_APPOINTMENTS_FAILURE:
return {
...state,
loading: false,
error: action.payload.error,
data: []
};
default:
return state;
}
}
//actions.js
import { listUrl } from "../cst";
export const FETCH_APPOINTMENTS_BEGIN = "FETCH_APPOINTMENTS_BEGIN";
export const FETCH_APPOINTMENTS_SUCCESS = "FETCH_APPOINTMENTS_SUCCESS";
export const FETCH_APPOINTMENTS_FAILURE = "FETCH_PRODUCTS_FAILURE";
export const fetchAppointmentsBegin = () => ({
type: FETCH_APPOINTMENTS_BEGIN
});
export const fetchAppointmentsSuccess = appointments => ({
type: FETCH_APPOINTMENTS_SUCCESS,
payload: { appointments }
});
export const fetchAppointmentsFailure = error => ({
type: FETCH_APPOINTMENTS_FAILURE,
payload: { error }
});
export function fetchAppointments() {
return dispatch => {
dispatch(fetchAppointmentsBegin());
return fetch(listUrl)
.then(handleErrors)
.then(res => res.json())
.then(json => {
dispatch(fetchApointmentsSuccess(json.appointment));
return json.appointment;
})
.catch(error => dispatch(fetchAppointmentsFailure(error)));
};
}
// Handle HTTP errors since fetch won't.
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
// app.js
export default function App() {
return (
<Provider store={Store}>
<Navigation />
</Provider>
);
}
//list rendrer component :
const mapStateToProps = state => ({
data: state.appointments.data,
loading: state.loading,
error: state.error
});
the console.log of store.getstate() gives :
Object {
"appointmentsReducer": Object {
"data": Array [],
"error": null,
"loading": false,
},
"form": Object {},
I'm not sure where the problem is.
Is it due to the asynchronous call not being handled properly?
If I use saga to handle the fetch, will it resolve the problem?
Any help would be appreciated .

Invariant Violation : Invalid Hooks Call. Hooks Call can only be made inside body of function components

Hi am getting this error at Connect() function in LoginScreen.js. On commenting it its working fine. I am guessing either my Store is not properly setup or i am not able to connect LoginScreen Component to Redux Store.
LoginScreen.js
import React, { Component } from "react";
import PhoneInput from "react-native-phone-input";
import { connect } from "react-redux";
import { View, StatusBar } from "react-native";
import { Container, Item, Input, Button, Text } from "native-base";
import {
phoneChanged,
codeChanged,
onCodeDispatched,
onPhoneLogin,
clearAuth,
onSignOut
} from "../Actions/AuthActions";
//import firebase from "react-native-firebase";
import { auth } from "../Config/firebase";
export class LoginScreen extends Component {
}
export default connect(
null, // passing null just for testing
null
)(LoginScreen);
Store.js
import ReduxThunk from "redux-thunk";
import { createStore, applyMiddleware, compose } from "redux";
import reducer from "../Reducers/index";
let composeEnhancers = compose;
/* eslint no-undef: 0 */
if (__DEV__) {
/* eslint no-underscore-dangle: 0 */
composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
}
const store = createStore(
reducer,
{},
composeEnhancers(applyMiddleware(ReduxThunk))
);
export { store };
AuthReducer.js
import {
LOGIN_FAIL,
LOGIN_SUCCESS,
LOGIN_USER,
PHONE_CHANGED,
CODE_SENT_ERROR,
CODE_CHANGED,
CODE_DISPATCHED,
LOGIN_USER_PHONE,
CODE_SENT,
CODE_NOT_CONFIRMED,
LOGOUT,
SET_USER_OBJECT,
CLEAR_AUTH
} from "../Actions/ActionTypes";
const INITIAL_STATE = {
phone: "+30",
user: null,
message: "",
loading: false,
codeInput: "",
confirmResult: null
};
const AuthReducer = (state = INITIAL_STATE, action) => {
console.log(action);
switch (action.type) {
case PHONE_CHANGED:
return {
...state,
phone: action.payload
};
case CODE_CHANGED:
return {
...state,
codeInput: action.payload
};
case LOGIN_USER:
return {
...state,
loading: true,
message: ""
};
case LOGIN_USER_PHONE:
return {
...state,
loading: true,
message: "Sending code...",
phone: action.payload
};
case CODE_DISPATCHED:
return {
...state,
loading: true,
message: ""
};
case CODE_SENT:
return {
...state,
loading: true,
message: "Code has been sent!",
confirmResult: action.payload
};
case CODE_SENT_ERROR:
return {
...state,
loading: false,
message: `Sign in with Phone number error: ${action.payload}`,
confirmResult: null
};
case SET_USER_OBJECT:
return {
...state,
user: action.payload
};
case CODE_NOT_CONFIRMED:
return {
...state,
message: `Code confirmation error: ${action.payload}`
};
case LOGIN_SUCCESS:
return {
...INITIAL_STATE,
user: action.payload,
message: "login Success"
};
case LOGIN_FAIL:
return {
...state,
message: "Authentication Failed.",
loading: false,
password: "",
phone: "+91"
};
case LOGOUT:
return {
...state,
message: "",
user: null
};
case CLEAR_AUTH:
return {
...state,
...INITIAL_STATE
};
default:
return state;
}
};
export default AuthReducer;
Root.js
import React from "react";
import { Provider } from "react-redux";
import { Navigator } from "./Navigation/Index";
import { store } from "./Store/Index";
export default class Root extends React.Component {
render() {
return (
<Provider store={store}>
<Navigator />
</Provider>
);
}
}
AuthActions.js
//import { firebase } from "react-native-firebase";
import * as actionTypes from "./ActionTypes";
import { auth } from "../Config/firebase";
const phoneChanged = text => {
return {
type: actionTypes.PHONE_CHANGED,
payload: text
};
};
const onLoginSuccess = (dispatch, user) => {
dispatch({
type: actionTypes.LOGIN_SUCCESS,
payload: user
});
};
const signOut = dispatch => {
dispatch({
type: actionTypes.LOGOUT
});
};
const onPhoneLogin = phone => {
return dispatch => {
dispatch({
type: actionTypes.LOGIN_USER_PHONE
});
auth
.signInWithPhoneNumber(phone)
// sign in success
.then(confirmResult => {
onCodeSent(dispatch, confirmResult);
})
// sign in error
.catch(error => onCodeSentError(dispatch, error));
};
};
const codeChanged = text => {
return {
type: actionTypes.CODE_CHANGED,
payload: text
};
};
const onCodeSent = (dispatch, confirmResult) => {
dispatch({
type: actionTypes.CODE_SENT,
payload: confirmResult
});
};
const onCodeConfirmError = (dispatch, error) => {
dispatch({
type: actionTypes.CODE_NOT_CONFIRMED,
payload: error
});
};
const onCodeDispatched = code => {
return (dispatch, getState) => {
getState()
.auth.confirmResult.confirm(code)
.then(user => onLoginSuccess(dispatch, user))
.catch(error => onCodeConfirmError(dispatch, error));
};
};
const onCodeSentError = (dispatch, error) => {
dispatch({
type: actionTypes.CODE_SENT_ERROR,
payload: error
});
};
const onSignOut = () => {
return dispatch => {
auth
.signOut()
.then(() => signOut(dispatch))
.catch(error => console.log(error));
};
};
const clearAuth = () => {
return dispatch => {
dispatch({
type: actionTypes.CLEAR_AUTH
});
};
};
export {
onSignOut,
clearAuth,
codeChanged,
onPhoneLogin,
phoneChanged,
onCodeDispatched
};
The Idea is basically to call LoginScreen which is part of 'Auth' StackNavigator and Render PhoneNumberInput and OTP.
React-redux (> 7.0.1) uses hook & React-native (< 0.59) doesn’t support Hooks yet.
You can run npm ls react-native in your application folder to check which version you’re using.
If you find more than one of them, this might also create problems. more on it
There are two solution
Upgrade the react native version to 0.59.0
OR
Downgrade the react redux version to 6.0.1
I hope it help you.

Use a global alert across the react-native app

I'm a beginner for react-native and I need to alert to the user based on a status which will be retrieved from an API in every 15 seconds. For this I'm using react-native-background-timer in my main component to call the service. But when app is in some other screen (component) even though the service executes perfectly in the main component, it doesn't update it's props or status depending on the result it received (I guess this should be because I'm in a some other screen and props of main component will not be updated). Due to that alert will not be triggered if app is not in the main component
Can anyone please suggest me an approach for this?
class Home extends Component{
constructor(props){
super(props)
this._onPopUpShowed = this._onPopUpShowed.bind(this)
}
componentDidMount(){
//Initial call after the launch
this.props.fetchLiveOrderData()
//Start timer for polling
const intervalId = BackgroundTimer.setInterval(() => {
isBackgroudLoad=true
this.props.fetchLiveOrderData()
}, 1000*15);
}
render(){
const{payload,isFetching,isError,isSuccess} = this.props.liveOrderData
return(
//Render UI depending on the data fetched
);
}
}
//map state to props
const mapStateToProps = state => {
return {
liveOrderData: state.liveOrderData
}
}
//map dispatch to props
const mapDispatchToProps = dispatch => {
return {
fetchLiveOrderData : () => dispatch(fetchLiveOrderData())
}
}
export default connect(mapStateToProps, mapDispatchToProps) (Home)
liveOrderReducer.js
import {
FETCHING_LIVE_ORDER_DATA, FETCHING_LIVE_ORDER_DATA_SUCCESS, FETCHING_LIVE_ORDER_DATA_ERROR
} from '../constants'
const initialState = {
payload: [],
msg:[],
isFetching: true,
isError: false,
isSuccess: false
}
export default liveOrderReducer = (state = initialState, action) => {
switch(action.type){
case FETCHING_LIVE_ORDER_DATA :
return {
...state,
payload: [],
msg:[],
isFetching: true,
isError: false,
isSuccess: false
}
case FETCHING_LIVE_ORDER_DATA_SUCCESS :
return {
...state,
payload: action.data,
msg:[],
isFetching: false,
isError: false,
isSuccess:true
}
case FETCHING_LIVE_ORDER_DATA_ERROR :
return {
...state,
payload: [],
msg:action.msg,
isFetching: false,
isError: true,
isSuccess:false
}
default:
return state
}
}
index.js
import {
FETCHING_LIVE_ORDER_DATA, FETCHING_LIVE_ORDER_DATA_SUCCESS, FETCHING_LIVE_ORDER_DATA_ERROR
} from '../constants'
import api from '../lib/api'
export const getLiveOrderData = () => {
return {
type : FETCHING_LIVE_ORDER_DATA
}
}
export const getLiveOrderDataSuccess = data => {
return {
type : FETCHING_LIVE_ORDER_DATA_SUCCESS,
data
}
}
export const getLiveOrderDataFailure = () => {
return {
type : FETCHING_LIVE_ORDER_DATA_ERROR
}
}
export const fetchLiveOrderData = () => {
return(dispatch) => {
dispatch(getLiveOrderData())
api.getOrder().then(resp => {
dispatch(getLiveOrderDataSuccess(resp))
}).catch((err) => {
dispatch(getLiveOrderDataFailure(err))
})
}
}
Move the notification code to the container or the root component. This will ensure you will receive notifications even if the user moved away from the home screen.