I am writing auth for my react app and the function that fetches the token can be seen below
axios.post("mywebsite.com/api/token-auth", {
username: this.state.username,
password: this.state.password
})
.then((fulfilled) => {
userData = fulfilled.data;
AsyncStorage.setItem("userData ", JSON.stringify(userData))
.then(
() => {
this.scaleAnimationDialog.dismiss();
// Set timeout is added to makesure the animation closes properly before navigating.
setTimeout(
() => this.props.navigation.navigate("Home", userData), 500
);
}
)
.catch(
() => {
alert("Something went wrong, please try again");
this.scaleAnimationDialog.dismiss();
}
)
})
.catch((error) => {
this.scaleAnimationDialog.dismiss();
alert("Authentication failed please try again");
});
When I try to login with valid credentials, it redirects to Home Screen as expected but if I refresh the app, it again goes to the LoginScreen as the AsyncStorage doesn't have key 'userData',
When the app fires up, LoadingScreen is the first component to load and which decides what screen to load. I implemented this as per react-navigation documentation and the code is as follows
_bootstrapAsync = async () => {
const userDataString = await AsyncStorage.getItem('userData');
const userDataObject = JSON.parse(userDataString);
// This will switch to the App screen or Auth screen and this loading
// screen will be unmounted and thrown away.
this.props.navigation.navigate(userDataString ? "Home":"AuthNavigator", userDataObject);
};
What am I doing wrong here. I am new to using promises, Please keep the answer more descriptive.
I also tried async await with try/catch but in that case the code never went past await and the loading popup never goes off. Please point out if my code is wrong and also suggest the appropriate solution.
Thanks!
How are you checking for the key in AsyncStorage?
Do it before the component renders, using the lifecycle method componentWillMount.
Something like this:
componentWillMount() {
AsyncStorage.getItem('userData')
.then(userData => {
this.setState({ userData });
})
.catch((err) => {
// Handle error here (maybe fetch user?)
})
}
Then, inside the render method render accordingly this.state.userData.
Hope this makes sense :)
After some digging, I found out there is this issue with AsyncStorage for lot of other people, thanks to #Filipe for pointing to the right thread. After a lot of struggle I've decided to just use localstorage like sqlite or something and found this library https://github.com/sunnylqm/react-native-storage. It implements AsyncStorage internally and provides a wrapper to set and get data. Somehow this seems to work for me. If anyone is facing the same issue as me. Give that library a try.
Related
I want to create a ticket functionality. On the main page (the parent) I have a axios get function that once the page is loaded, the useEffect is listing all the tickets components.
On the second page I have the child component where I imported with props the reloadTickets={fetchTickets} method to be used every time I have added a new ticket.
The problem is that If I add a post, one time the post is not added, second time the post is added twice and third time the post is adding too many times on a single form submission. Sometime the post is not added at all, but after I try more times, the post is added twice and more.....
This is my PARENT COMPOENTN
This is my Child component
PS. Sorry for the pictures. But react code cannot be formated to look good on my page.
Well the problem is you are not waiting for axios call to finish, since axios.post method is asynchronous.
You need to make your function async and await axios.post method, here is an example how you can do this.
const createTicket = async (e) => {
e.preventDefault();
try {
await axios.post(postTicketURL, {
userID: userID,
content: ticketArea
}, configHeader);
await reloadTickets();
}
catch (error) {
console.log(error);
}
}
Update: I see that you are already using .then and .catch approach in your parent component, so you can solve this with that like this:
const createTicket = async (e: any) => {
e.preventDefault();
axios.post(postTicketURL, {
userID: userID,
content: ticketArea
}, configHeader).then((res: any) => {
console.log(res);
reloadTickets();
}).catch((err: any) => {
console.log(err);
});
}
But I would recommend async await approach, and also you should refactor your code inside TicketsComponent -> fetchTickets to match that.
Why?
Because async await is much more clear and modern approach and you are avoiding potential callback hell, for example if you now want to wait reloadTickets function to end properly. You can find out more about this topics here:
https://www.loginradius.com/blog/async/callback-vs-promises-vs-async-await/
I have one problem and have searched a lot and none of the solution is working for me on the forum.
I call 6 API's on the screen (there is no loader) and there is one button at bottom which navigates user to another screen.
i have added coded in componentDidMount() ,
componentDidMount() {
this.props.API1();
this.props.API2();
this.props.API3();
}
However until i All three API's response comes in getderivedatatefromprops , the button at bottom used to navigate to another screen does not work.
this.props.API1() are redux actions defined as below
export const API1 = () => {
return (dispatch) => {
new Promise(async () => {
const body = {
method: 'GET',
url: "get url of API",
};
axios(body)
.then((response) => {
dispatch({
type: 'GETRESPONSE',
payload: { response: response.data, error: false },
});
})
.catch((error) => {
dispatch({
type: 'GETRESPONSE',
payload: { response: error?.response, error: true },
});
});
});
};
};
The problem is that you are calling async calls directly inside redux actions, which is not what redux was made to do.
You should use either redux-sage or redux-thunk if that is what you want to do.
Also, I don't see in your design why you would want to make API1, 2, and 3 redux actions. Actions are meant to store date, not for triggering events that will store data. So you could just call the API calls, and then once they return send the data to redux.
I think it is too late to answer but if somebody have this kind of problems just be sure if you do not have to many re-renders. Even if you read the documentation React Native Performance Tips. There was written that too many re-renders will cause frames to be dropped.
Just make sure that you do not have to many re-renders, use Purecomponents and don`t change state on complex components many times. I hope it would be help full.
Note: I am using react js and all my code is within a react component class.
I want to check if someone is an admin, so I made this function:
makeDatabaseCall = () => {
let userDB = this.props.firestore.collection('user');
let queryUserDB = userDB.where('userIdentifier', '==', this.props.auth.uid).limit(1).get()
.then(snapshot => {
console.log(snapshot.docs[0].data().clearance)
return (
this.setState({
clearance: snapshot.docs[0].data().clearance
})
)
})
.catch(err => {
console.log('Error getting documents', err);
});
}
But I need to use this.props.auth.uid, so I need to wait until the auth is loaded.
I don't want to put this inside an if statement in my render() because that would make my code inefficient (it would keep checking forever).
How can I use the concept of promises to make an asynchronous call to firebase, see if the auth is loaded, and then run the function above?
Also here is my auth code:
const mapStateToProps = (state) => {
return {
auth: state.firebase.auth
}
}
export default compose(
connect(mapStateToProps),
firestoreConnect([
{ collection: 'events',
collection: "user"
}
])
)(Dashboard)```
That's actually not the strategy you want to use. You should use an auth state listener as described in the documentation to receive a callback whenever the user signs in or out. You can use this callback to determine exactly when a user is signed in, and get the UID for use in your query. You probably don't want to try to convert this into a promise, because you are given no guarantee when a user actually takes this action in your app. Your listener should kick off any queries (or send other signals using whatever frameworks you use, such as RxJS) as necessary when the user signs in.
I'm using react hooks and need to do a page refresh on state change.
I tried using window.location.reload() but this doesn't result in a proper refresh.
const responseGoogle = async googleData => {
await userLogin(googleData);
setLoggedIn(true);
window.location.reload()
};
Only a manual F5 refresh does the job!
Is there anything that's like a full refresh in React?
Any help is much appreciated.
If you mean just to re-render the page you can use hooks function,
const [dummy, reload] = useState(false);
and when you want to re-render just call the reload function
reload(!dummy);
just remember to import the useState from react;
Maybe you are running the window.location.reload() even the await promise fails. I would suggest to use trycatch in async functions to easily determine if it's successful or not.
You may try this code below
const responseGoogle = async googleData => {
try {
await userLogin(googleData);
setLoggedIn(true);
window.location.reload() //this will run if the await userLogin is succeeded
} catch (error) {
console.log(error); //will show an error if it fails
}
};
Actually all I had to do was to pass a boolean into the function: window.location.reload(true) to force the reload.
While working on a side project, I faced an issue with react-router-dom.
What I want to implement is: When I submit a Form, I need to save the data on my server. While the request is pending, I need to display a loading indicator. Once the server says everything is ok, I need to redirect the user on a new page
action.js
export const addNotification = value => async dispatch => {
dispatch(addNotificationPending())
try {
const response = await client.createNotification(values)
dispatch(addNotificationSuccess(response))
} catch(e) {
dispatch(addNotificationFailure())
}
}
component.js
class CreateNotificationForm extends Component {
onSubmit = (values) => {
this.props.addNotification(parameters, history)
}
render() {
const { isCreating } = this.props
const submitBtnText = isCreating ? 'Creating...' : 'Submit'
return (
<Form>
// content omitted
<Submit value={submitBtnText} />
</Form>
)
}
}
const mapStateToProps = (state) => ({
isCreating: getIsFetching(state)
})
const mapDispatchToProps = (dispatch) => ({ // omitted })
connect(mapStateToProps, mapDispatchToProps)(CreateNotificationForm)
So far so good: When I submit my form, the form's submit button shows a Creating... text.
However, how do I tell react-router to load a new path once the request is successful?
Right now, I've done that by using withRouter and using this.props.history as a second argument for this.props.addNotification.
It works great, but it seems really wrong
I've seen solutions using react-router-redux, but I don't really want to add a new middleware to my store.
Should I make the API call inside my component and use a Promise?
Any help?
Update:
After working a little on my own React project, and thinking about similar situations where I handle route changes there, I decided I want to change my original answer. I think the callback solution is OK, but the solution that you already mentioned of making the API call inside your component and using a promise is better. I realized that I've actually been doing this in my own app for a while now.
I use redux-form in my app, and it provides onSubmitSuccess/onSubmitFail functions that you can use to handle the submit result, and each of those rely on you returning a promise (usually from your action creator).
I think the fact that one of the most popular packages for form submission in React/Redux supports this pattern is an indication that it's probably a good pattern to use. Also, since react-router passes history into your component, it seems logical that they expect most people to do a lot of their programmatic route changes inside the component.
Here's an example of what the promise solution would look like with your code:
action.js
export const addNotification = value => dispatch => {
return new Promise(async (resolve, reject) => {
dispatch(addNotificationPending())
try {
const response = await client.createNotification(values)
dispatch(addNotificationSuccess(response))
resolve(response)
} catch(e) {
dispatch(addNotificationFailure())
reject(e)
}
})
}
component.js
onSubmit = async () => {
try {
await this.props.addNotification(parameters)
this.props.history.push('/new/route')
} catch(e) {
// could use a try/catch block here to display
// an error to the user here if addNotification fails,
// or go to a different route
}
}
Old Answer:
A simple solution would be to allow addNotification() to accept a callback function as an optional second argument.
export const addNotification = (value, callback=null) => async dispatch => {
dispatch(addNotificationPending())
try {
const response = await client.createNotification(values)
dispatch(addNotificationSuccess(response))
(typeof callback === 'function') && callback()
} catch(e) {
dispatch(addNotificationFailure())
}
}
Then inside your component use the router to go to the new route.
onSubmit = (values) => {
this.props.addNotification(parameters, () => {
this.props.history.push('/new/route')
})
}
You should not write your asynchronous calls in reducers or actions as the documentation clearly suggests them to be pure functions. You will have to introduce a redux-middleware like redux-thunk or redux-saga (I personally prefer sagas)
All your async calls will happen inside the middleware, and when it succeeds, you can use react-routers history .replace() or .push() methods to update your route. Let me know if it makes sense
You can use one popular package axios
See Here https://www.npmjs.com/package/axios
and you can implement your login like
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
You can write your loader login while calling api
and then you can hide your loader in .then