Reputation: 137
I am new to React Native and I am currently trying to fetch credential data from a website and save it into device memory. However I am encountering few issues regarding it!
My flow:
When app is launched the app will check asyncStorage if there are credentials.
If so, we go straight into the app. Otherwise we enter the RootStackScreen(aka login Screen)*** (issue #1)
(LOGIN) A user will enter his/her login into text input which would be saved in a useState.
Using a function 'loginHandle' a fetch request is called using the info from useState and we setCredential with the data returned.** (issue #2)
The app should rerender and see that there is a credential and load into the main screen. *** (issue #3)
However, I'm encountering 2 issues.
*** Even when asyncStorage does contain the credentials when I check appLoad.credentials in the return(...) it returns a null. However, it returns the correct value in my useEffect(). I thought by using useEffect() I am calling the functions during ComponentsDidMount which is before the rendering of the screen. So shouldn't appLoad.credentials contain a string?
On my console.log(credential) It is always one step behind. I.e. I press the login button once. The console log will return null. However, I thought that since the console.log() is after the fetch command that the credentials should be set before the console.log call!
How do I get the app to rerender. I've seen online regarding force rerendering but I heard that it was bad!
App.js
import AsyncStorage from '@react-native-community/async-storage';
...
export default function App() {
const STORAGE_KEY = '@deviceCredentials';
const [data, setData] = useState('');
const appLoad = {
isLoading: true,
credentials: null
};
//Runs during ComponentsDidMount
useEffect(() => {
setTimeout(async () => {
//In 1000ms try to get credentials
try {
appLoad.credentials = await AsyncStorage.getItem(STORAGE_KEY);
console.log(appLoad.credentials);
} catch (e) {
console.log(e);
}
}, 1000);
}, []);
//Render
return (
<NavigationContainer>
{/* If credentials is not null then we can go straight into Main screen,
otherwise we go to Login Screen */}
{appLoad.credentials !== null ? (
<Drawer.Navigator drawerContent={props => <DrawerContent {...props} />}>
<Drawer.Screen name="MusteringDrawer" component={MainTabScreen} />
</Drawer.Navigator>
)
:
<RootStackScreen />
}
</NavigationContainer>
);
};
SignIn.js
...
...
const [data, setData] = useState({
username: '',
password: '',
...
})
const [credential, setCredential] = useState({ data: null });
const STORAGE_KEY = '@deviceCredentials';
...
...
//Handles user data
const handleValidUser = (val) => {
setData({
...data,
username: val,
});
}
const loginHandle = () => {
fetch(url + data.password + data.username)
.then(x => x.text())
.then(y => {
setCredential({ data: y });
});
console.log(credential);
AsyncStorage.setItem(STORAGE_KEY, credential);
}
return(
<Text>Username</Text>
<TextInput
placeholder="Your Username"
onChangeText={(val) => handleValidUser(val)}
onEndEditing={(e) => handleValidUser(e.nativeEvent.text)}
/>
<Text>Password</Text>
<TextInput
placeholder="Your Password"
onChangeText={(val) => handlePasswordChange(val)}
/>
<View style={styles.button}>
<TouchableOpacity onPress={() => { loginHandle(); }}>
<Text>Sign In</Text>
</TouchableOpacity>
</View>
Upvotes: 3
Views: 2353
Reputation: 4141
Are you aware of the fact that:
const appLoad = {
isLoading: true,
credentials: null
};
is re-defined every render? Even more so, if the value gets set in the effect hook, react has no way of knowing that it was changed, thus will not re-render. And if it does re-render (for some entierly different reason, this will not re-render)... well, new variable :D.
What you want is remembering data like this across all renders. You do this, as you surely know, by using the useState
hook.
export default function App() {
// Remove this, seems to be a leftover from testing
// const [data, setData] = useState('');
const [isLoading, setIsLoading] = useState(true);
const [credentials, setCredentials] = useState(null);
// Check for credentials in the first render
useEffect(() => {
// 1. I'm not sure why you add a delay, It will probably only make your app appear sluggish
// 2. Use effect callbacks should not be made async, see this post
// https://stackoverflow.com/questions/53332321/react-hook-warnings-for-async-function-in-useeffect-useeffect-function-must-ret
async function fetchCredentials() {
setCredentials(await AsyncStorage.getItem(STORAGE_KEY));
setIsLoading(false);
}
fetchCredentials();
}, []);
// Whatever your render code may be
return null;
}
It may very well be that I have not solved your problem (I may miss understood you, and most of the time I'm generally a bit stupid). In this case, don't hesitate to get back to me via comments.
Cheers and happy coding!
Upvotes: 3