undefined is not an object (evaluating 'userInfo._name') help me - react-native

I'm asking you this question because an error occurred again.
We're now receiving data through axios and storing that data through useState().
So if you create it on the screen and render it right away, you can see the data, but if you go to the previous page and go back in, an error occurs.
Please let me know how to solve it.
my Error
TypeError: undefined is not an object (evaluating 'userInfo._name')
my Code
import React, { useState, useEffect } from "react";
import { withNavigation } from "react-navigation";
import { Text, View, StyleSheet, SafeAreaView, Image } from "react-native";
import {
widthPercentageToDP as wp,
heightPercentageToDP as hp,
} from "react-native-responsive-screen";
import axios from "axios";
const MyPage = ({ navigation, info }) => {
const [userInfo, setUserInfo] = useState();
const getData = async () => {
try {
axios
.get(
"http://everyweardev-env.eba-azpdvh2m.ap-northeast-2.elasticbeanstalk.com/api/v1/user"
)
.then((res) => res)
.then((data) => {
setUserInfo(data.data.data.result);
})
.catch((err) => console.log(err));
} catch (error) {}
};
useEffect(() => {
const unsubscribe = navigation.addListener("focus", () => {
getData();
});
return unsubscribe;
}, [navigation]);
return (
<View>
{/* <Image source={require("../../images/profile.png")} /> */}
<Text>{userInfo._name}</Text>
<Text>{userInfo._mail}</Text>
</View>
);
};
export default withNavigation(MyPage);

The problem is happening in the initial render where the userInfo object is null.
Do something like below, where you access the property only when there is a value for userInfo
<Text>{userInfo?._name}</Text>
<Text>{userInfo?._mail}</Text>

Related

Context API dispatch not called with onEffect while using expo-splash-screen

When I am trying to use the dispatch function recieved with the useContext hook I cannot get the change the content of the data inside the context. It looks like as if the call wasn't even made, when I try to log something inside the conext's reducer it doesn't react. When I try to call it from other components, it works just fine.
Sorry if it's not clean enough, I'm not too used to ask around here, if there's anything else to clarify please tell me, and I'll add the necessary info, I just don't know at the moment what could help.
import { QueryClient, QueryClientProvider } from "react-query";
import LoginPage from "./src/pages/LoginPage";
import { UserDataContext, UserDataProvider } from "./src/contexts/UserData";
import { useState } from "react";
import AsyncStorage from "#react-native-async-storage/async-storage";
import { useContext } from "react";
import * as SplashScreen from "expo-splash-screen";
import { useEffect } from "react";
import { useCallback } from "react";
import { UserData } from "./src/interfaces";
SplashScreen.preventAutoHideAsync();
const queryClient = new QueryClient();
export default function App() {
const [appReady, setAppReady] = useState<boolean>(false);
const { loggedInUser, dispatch } = useContext(UserDataContext);
useEffect(() => {
async function prepare() {
AsyncStorage.getItem("userData")
.then((result) => {
if (result !== null) {
console.log(loggedInUser);
const resultUser: UserData = JSON.parse(result);
dispatch({
type: "SET_LOGGED_IN_USER",
payload: resultUser,
});
new Promise((resolve) => setTimeout(resolve, 2000));
}
})
.catch((e) => console.log(e))
.finally(() => setAppReady(true));
}
if (!appReady) {
prepare();
}
}, []);
const onLayoutRootView = useCallback(async () => {
if (appReady) {
await SplashScreen.hideAsync();
}
}, [appReady]);
if (!appReady) {
return null;
}
return (
<>
<UserDataProvider>
<QueryClientProvider client={queryClient}>
<LoginPage onLayout={onLayoutRootView} />
</QueryClientProvider>
</UserDataProvider>
</>
);
}
I'm thinking I use the context hook too early on, when I check the type of the dispatch function here it says it's [Function dispatch], and where it works it's [Function bound dispatchReducerAction].
I think the problem might come from me trying to call useContext before the contextprovider could render, but even when I put the block with using the dispatch action in the onLayoutRootView part, it didn't work.

How to change routes when auth status changes in React Native

What I need to do is to render the react native routes based on the users auth status. Right now I am doing this the wrong way, by having an interval running to check for auth status change:
import React, { useState, useEffect } from 'react';
import { AppLoading } from 'expo';
import { checkAuth } from './auth';
import { LoggedInRoutes, LoggedOutRoutes } from './router';
export default () => {
const [isReady, setReady] = useState(false);
const [loggedIn, setLoggedIn] = useState(false);
useEffect(() => {
setInterval(() => {
checkAuth()
.then((res) => { setLoggedIn(res); setReady(true); console.log('checked..') })
.catch((err) => alert(err));
}, 1500);
}, [loggedIn]);
if (!isReady) {
return (
<AppLoading
onFinish={() => setReady(true)}
/>
);
}
return (
loggedIn ? <LoggedInRoutes /> : <LoggedOutRoutes />
);
}
But obviously that is quite bad. I am using async storage to save the user when he authenticates and remove him from storage when he clicks the logout button.
Is there a way to check for changes in async storage and re-render the routes? or run a function that changes loggedIn state when user click login/logout button?
I would recommend to use switchNavigator in react navigation
reactnavigation.org/docs/4.x/auth-flow – mr-nobody 40 secs ago Edit Delete
this approach will works like a charm.
import React, {useState, useEffect} from 'react';
import OnBoardingRoutes from './onBoarding.routes';
import AppRoutes from './app.routes';
import checkFirstUsage from "./checkFirstUsage/path";
const Routes: React.FC = () => {
const [loading, setLoading] = useState(true)
const [firstUsage,setFirstUsage] =useState(null);
useEffect(() => {
async function check() {
const fU = await checkFirstUsage()
setFirstUsage(fU)
setLoading(false)
}
check()
},[])
if (loading) return null // or any better component
return firstUsage ? <OnBoardingRoutes /> : <AppRoutes />;
};
export default Routes;

useEffect returns unhandled promise

I have been for several hours trying to get an API to be called in ReactNative useEffect hook. Sometimes when I restart my app the value is resolved. But most of the time, I have an Unhandled promise rejection. I googled and tried various methods. I tried using .then etc.. I just can't figure it out.
import React, { useState, useContext, useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, FlatList } from 'react-native';
import { EvilIcons } from '#expo/vector-icons';
import jsonServer from '../api/jsonServer';
const ShowScreen = ({ navigation }) => {
const id = navigation.getParam('id');
const [post, setPost] = useState([]);
const getBlog = async () => {
const result = await jsonServer.get(`http://0.0.0.0/blog/docroot/jsonapi/node/article/${id}`);
return result;
}
useEffect(() => {
async function setToState() {
const val = await getBlog();
setPost(val);
}
setToState();
},[]);
return (
<View>
<Text>Here { console.log(post) }</Text>
</View>
);
};
ShowScreen.navigationOptions = ({ navigation }) => {
return {
headerRight: (
<TouchableOpacity
onPress={() =>
navigation.navigate('Edit', { id: navigation.getParam('id')
})}
>
<EvilIcons name="pencil" size={35} />
</TouchableOpacity>
)
};
};
const styles = StyleSheet.create({});
export default ShowScreen;
What you could do is something like this:
....
....
const [post, setPost] = useState([]);
const [isMounted, setIsMounted] = useState(false);
const getBlog = async () => {
const result = await jsonServer.get(`http://0.0.0.0/blog/docroot/jsonapi/node/article/${id}`);
return result;
}
useEffect(() => {
setIsMounted(true)
async function setToState() {
// using try catch I'm handling any type of rejection from promises. All errors will move to catch block.
try{
const val = await getBlog();
// checking if component is still mounted. If mounted then setting a value. We shouldn't update state on an unmounted component.
if(isMounted){
setPost(val);
}
} catch(err){
console.log("Error", err)
}
}
setToState();
return () => {
// Setting is mounted to false as the component is unmounted.
setIsMounted(false)
}
},[]);
I believe this will solve your Unhandled promise rejection error. Please try if it still doesn't solve the issue will create the same in Sanck.
I think my issue was not just promise, the issue is also seems to be me not handling undefined/null in the state. The below code is working for me.
import React, { useState, useContext, useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, FlatList } from 'react-native';
import { EvilIcons } from '#expo/vector-icons';
import jsonServer from '../api/jsonServer';
const ShowScreen = ({ navigation }) => {
const id = navigation.getParam('id');
const [post, setPost] = useState([]);
const getBlog = async () => {
const result = await jsonServer.get(`http://hello.com/jsonapi/node/article/${id}`).then(
res => {
setPost(res)
return res;
}, err => {
console.log(err);
});
}
useEffect(() => {
setPost(getBlog());
},[]);
return (
<View>
<Text>{ post.data ? post.data.data.id : "" }</Text>
</View>
);
};
export default ShowScreen;
Note: I am setting the state in useEffect as well as in the request. I am yet to check if I can just do it once.

Redux | How do I properly connect to the redux store

I got my redux store to work but only if I import the store in the action creator file and call store.dispatch() from the action creator. Apparently calling store.dispatch() isn't a good practice and if I connect the function properly to the store, then I can just call dispatch() instead of store.dispatch(). That said, when I tried to just use dispatch(), I got the error: "Can't find variable: dispatch".
My best guess is that my issue is that I'm not using Redux's connect() function properly at the bottom of this code:
import React, {useState} from 'react'
import { connect } from 'react-redux'
import { StyleSheet, View, Text, TextInput, Image, TouchableOpacity } from 'react-native';
import { signup } from '../../actions/authAction'
const ApprovedScreen = () => {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
return (
<View>
<TextInput
style={styles.textInput}
value={email}
onChangeText={text=>setEmail(text)}
/>
<TextInput
value={password}
onChangeText={text=>setPassword(text)}
/>
<TouchableOpacity onPress={()=>signup({email, password})}>
<Text>Create An Account</Text>
</TouchableOpacity>
</View>
)
}
export default connect(null, { signup })(ApprovedScreen)
And here's the { signup } function code:
import axios from 'axios'
import { LOG_IN } from './types'
export function signup ({email, password}) {
console.log('singup function ran')
axios
.post('https://MYAPI.com/signup', {email, password})
.then(response => dispatch({
type: LOG_IN,
payload: response.data.token
}))
.catch(error => {
console.log(error)
})
}
Since you're using redux-thunk, the action needs to return another function, which takes dispatch as an argument:
export function signup({ email, password}) {
return function(dispatch) {
return axios
.post('https://MYAPI.com/signup', {
email,
password
})
.then(response => dispatch({
type: LOG_IN,
payload: response.data.token
}))
.catch(error => {
console.log(error)
})
}
}
Also you need to pass signup function as the prop to your component:
const ApprovedScreen = ({signup}) => {

The redux is giving "is not a function" error - redux sauce

I'm using redux sauce. When I dispatch an action, it is giving me "_HelpRedux.default.request is not a function" error. Any help is appreciated. How can I make it right? I'm out of ideas. Thanks in advance
HelpRedux.js
import { createReducer, createActions } from 'reduxsauce';
import Immutable from 'seamless-immutable';
const { Types, Creators } = createActions({
helpPostt: null,
dashboardRequest: ['data'],
})
export const HelpTypes = Types
export default Creators
export const INITIAL_STATE = Immutable({
fetching: false,
data: null,
})
export const helpRequestPost = (state) => {
return state;
}
export const request = (state) => state.merge({ fetching: true });
export const reducer = createReducer(INITIAL_STATE, {
[Types.HELP_POSTT]: helpRequestPost,
[Types.DASHBOARD_REQUEST]: request,
})
HelpScreen.js
import React, { Component } from 'react'
import { View, Text, KeyboardAvoidingView, ScrollView, TouchableOpacity, TextInput, Linking, Platform } from 'react-native'
import { connect } from 'react-redux';
import HelpRedux from '../Redux/HelpRedux';
class HelpScreen extends Component {
tryLogin = () => {
this.props.request(); //_HelpRedux.default.request is not a function
//this.props.helpRequestPost(); //_HelpRedux.default.helpRequestPost is not a function
}
render () {
console.log("help", this.props);
return (
<View>
<TouchableOpacity onPress={this.tryLogin}>
<Text>abc</Text>
</TouchableOpacity>
</View>
)
}
}
const mapDispatchToProps = (dispatch) => {
return {
request: () => dispatch(HelpRedux.request()),
helpRequestPost: (key) => dispatch(HelpRedux.helpRequestPost(key)),
}
}
export default connect(null, mapDispatchToProps)(HelpScreen)
. .
OK I made a mistake here. Instead of calling an action, I was calling reducer.
request: () => dispatch(HelpRedux.helpPostt()),