Undefined is not a function {near This.state} react Native error - react-native

In my react native code I am facing this issue. some time it's work and some time it doesn't.
I am beginner in user of React-redux for state management of component any lead will be appreciated.
import React, { PureComponent } from "react"
import PurchaseInvoiceView from "../Components/Purchaseinvoiceview"
import ReturnInvoiceView from "../Components/Returninvoiceview"
import { connect } from "react-redux"
import { translate } from "../i18n"
import { TabView, SceneMap, TabBar } from "react-native-tab-view"
import styles from "./Styles/InvoiceScreenStyle"
import { Cache } from "react-native-cache"
import AsyncStorage from "#react-native-community/async-storage";
import {
getPurchaseInvoice,
getPurchaseInvoiceFromCache,
getReturnInvoice,
getReturnInvoiceFromCache,
} from "../Redux/Actions/invoiceActions"
class InvoiceScreen extends PureComponent {
state = {
index: 0,
routes: [
{ key: "purchase", title: translate("invoiceScreen.purchase") },
{ key: "return", title: translate("invoiceScreen.return") },
],
token: null,
vansale_id: 0,
user: null,
}
componentDidMount() {
const { dispatch, userData } = this.props
this.setState(
{
token: userData.data.package.token || value.package.token,
vansale_id: userData.data.package.id || value.package.id,
},
() => {
const { token, vansale_id } = this.state
dispatch(getPurchaseInvoice(token, { vansale_id}))
.then(res => {
console.log(res);
if (res.payload.status === true) {
var cache = new Cache({
namespace: "FGMM",
policy: {
maxEntries: 50000,
},
backend: AsyncStorage,
})
cache.setItem("invoiceData", res.payload, function(err) {})
}
})
.catch(err => {
var cache = new Cache({
namespace: "FGMM",
policy: {
maxEntries: 50000,
},
backend: AsyncStorage,
})
cache.getItem("invoiceData", function(err, value) {
if (value) {
if (value.status === true) {
dispatch(getPurchaseInvoiceFromCache(value))
}
}
})
})
dispatch(getReturnInvoice(token, { vansale_id,}))
.then(res => {
console.log(res)
if (res.payload.status === true) {
var cache = new Cache({
namespace: "FGMM",
policy: {
maxEntries: 50000,
},
backend: AsyncStorage,
})
cache.setItem("invoiceReturnData", res.payload, function(err) {})
}
})
.catch(err => {
var cache = new Cache({
namespace: "FGMM",
policy: {
maxEntries: 50000,
},
backend: AsyncStorage,
})
cache.getItem("invoiceReturnData", function(err, value) {
if (value) {
if (value.status === true) {
dispatch(getReturnInvoiceFromCache(value))
}
}
})
})
},
)
var cache = new Cache({
namespace: "FGMM",
policy: {
maxEntries: 50000,
},
backend: AsyncStorage,
})
cache.getItem("userData", function(err, value) {
if (value) {
if (value.status === true) {
}
} else {
this.setState({
token: userData.data.package.token || value.package.token,
vansale_id: userData.data.package.id || value.package.id,
})
}
})
}
render() {
return (
<View style={styles.container}>
<TabView
navigationState={this.state}
activeColor="#777"
inactiveColor="#000"
renderTabBar={props => (
<TabBar
{...props}
indicatorStyle={styles.indicator}
style={styles.tabBar}
labelStyle={styles.labelStyle}
/>
)}
renderScene={SceneMap({
purchase: PurchaseInvoiceView,
return: ReturnInvoiceView,
})}
onIndexChange={index => this.setState({ index })}
initialLayout={{ width: Dimensions.get("window").width }}
/>
</View>
)
}
}
const mapStateToProps = state => {
return {
userData: state.userData,
invoiceData: state.invoiceData,
returnInvoiceData: state.invoiceData.return,
}
}
export default connect(mapStateToProps)(InvoiceScreen)
when I am using this on Android it is working perfectly fine, but for an iOS it's crashing

Related

Multiple useEffect in react-native to achieve mentioned functionality

I need help with the async nature of Async storage and axios api. Here's the functionality that I am trying to achieve ->
send request to two separate api to get some data.
display that data on the screen with some additional text
api request are authenticated so a token is passed as Authentication Header
I have attached the current implementation, I am having the a number of errors in this
Errors:
Login_token not set in state after fetching from Async Storage.
Data not set in state after api call
both resulting in either failed api calls or undefined state errors on render
This is my code.
import React, { FunctionComponent, useEffect, useCallback, useState} from 'react';
import { StyleSheet, View} from 'react-native';
// chat
import { GiftedChat } from 'react-native-gifted-chat';
// navigation
import { RootStackParamList } from '../../navigators/RootStack';
import { StackScreenProps } from '#react-navigation/stack';
export type Props = StackScreenProps<RootStackParamList, "Chat">;
// api
import { Convo_details, Send_Msg, List_Msg, Expert_Public_Profile } from '../../api/UserApi';
import Spinner from 'react-native-loading-spinner-overlay';
import AsyncStorage from '#react-native-async-storage/async-storage';
import uuid from 'react-native-uuid';
const Chat: FunctionComponent<Props> = ({ navigation, route, ...props }) => {
// console.log(props.route.params);
const [login_token, setlogin_token] = useState('')
const [conversation_id, setconversation_id] = useState('')
const [conversation_details, setconversation_details] = useState({})
const [currentuser, setcurrentuser] = useState({})
const [loading, setLoading] = useState(false);
const [expertuid, setexpertuid] = useState('')
const [ExpertProfile, setExpertProfile] = useState({})
const [messages, setMessages] = useState([]);
useEffect(() => {
getlogintoken()
console.log("####################################","getlogintoken");
}, [])
/* conversationid */
useEffect(() => {
if (route.params != null) {
setconversation_id(route.params[0])
}
console.log("####################################","conversation id");
}, [])
/* expert uid */
useEffect(() => {
if (route.params != null) {
setexpertuid(route.params[1])
}
console.log("####################################","expert uid");
}, [])
/* expert public profile */
useEffect(() => {
getexpertpublicprofile()
getConvo_details()
console.log("####################################","convo_details");
}, [])
useEffect(() => {
// get current user
AsyncStorage.getItem("currentuser").then(res => {
if (res != null) setcurrentuser(res)
else alert("Current user not found")
})
console.log("####################################","current user");
}, [])
// set welcome msg
useEffect(() => {
if (Object.keys(conversation_details).length != 0 && Object.keys(ExpertProfile).length != 0)
setwelcomemsg()
}, [])
const onSend = useCallback(async (messages = []) => {
// console.log(messages[0].text);
setMessages(previousMessages => GiftedChat.append(previousMessages, messages))
const data = {
conversation_id: "f98d6851-a713-4f58-9118-77a779ff175f",//conversation_id,
message_type: "TEXT",
body: messages[0].text
}
const res: any = await Send_Msg(data, login_token)
.catch(error => {
alert(`Send_Msg -> ${error}`)
console.log(error);
return
})
if (res.status == 200) {
console.log(res.data);
} else console.log(res);
}, [])
const getexpertpublicprofile = async () => {
setLoading(true)
const res: any = await Expert_Public_Profile(expertuid, login_token)
.catch(error => {
setLoading(false)
console.log("Expert public profile ->");
alert(`Expert public profile ->${error.message}`)
console.log(error);
return
})
setLoading(false)
if (res.status === 200) setExpertProfile(res.data)
else {
alert(`get expert public profile${res.data.message}`)
console.log("getexpertpublicprofile -->");
console.log(res.data);
}
}
const getlogintoken = () => {
AsyncStorage.getItem("login_token").then(res => {
if (res != null) {
setLoading(false)
setlogin_token(res)
}
else alert("No login token found")
})
}
const getConvo_details = async () => {
setLoading(true)
const res: any = await Convo_details(conversation_id, login_token)
.catch(error => {
setLoading(false)
alert(`Convo_details-->${error.message}`)
console.log("Convo_details -->");
console.log(error);
return
})
setLoading(false)
if (res.status === 200) setconversation_details(res.data)
else {
alert(`get convo details-> ${res.data.message}`)
console.log("getConvo_details -->");
console.log(res.data);
}
}
const setwelcomemsg = () => {
try {
let user = JSON.parse(currentuser)
let messages = [
{
_id: uuid.v4().toString(),
conversation_id: conversation_details.conversation_id,
created_at: new Date(),
from: conversation_details.recipient.user_uid,
type: "TEXT",
text: `About Me - ${ExpertProfile.bio}`,
user: {
_id: conversation_details.recipient.user_uid,
}
},
{
_id: uuid.v4().toString(),
conversation_id: conversation_details.conversation_id,
created_at: new Date(),
from: conversation_details.recipient.user_uid,
type: "TEXT",
text: `My name is ${conversation_details.recipient.name}`,
user: {
_id: conversation_details.recipient.user_uid,
}
},
{
_id: uuid.v4().toString(),
conversation_id: conversation_details.conversation_id,
created_at: new Date(),
from: conversation_details.recipient.user_uid,
type: "TEXT",
text: `Hi ${user.full_name}`,
user: {
_id: conversation_details.recipient.user_uid,
}
}]
setMessages(previousMessages => GiftedChat.append(previousMessages, messages))
} catch (error) {
console.log("try -> set welcome msg");
console.log(error);
return
}
}
return (
<View style={styles.maincontainer}>
<Spinner
visible={loading}
textContent={'Loading...'}
textStyle={{ color: '#FFF' }}
/>
<GiftedChat
messages={messages}
onSend={messages => onSend(messages)}
user={{
_id: currentuser.user_uid,
}}
isTyping={false}
scrollToBottom={true}
showAvatarForEveryMessage={true}
renderAvatar={() => null}
/>
</View>
);
}
export default Chat;
const styles = StyleSheet.create({
maincontainer: {
flex: 1,
},
});
When axios returns, it usually give the response as res.data, so in your case, try either res.data or res.data.yourToken (I'm not sure how it's your object).
Gurav,
As far as your code above, The api call's will trigger even before you get currentuser or loginToken. You have to handle the api call after getting the currentuser and loginToken. This can be gracefully handled with async, await.
example code:
useEffect(() => {
getData()
}, [])
useEffect(() => {
if(login_token && currentuser) {
//The api call goes here after you get the logintoken andcurrentuser.
// The above condition is just an example but will vary based on your requirements
}
}, [login_token, currentuser])
const getData = async () => {
await getlogintoken()
await getcurrentuser()
}
const getlogintoken = async () => {
await AsyncStorage.getItem("login_token").then(res => {
if (res != null) {
setLoading(false)
setlogin_token(res)
}
else alert("No login token found")
})
}
const getcurrentuser = async () => {
await AsyncStorage.getItem("currentuser").then(res => {
if (res != null) setcurrentuser(res)
else alert("Current user not found")
})
}

global store plugin error with vue 3 condition

With vue 3, I have state actions in different files globally, but they do not perform the necessary checks in the vue connector. I don't understand where I am doing wrong. I will be happy if you help.
I am waiting for your help to see the mistakes I have made. I am open to any comments.
app.js
require('./bootstrap');
import {createApp} from 'vue';
import App from './App.vue'
import WebsiteRouter from './Website/router';
import AdminRouter from './Admin/router';
import ApplicationRouter from './Application/router';
import store from './store';
axios.defaults.withCredentials = true;
store.dispatch('getUser').then(() => {
createApp(App)
.use(chanceRoute())
.use(store)
.use(DashboardPlugin)
.mount("#app");
})
import { createStore } from 'vuex'
import app from './app'
import appConfig from './app-config'
export default new createStore({
modules: {
app,
appConfig,
}
})
app\index.js
import axios from 'axios';
import sharedMutations from 'vuex-shared-mutations';
export default {
state: {
user: null
},
getters: {
user(state) {
return state.user;
},
verified(state) {
if (state.user) return state.user.email_verified_at
return null
},
id(state) {
if (state.user) return state.user.id
return null
}
},
mutations: {
setUser(state, payload) {
state.user = payload;
}
},
actions: {
async login({dispatch}, payload) {
try {
await axios.get('/sanctum/csrf-cookie');
await axios.post('/api/login', payload).then((res) => {
return dispatch('getUser');
}).catch((err) => {
throw err.response
});
} catch (e) {
throw e
}
},
async register({dispatch}, payload) {
try {
await axios.post('/api/register', payload).then((res) => {
return dispatch('login', {'email': payload.email, 'password': payload.password})
}).catch((err) => {
throw(err.response)
})
} catch (e) {
throw (e)
}
},
async logout({commit}) {
await axios.post('/api/logout').then((res) => {
commit('setUser', null);
}).catch((err) => {
})
},
async getUser({commit}) {
await axios.get('/api/user').then((res) => {
commit('setUser', res.data);
}).catch((err) => {
})
},
async profile({commit}, payload) {
await axios.patch('/api/profile', payload).then((res) => {
commit('setUser', res.data.user);
}).catch((err) => {
throw err.response
})
},
async password({commit}, payload) {
await axios.patch('/api/password', payload).then((res) => {
}).catch((err) => {
throw err.response
})
},
async verifyResend({dispatch}, payload) {
let res = await axios.post('/api/verify-resend', payload)
if (res.status != 200) throw res
return res
},
async verifyEmail({dispatch}, payload) {
let res = await axios.post('/api/verify-email/' + payload.id + '/' + payload.hash)
if (res.status != 200) throw res
dispatch('getUser')
return res
},
},
plugins: [sharedMutations({predicate: ['setUser']})],
}
app-config\index.js
import { $themeConfig } from '../../themeConfig'
export default {
namespaced: true,
state: {
layout: {
isRTL: $themeConfig.layout.isRTL,
skin: localStorage.getItem('skin') || $themeConfig.layout.skin,
routerTransition: $themeConfig.layout.routerTransition,
type: $themeConfig.layout.type,
contentWidth: $themeConfig.layout.contentWidth,
menu: {
hidden: $themeConfig.layout.menu.hidden,
},
navbar: {
type: $themeConfig.layout.navbar.type,
backgroundColor: $themeConfig.layout.navbar.backgroundColor,
},
footer: {
type: $themeConfig.layout.footer.type,
},
},
},
getters: {},
mutations: {
TOGGLE_RTL(state) {
state.layout.isRTL = !state.layout.isRTL
document.documentElement.setAttribute('dir', state.layout.isRTL ? 'rtl' : 'ltr')
},
UPDATE_SKIN(state, skin) {
state.layout.skin = skin
// Update value in localStorage
localStorage.setItem('skin', skin)
// Update DOM for dark-layout
if (skin === 'dark') document.body.classList.add('dark-layout')
else if (document.body.className.match('dark-layout')) document.body.classList.remove('dark-layout')
},
UPDATE_ROUTER_TRANSITION(state, val) {
state.layout.routerTransition = val
},
UPDATE_LAYOUT_TYPE(state, val) {
state.layout.type = val
},
UPDATE_CONTENT_WIDTH(state, val) {
state.layout.contentWidth = val
},
UPDATE_NAV_MENU_HIDDEN(state, val) {
state.layout.menu.hidden = val
},
UPDATE_NAVBAR_CONFIG(state, obj) {
Object.assign(state.layout.navbar, obj)
},
UPDATE_FOOTER_CONFIG(state, obj) {
Object.assign(state.layout.footer, obj)
},
},
actions: {},
}

How to use 'await/async' in 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() }
}
}
}

can't set response from api to messages array of GiftedChat

I am new to react native. I am currently developing a messaging app.
I have used npm-giftedChat for UI & functionalities. The problem is I need to get the response from api & set it to the messages array of giftedchat. I receive data from API and while I set it to messages array it loops over data and renders only the last data in that array.
Any help would be appreciated.I have added my code here
Please find where I am going wrong?
componentWillMount() {
var arrMsg = [];
var data = params.data
for(let i = 0; i < data.Replies.length ; i++){
var obj = {
_id: data.To._id,
text: data.Replies[i].Reply,
createdAt: data.Replies[i].CreatedDate,
user: {
_id: data.From._id,
name: 'React Native',
avatar: data.From.Profile.DisplayPicture
},
image: '',
}
arrMsg.push(obj)
}
this.setState({messages: arrMsg})
}
Sample output
My self also facing same issues..
setting is very important in gifted chat..
so try to use following in ur code,i have edited same like your code.if any queries let me know thanks.
for (let i = 0; i < data.Replies.length; i++) {
console.log(data.Replies[i].CreatedDate);
debugger
var id = data.From._id
if (data.To.id == UserID) {
id = this.state.userID
}
const obj = {
_id: Math.round(Math.random() * 1000000),
text: data.Replies[i].Reply,
createdAt: data.Replies[i].CreatedDate,
user: {
_id: id,
name: 'React Native',
avatar: data.From.Profile.DisplayPicture
},
image: '',
}
arrMsg.push(obj);
};
this.setState((previousState) => {
return {
messages: GiftedChat.append(previousState.messages, arrMsg)
};
});
I wrote a gist here on how to add a web socket listening to a rails channel to a react native chat screen + Gifted Chat
// chat.js
import React, { Component } from 'react';
import {
Text,
View,
StyleSheet,
TouchableHighlight,
Dimensions,
AppState,
AsyncStorage,
Alert
} from 'react-native';
import {
GiftedChat,
Actions,
Bubble,
SystemMessage
} from 'react-native-gifted-chat';
import axios from 'axios';
import ActionCable from 'react-native-actioncable';
import { yourRootUrl, websocketUrl } from '../config/constants';
class Chat extends Component {
state = {
messages: [],
client: '',
accessToken: '',
expiry: '',
uid: '',
userId: ''
}
componentDidMount() {
AsyncStorage.multiGet(
['client', 'expiry',
'access_token', 'uid',
'account_balance', 'userId'
]
)
.then((result) => {
this.setState({
client: result[0][1],
expiry: result[1][1],
accessToken: result[2][1],
uid: result[3][1],
userId: result[5][1]
});
})
.then(() => {
this.getPreviousMessages();
})
.then(() => {
this.createSocket();
})
.catch(() => {
//error logic
});
}
getPreviousMessages() {
//when we open the chat page we should load previous messages
const { chatId } = this.props.navigation.state.params;
const { client, accessToken, uid, userId } = this.state;
const url = yourRootUrl + '/chats/' + chatId;
const headers = {
'access-token': accessToken,
client,
expiry,
uid
};
axios.get(url, { headers })
.then((response) => {
/*
lets construct our messages to be in
same format as expected by GiftedChat
*/
const allMessages = [];
response.data.included.forEach((x) => {
if (x.attributes.system) {
const sysMessage = {
_id: x.id,
text: x.attributes['message-text'],
createdAt: new Date(x.attributes['created-at']),
system: true
};
allMessages.push(sysMessage);
} else {
const userMessage = {
_id: x.id,
text: x.attributes['message-text'],
createdAt: new Date(x.attributes['created-at']),
user: {
_id: x.attributes['sender-id'],
avatar: x.attributes['sender-avatar'],
},
image: x.attributes.image,
};
allMessages.push(userMessage);
}
});
if (allMessages.length === response.data.included.length) {
//lets sort messages according to date created
const sortAllMessages = allMessages.sort((a, b) =>
b.createdAt - a.createdAt
);
this.setState((previousState) => {
return {
messages: GiftedChat.append(previousState.messages, sortAllMessages)
};
});
}
})
}
createSocket() {
//assuming you have set up your chatchannel in your rails backend
const { client, accessToken, uid, userId } = this.state;
const { chatId } = this.props.navigation.state.params; //using react-navigation
const WEBSOCKET_HOST = websocketUrl +
'access-token=' + accessToken + '&client=' +
client + '&uid=' + uid;
const cable = ActionCable.createConsumer(WEBSOCKET_HOST);
this.channel = cable.subscriptions.create(
{
channel: 'ChatChannel',
id: chatId
}, {
received: (data) => {
console.log('Received Data:', data);
if ((data.message.sender_id !== parseInt(userId))
|| (data.message.image !== null)) {
//ensuring you do not pick up your own messages
if (data.message.system === true) {
const sysMessage = {
_id: data.message.id,
text: data.message.message_text,
createdAt: new Date(data.message.created_at),
system: true
};
this.setState((previousState) => {
return {
messages: GiftedChat.append(previousState.messages, sysMessage)
};
});
} else {
const userMessage = {
_id: data.message.id,
text: data.message.message_text,
createdAt: new Date(data.message.created_at),
user: {
_id: data.message.sender_id,
avatar: data.message.sender_avatar,
},
image: data.message.image,
};
this.setState((previousState) => {
return {
messages: GiftedChat.append(previousState.messages, userMessage)
};
});
}
}
},
connected: () => {
console.log(`Connected ${chatId}`);
},
disconnected: () => {
console.warn(`${chatId} was disconnected.`);
},
rejected: () => {
console.warn('connection rejected');
},
});
}
onSend(messages = []) {
const { chatId } = this.props.navigation.state.params;
const { client, accessToken, uid, userId } = this.state;
this.setState((previousState) => {
return {
messages: GiftedChat.append(previousState.messages, messages)
};
});
messages.forEach((x) => {
const url = yourRootUrl + '/messages';
const headers = {
'access-token': accessToken,
client,
expiry,
uid
};
const data = {
chat_id: chatId,
sender_id: userId,
sender_name: name,
message_text: x.text,
image: x.image
};
/*
send the message to your rails app backend
hopefully you have a callback in your model like
after_create :broadcast_message
then broadcast to the chat channel from your rails backend
*/
axios.post(url, data, { headers })
.then(response => console.log(response));
});
}
renderBubble(props) {
return (
<Bubble
{...props}
wrapperStyle={{
left: {
backgroundColor: '#f9f9f9',
}
}}
/>
);
}
renderSystemMessage(props) {
return (
<SystemMessage
{...props}
containerStyle={{
marginBottom: 15,
}}
textStyle={{
fontSize: 14,
textAlign: 'center'
}}
/>
);
}
render() {
return (
<GiftedChat
messages={this.state.messages}
onSend={message => this.onSend(message)}
user={{
_id: parseInt(userId)
}}
renderBubble={this.renderBubble}
renderSystemMessage={this.renderSystemMessage}
/>
);
}
}

React Native + Redux component renders before store action's complete

I have react native application with redux, where user get nevigated to home component after successful login. But home component get rendered before it receive user profile through store. If I use 'Home' component as connected component then on re-render it receives profile.
It is a correct flow or do I able to delay rendering of 'Home' till store is populated with new data.
Here is code
Types
export const FETCH_PROFILE = 'FETCH_PROFILE';
export const UPDATE_PROFILE = 'UPDATE_PROFILE';
export const DELETE_PROFILE = 'DELETE_PROFILE';
export const FETCH_STREAMS = 'FETCH_STREAMS';
Reducer
export default function profile(state = {}, action) {
switch (action.type) {
case types.FETCH_PROFILE:
return {
...state,
profile: action.profile
}
case types.UPDATE_PROFILE:
return {
...state,
profile: action.profile
}
case types.DELETE_PROFILE:
return {
...state,
profile: null
};
default:
return state;
}
}
Actions
var PROFILE_KEY = "#myApp:profile";
export function fetchProfile() {
return dispatch => {
AsyncStorage.getItem(PROFILE_KEY)
.then((profileString) => {
dispatch({
type: types.FETCH_PROFILE,
profile: profileString ? JSON.parse(profileString) : {}
})
})
}
}
export function updateProfile(data) {
return dispatch => {
AsyncStorage.setItem(PROFILE_KEY, JSON.stringify(data))
.then(() => {
dispatch({
type: types.UPDATE_PROFILE,
profile: data
})
})
}
}
export function deleteProfile() {
return dispatch => {
AsyncStorage.removeItem(PROFILE_KEY)
.then(() => {
dispatch({
type: types.DELETE_PROFILE
})
})
}
}
Login Component
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
username: "",
password: "",
error: "",
showProgress: false,
};
}
_focusNextField(nextField) {
this.refs[nextField].focus();
}
_onLoginPressed() {
this.setState({showProgress: true});
this._login();
}
async _login() {
try {
let response = await fetch( BASE_URL + url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
user: {
email: this.state.username,
password: this.state.password,
}
})
});
let res = await response.text();
if (response.status >= 200 && response.status < 300) {
let user = JSON.parse(res);
this.props.updateProfile(user.user);
this.setState({showProgress: false});
this.props.navigator.replace({name: 'Home'});
}
else {
let error = JSON.parse(res);
throw error.errors;
}
} catch(error) {
this.setState({error: error});
this.setState({showProgress: false});
console.log("error " + error);
}
}
render() {
return (
<View style={styles.loginBox}>
<TextInput
ref="username"
value={this.state.username}
placeholder="Username"
keyboardType="email-address"
onChangeText={(username) => this.setState({username}) }
onSubmitEditing={() => this._focusNextField('password')}/>
<TextInput
ref="password"
placeholder="Password"
value={this.state.password}
secureTextEntry={true}
onChangeText={(password) => this.setState({password}) }
returnKeyType="go"/>
<Button textStyle={{fontSize: 14}} onPress={this._onLoginPressed.bind(this)} style={{marginTop: 30}}>
Sign In
</Button>
</View>
);
}
}
const styles = StyleSheet.create({
loginBox: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
alignItems: 'stretch',
margin: 10,
}
});
var {updateProfile} = require('../Actions');
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
module.exports = connect(
null,
(dispatch) => {
return bindActionCreators({updateProfile}, dispatch)
}
)(Login)
Home
class Home extends React.Component {
render() {
return (
<View style={{flex: 1, backgroundColor: '#fff'}}>
<Text style={{margin: 10, fontSize: 15, textAlign: 'left'}}>I'm in the Drawer!</Text>
<Text>Auth key : {this.props.profile ? this.props.profile.authentication_token : 'authentication_token'}</Text>
</View>
);
}
}
//module.exports = Home;
import { connect } from 'react-redux';
module.exports = connect(
(state) => {
return {
profile: state.profile
}
},
null
)(Home)
If you're using redux-thunk, you can delay the transition until data is loaded. You need to change some small things.
Add return to action creator.
export function updateProfile(data) {
return dispatch => {
return AsyncStorage.setItem(PROFILE_KEY, JSON.stringify(data))
.then(() => {
dispatch({
type: types.UPDATE_PROFILE,
profile: data
})
})
}
}
add await
if (response.status >= 200 && response.status < 300) {
let user = JSON.parse(res);
await this.props.updateProfile(user.user);
this.setState({showProgress: false});
this.props.navigator.replace({name: 'Home'});
}