Reputation: 102
I was trying to build an Authentication system in React Native using :
this is my system flow: I want only to show the Sign screen when (isAuthenticated == false), when it's true I want to show UserUI Screen
<Provider store={store}>
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerShown:false
}}
>
{isAuthenticated == false ? (
<>
<Stack.Screen name="Sign" component={Sign} />
</>
) : (
<>
<Stack.Screen name="UserUI" component={Tabs} />
</>
)}
</Stack.Navigator>
</NavigationContainer>
</Provider>
also the (isAuthenticated) depends on this function :
React.useEffect(()=>{
const showStorage = async () => {
try {
const userObj = await AsyncStorage.getItem('currentStoredUserState')
if(userObj != null){
let jsonObject = JSON.parse(userObj)
setIsAuthenticated(jsonObject.state)
}else{
setIsAuthenticated(false)
}
keys()
}catch(err){
console.log(err)
}
}
showStorage()
},[itemsLength])
but the problem is that (isAuthenticated) does not change to true when the user authenticates and I have to refresh the app manually -after AsyncStorage is not empty- to access'UserUI' Screen.
as you can see, I set (itemsLength) as dependecy but it does not work as well :
const [itemsLength,setItemLength] = useState(0)
const keys = async () => {
let key = await AsyncStorage.getAllKeys()
setItemLength(key.length)
}
everything above is inside App.js
sign in action :
const setAuthStorage = async (data) => {
try {
let authState = { state: true,currentStoredUser_ : data }
await AsyncStorage.setItem('currentStoredUserState', JSON.stringify(authState))
} catch (err) {
console.log(err)
}
}
export const sign_in = (userSignData) => (dispatch) => {
auth.signInWithEmailAndPassword(userSignData.email, userSignData.password)
.then(userCredential => {
const user = userCredential.user
db.collection('users').doc(auth.currentUser.uid.toString()).get()
.then(doc => {
if (doc.exists) {
setAuthStorage(doc.data())
dispatch({
type: SIGN_IN,
payload: {
authenticated: true,
currentUser: user,
}
})
}
})
}).catch(err => {
console.log(err)
})
}
Sign Hanlder in Sign Screen :
const signHandler = () => {
if (isSignedUp) {
dispatch(sign_in(userSignData))
} else {
dispatch(sign_up(userSignData))
}
}
Solved, but not quite perfect, –
thank you guys for your help I really appreciate it, you asked me to change the rendering condition using redux state but I could not access the state outside the , for this mission I had to subscribe to store changes in the App.js Component and when we say subscribe == listening to every state change and this is not good for the app performance :
store.subscribe(()=>{
/*
I combined two states: rootReducer (for user details) and postReducer(for posts changes)
*/
// making sure it's rootReducer state
if(store.getState().hasOwnProperty('rootReducer')){
//when user sign in or sign up rootReducer.authenticated will change from false to true
if(store.getState().rootReducer.authenticated == true){
// change the rendering condition
setIsAuthenticated(true)
}else{
setIsAuthenticated(false)
}
}
})
I also made some changes inside actions to change the state only after storing data
const setAuthStorage = async (data) => {
try {
let authState = { state: true,currentStoredUser_ : data }
// to provide promise chaining this function will store & return a promise
return await AsyncStorage.setItem('currentStoredUserState', JSON.stringify(authState))
} catch (err) {
console.log(err)
}
}
export const sign_in = (userSignData) => (dispatch) => {
auth.signInWithEmailAndPassword(userSignData.email, userSignData.password)
.then(userCredential => {
const user = userCredential.user
db.collection('users').doc(auth.currentUser.uid.toString()).get()
.then(doc => {
if (doc.exists) {
setAuthStorage(doc.data())
.then(()=>{
dispatch({
type: SIGN_IN,
payload: {
authenticated: true,
currentUser: user,
}
})
})
}
})
}).catch(err => {
console.log(err)
})
}
If a found a better solution I will share it with you guys 🤞
Upvotes: 3
Views: 1347
Reputation: 2014
As per my understanding what you are trying to achieve is,
How do it most similar to what you have:
Edit Adding code to make the solution in question better
App.js file
import AppNavigation from './AppNavigation'
...
<Provider store={store}>
<AppNavigation/>
</Provider>
AppNavigation.js
import {useSelector} from 'react-redux'
...
const AppNavigation =()=>{
const isAuthenticated = useSelector(state=>state.authenticated===true);
return(
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerShown:false
}}
>
{isAuthenticated == false ? (
<>
<Stack.Screen name="Sign" component={Sign} />
</>
) : (
<>
<Stack.Screen name="UserUI" component={Tabs} />
</>
)}
</Stack.Navigator>
</NavigationContainer>
)
}
Upvotes: 2
Reputation: 126
You should display the routes with condition on the redux state and then it will automatically change the routes on login.
Upvotes: 2