React Navigation - How to access this.props.navigation in axios instance - javascript

I have an axios instance to set my baseURL and set the interceptors but everytime i want to use this.props.navigation i got an error undefined is not an object (this.props.navigation)
here's the code:
// Axios Setting
api.axiosInstance.interceptors.response.use((response)=>{
return response
}, (error) => {
if(error.response == undefined){
return Promise.reject(
Alert.alert("Perhatian","Tidak dapat terhubung ke server \n\n Cek koneksi internet anda",[
{text:"OK", onPress:()=>BackHandler.exitApp()}
])
)
} else if (error.response.status === 401) {
return Promise.reject(
goToLogin("Login", null)
);
}
})
//goToLogin Function on different js :
export function goToLogin(routeName, params){
return(
Alert.alert("Peringatan", "Sepertinya akun ini sudah login di perangkat lain\n\nsilahkan logout dan login kembali untuk dapat mengakses kembali",[
{text:'Logout', onPress:()=>{
console.log(this.props) // i got undefined on this line
// this.props.navigation.dispatch(
// NavigationActions.navigate({
// routeName:routeName,
// key:routeName,
// params:params
// })
// ),
// deleteAllUser()
}}
],{cancelable: false})
)
}
How can i access this.props.navigation when the component (axios) is not on screen?

Your problem is that you are trying to use the navigation prop from functions that are not React Components and probably don't have access to the navigation at all. In these cases, it's recommended to use a Navigation Service as described in the docs
Here is a simple example of how you can use it with axios interceptors to have a better understanding of what I'm suggesting:
https://snack.expo.io/BkLWor8FX
On App.js I make a GET call using axios to a non existent url like "https://www.google.coms". The interceptor will handle the failed request (Interceptors.js) and it will use the NavigationService(NavigationService.js) to redirect to the error screen.
I think you can use something similar in your case. Good luck!

Related

How to invalidate from React-Query mutation?

I am trying to refetch a specific data, which should update after a post request in another API. It's worth mentioning that based on that request two APIs should update and one of them updates, another one not.
I've tried multiple ways to refetch the API based on successful post request, but nothing seems to work.
I tried to also add 2nd option like this, but without success.
{
refetchInactive: true,
refetchActive: true,
}
As it mentioned in this discussion,
maybe "keys are not matching, so you are trying to invalidate something that doesn't exist in the cache.", but not sure that it's my case.
What is more interesting is that clicking the invalidate button in the devtools, the invalidation per se works.
Mutation function:
import { BASE_URL, product1Url, product2Url } from './services/api'
import axios from 'axios'
import { useMutation, useQueryClient } from 'react-query'
export const usePostProduct3 = () => {
const queryClient = useQueryClient()
const mutation = useMutation(
async (data) => {
const url = `${BASE_URL}/product3`
return axios.post(url, data).then((response) => response)
},
{
onSuccess: async (data) => {
return (
await queryClient.invalidateQueries(['prodKey', product1Url]), //this one doesn't work
queryClient.invalidateQueries(['prodKey', product2Url]) //this api refetch/update works
) },
},
)
return mutation
}
What am I doing wrong?
You shold do it like this
onSuccess:(response)=>{
queryClient.setQueryData("your query key",response.data)
}
Or if you just want to invalidate queries
import {querCache} from "react-query"
...
onSuccess:(response)=>{
queryClient.invalidateQueries("your query key")
queryClient.invalidateQueries("your query key")
//you also can refetch data like this
queryCache.refetchQueries("your query key")
}

How to setup like a function should be called only one time even after reload

I'm trying to make a Post request on component Mount. But if user reloads the page or states changes, then the function is called again as I'm useEffect and it sends the request again. But I want any better thing where the Post request should be made once and if even the page refreshes the shouldn't be called again if it has been called.
I'm using the Function base component. and make Post requests using redux.
const Main = () => {
// ....
// Here I'm forcing user to login if there's user is logged in then want to make a silent post request, But it sends request everytime on state change.
useEffect(() => {
getLocalStorage()
if (!userInfo) {
setModalShow(true)
}
if (userInfo) {
dispatch(postRequest())
setModalShow(false)
}
}, [userInfo])
return (
<div>Some JSX </div>
)
}
export default Main
So need your help to fix that issue. Can we use localStorage to store the information either the post request is already have been made or any other better idea?
Best way is to use localstorage, not sure if my placements of setting ang getting value from localstorage are on the right spot.
const Main = () => {
// ....
// Here I'm forcing user to login if there's user is logged in then want to make a silent post request, But it sends request everytime on state change.
useEffect(() => {
getLocalStorage()
// Check if the value of logged is true initiali will be false until the
// first request if made
if (!!localStorage.getItem('logged')) {
setModalShow(true)
}
if (userInfo) {
dispatch(postRequest())
setModalShow(false)
// set the value when the request is finished
localStorage.setItem('logged', true)
}
}, [userInfo])
return (
<div>Some JSX </div>
)
}
export default Main
There is a package named redux-persist that you can save the state, for example in localStorage. You can use this package, and send post request if there is not any data in state.
Using localStorage for that purpose is pretty useful, you can save the information on post request whether it was made or not.
For a basic setup;
this could be like that:
const postRequestStatus = localStorage.getItem('postRequestMade') ? JSON.parse(localStorage.getItem('postRequestMade')) : null
useEffect(() => {
getLocalStorage()
if (!userInfo) {
setModalShow(true)
}
if (userInfo) {
setModalShow(false)
if (!postRequestStatus) {
dispatch(postRequest())
console.log('Post Request Made')
localStorage.setItem('postRequestMade', true)
}
}
}, [userInfo, postRequestStatus])
Here's a catch. As far there is information in localStorage, of postRequestMade true . The request won't be made. So some point on the site you should set any logic to clear it out where it is necessary.
Secondly, What if the request was not successful if there was an error from the server. Then, you should also consider error handling as well. As you mentioned you are using redux and I'm sure there would be Axios as well try the functionality like that:
useEffect(() => {
getLocalStorage()
if (!userInfo) {
setModalShow(true)
}
if (userInfo) {
setModalShow(false)
if (!postRequestStatus) {
dispatch(postRequest())
// That block will take care if request was successful
// After a successful request postRequestMade should be set to true.
if (success) {
console.log('Successful Request')
localStorage.setItem('postRequestMade', true)
}
}
}
}, [userInfo, postRequestStatus, success])

location is not defined error in react + next js?

I am trying to send some text on basic of hosted url (where my build is deployed).but i am getting this error
ReferenceError: location is not defined
here is my code
https://codesandbox.io/s/laughing-mendel-pf54l?file=/pages/index.js
export const getStaticProps = async ({ preview = false, previewData = {} }) => {
return {
revalidate: 200,
props: {
//req.host
name: location.hostname == "www.google.com" ? "Hello" : "ccccc"
}
};
};
Can you show your imports, because it could be that you are importing router from 'next/client'
Assuming that you are using functional-based component
You need to import router as follows:
import {useRouter} from "next/router";
in your function body:
const router = useRouter();
getStaticProps() is executed at build time in Node.js, which has no location global object – Location is part of the browser API. Additionally, because the code is executed at build time, the URL is not yet known.
Change getStaticProps to getServerSideProps (see documentation). This will mean the function is called at runtime, separately for each request.
From the context object passed to getServerSideProps, pull out the Node.js http.IncomingMessage object.
On this object, look for the Host header.
export const getServerSideProps = async ({ req }) => {
return {
props: {
name: req.headers.host === "www.google.com" ? "Hello" : "ccccc"
}
};
};
Note:
I also changed == to ===, as it's generally advised to use the latter. The former can produce some unexpected results because of silent type conversions.
I also removed revalidate, as this is not applicable to getServerSideProps().

Unable to bind 'this' to callback function of AcquireTokenSilent - Azure AD MSAL & ReactJS

I am setting up authentication in ReactJS app using AzureAD MSAL. I am able to obtain id_token and access_token. But while getting access token, I am not able tot refer to local variables via this keyword. I tried to bind 'this' to the call back function but that leads to other issues.
I am implementing all the login functionality as a class.
import { UserAgentApplication } from "msal";
export default class AuthService {
constructor() {
this.applicationConfig = {
clientID: "<clientId>",
authority: "<azureADTenantUrl>"
};
this.scopes = [
"openid",
"<Other scopes>"
];
this.client = new UserAgentApplication(
this.applicationConfig.clientID,
this.applicationConfig.authority,
this.authCallback,
{
redirectUri: "http://localhost:3000/"
}
);
}
login = () => {
this.client.loginRedirect(this.scopes);
};
logout = () => {
this.client.logout();
};
authCallback = (erroDesc, token, error, tokenType) => {
if (tokenType == "id_token") {
this.acquireTokenSilent(this.scopes).then(
function(accessToken) {
console.log(accessToken);
},
function(error) {
console.log(error);
}
);
}
};
}
(this is not the actual error message, but a friendly description)
this.scopes is undefined as 'this' is scoped to UserAgentApplication.
to avoid this, I tried to bind the this to the callback function. I have added the following statement in the constructor.
this.authCallback = this.authCallback.bind(this);
this leads to another error.
(this is not the actual error message, but a friendly description)
this.acquireTokenSilent is undefined and 'this' do not have a definition for client to reference using this.client.acquireTokenSilent
So I have hard coded the scopes in the original code and was able to get access token, but again same problem in the call back. This time 'this' is null in the call back.
I tried to move the authCallback to the react component and pass it as a parameter to the service, but that also has same kind of problems.
Any help with how to configure this properly is really appreciated. thanks.
Try this replacement for authCallback. It doesn't entirely solve the problem, but can get you past the UserAgentApplication's hijacking of "this" object.
authCallback = (erroDesc, token, error, tokenType) => {
const client = window.client as Msal.UserAgentApplication;
if (tokenType == "id_token") {
client.acquireTokenSilent(["openid"]).then(
function(accessToken) {
console.log(accessToken);
},
function(error) {
console.log(error);
}
);
}
};
Alternatively, use the loginPopup function instead of loginRedirect as it does not have "this" problem that still exists in current MSAL v0.2.3.
I was able to get it working in msal 1.1.1. Try this way:
this.msalClient = new Msal.UserAgentApplication(config);
this.msalClient.handleRedirectCallback(authCallback.bind(this));
function authCallback(error,response)
{
if (response.tokenType === 'access_token' && response.accessToken)
{
this.accesstoken = response.accessToken;
}
}

Adding a loading animation when loading in ReactJS

I would like to add a loading animation to my website since it's loading quite a bit when entering the website. It is built in ReactJS & NodeJS, so I need to know specifically with ReactJS how to add a loading animation when initially entering the site and also when there is any loading time when rendering a new component.
So is there a way to let people on my website already, although it's not fully loaded, so I can add a loading page with some CSS3 animation as a loading screen.
The question is not really how to make a loading animation. It's more about how to integrate it into ReactJS.
Thank you very much.
Since ReactJS virtual DOM is pretty fast, I assume the biggest load time is due to asynchronous calls. You might be running async code in one of the React lifecycle event (e.g. componentWillMount).
Your application looks empty in the time that it takes for the HTTP call. To create a loader you need to keep the state of your async code.
Example without using Redux
We will have three different states in our app:
REQUEST: while the data is requested but has not loaded yet.
SUCCESS: The data returned successfully. No error occurred.
FAILURE: The async code failed with an error.
While we are in the request state we need to render the spinner. Once the data is back from the server, we change the state of the app to SUCCESS which trigger the component re-render, in which we render the listings.
import React from 'react'
import axios from 'axios'
const REQUEST = 'REQUEST'
const SUCCESS = 'SUCCESS'
const FAILURE = 'FAILURE'
export default class Listings extends React.Component {
constructor(props) {
super(props)
this.state = {status: REQUEST, listings: []}
}
componentDidMount() {
axios.get('/api/listing/12345')
.then(function (response) {
this.setState({listing: response.payload, status: SUCCESS})
})
.catch(function (error) {
this.setState({listing: [], status: FAILURE})
})
}
renderSpinner() {
return ('Loading...')
}
renderListing(listing, idx) {
return (
<div key={idx}>
{listing.name}
</div>
)
}
renderListings() {
return this.state.listing.map(this.renderListing)
}
render() {
return this.state.status == REQUEST ? this.renderSpinner() : this.renderListings()
}
}
Example using Redux
You can pretty much do the similar thing using Redux and Thunk middleware.
Thunk middleware allows us to send actions that are functions. Therefore, it allows us to run an async code. Here we are doing the same thing that we did in the previous example: we keep track of the state of asynchronous code.
export default function promiseMiddleware() {
return (next) => (action) => {
const {promise, type, ...rest} = action
if (!promise) return next(action)
const REQUEST = type + '_REQUEST'
const SUCCESS = type + '_SUCCESS'
const FAILURE = type + '_FAILURE'
next({...rest, type: REQUEST})
return promise
.then(result => {
next({...rest, result, type: SUCCESS})
return true
})
.catch(error => {
if (DEBUG) {
console.error(error)
console.log(error.stack)
}
next({...rest, error, type: FAILURE})
return false
})
}
}

Categories