Implementing redirect in Redux middleware - redirect

Let's say I have following action:
export function signIn(data) {
return {
type: USER_SIGN_IN,
promise: api.post('/sign_in', data)
}
}
and following middleware:
export default function promiseMiddleware() {
return next => action => {
const { promise, type, ...rest } = action
if (!promise) {
return next(action)
}
const SUCCESS = `${type}_SUCCESS`
const REQUEST = `${type}_REQUEST`
const ERROR = `${type}_ERROR`
next({ type: REQUEST, ...rest })
return promise
.then(res => {
next({ response: res.data, type: SUCCESS, ...rest })
return true
})
.catch(error => {
...
})
}
}
This code is loosely based on https://github.com/reactGo/reactGo/
But what if in then callback after calling next I want to make a redirect to another path?
I did following. I passed redirect url through action:
export function signIn(data) {
return {
type: USER_SIGN_IN,
promise: api.post('/sign_in', data),
redirect: '/'
}
}
and added another call of next method with push from react-router-redux.
import { push } from 'react-router-redux'
export default function promiseMiddleware() {
return next => action => {
const { promise, type, redirect, ...rest } = action
...
return promise
.then(res => {
next({ response: res.data, type: SUCCESS, ...rest })
next(push(redirect))
return true
})
.catch(error => {
...
})
}
}
It seems like it works, but I'm not sure if this is a good idea or if there are some pitfalls of multiple next calls and I shouldn't do like this?
Maybe there are some better approaches for implementing such redirects?

Related

sending request through proxy. request library works, axios does not

I am trying to update some old code to get rid of the request package since it is no longer maintained. I attempted to replace a proxy request with axios, but it doesn't work (I just get a timeout). Am I missing an axios config somewhere? The example using the request package works fine.
FAILS
export function sendAxiosApiRequest(enableProxy, proxyIndex,url,filepath?:string):object {
//https://support.zyte.com/support/discussions/topics/22000014602
let ca='READ IN FILE HERE'
let getOptions = {
url: url,
httpsAgent: tunnel.httpsOverHttp({
ca: ca,
proxy: {
host: 'http://MY_API_KEY:#proxy.crawlera.com',
port: '8011',
},
}),
proxy: false, //disable auto config, bc we set it manually
} as any;
console.log({getOptions})
return new Promise(resolve => {
try {
axios.get(getOptions,(err,response,html)=>{
if(err){
console.log(err);
resolve(false);
}
else {
try{
const output = JSON.parse(html);
resolve(output);
}catch(e){
console.log({html})
throw `ERROR parsing html: `+JSON.stringify(e)
}
}
})
}
catch (e) {
console.log(`Err parsing result from sendApiRequest:`,e);
resolve(false);
}
})
}
WORKS
export function sendRequestApiRequest(enableProxy, proxyIndex,url,filepath?:string):object {
let ca='READ IN FILE HERE'
let getOptions = {
url: url,
jar: true,
followAllRedirects: false,
} as any;
//console.log({filepath})
getOptions.proxy= 'http://MY_API_KEY:#proxy.crawlera.com'
getOptions.ca=ca
getOptions.requestCert =true
getOptions.rejectUnauthorized= true
return new Promise(resolve => {
try {
request.get(getOptions,(err,response,html)=>{
if(err){
console.log(err);
resolve(false);
}
else {
const output = JSON.parse(html);
resolve(output);
}
})
}
catch (e) {
console.log(e);
resolve(false);
}
})
}
Please have a look at the axios docs.
The method signature for the get-requests is axios.get(url[, config]) but your first parameter is actually an object. You might want to use axios({}) and update your getOptions with the missing method key:
let getOptions = {
url: url,
method: 'get', // this was missing!
httpsAgent: tunnel.httpsOverHttp({
ca: ca,
proxy: {
host: 'MY_API_KEY:#proxy.crawlera.com', // http is not needed, but it was http but you use httpsAgent?!
port: 8011,
},
}),
proxy: false,
} as any;

Redux toolkit createAsyncThunk using Parameter

I need your help.
We implemented createAsyncThunk using the Redux Toolkit.
However, as shown in the picture, I need a function to change ChannelId flexibly. Can't I use the parameter in createAsyncThunk?
How can I use it if I can?
Or is there any other way?
I am sorry that the quality of the question is low because I am Korean using a translator.
enter image description here
// ACTION
export const getYoutubeList_PlayList = createAsyncThunk(
"GET/YOUTUBE_PLAYLIST",
async (data, thunkAPI) => {
try {
const { data } = await axios.get<youtubeResponse>(
`https://www.googleapis.com/youtube/v3/playlists?key=${youTubeAcsses.apiKey}&channelId=${channelId}&part=snippet&maxResults=30`
)
return data
} catch (err: any) {
return thunkAPI.rejectWithValue({
errorMessage: '호출에 실패했습니다.'
})
}
}
);
// SLICE
const youtube_PlaylistSlice = createSlice({
name: "YOUTUBE_PLAYLIST",
initialState,
reducers: {},
// createAsyncThunk 호출 처리 = extraReducers
extraReducers(builder) {
builder
.addCase(getYoutubeList_PlayList.pending, (state, action) => {
state.loading = true;
})
.addCase(getYoutubeList_PlayList.fulfilled, (state, action: PayloadAction<youtubeResponse>) => {
state.loading = false;
state.data = action.payload;
})
.addCase(getYoutubeList_PlayList.rejected, (state, action: PayloadAction<any>) => {
state.error = action.payload;
});
},
});
You named both the incoming argument data as well as the result of your axios call. That will "shadow" the original data and you cannot access it any more. Give those two variables different names.
Here I called it arg, which allows you to access arg.channelId.
export const getYoutubeList_PlayList = createAsyncThunk(
"GET/YOUTUBE_PLAYLIST",
async (arg, thunkAPI) => {
try {
const { data } = await axios.get<youtubeResponse>(
`https://www.googleapis.com/youtube/v3/playlists?key=${youTubeAcsses.apiKey}&channelId=${arg.channelId}&part=snippet&maxResults=30`
)
return data
} catch (err: any) {
return thunkAPI.rejectWithValue({
errorMessage: '호출에 실패했습니다.'
})
}
}
);
You would now dispatch this as dispatch(getYoutubeList_PlayList({ channelId: 5 }))

How can i make the interceptor run a function on error exept for one specific request?

this is my interceptor:
axios.interceptors.response.use(
(response) => {
if (error.response?.status === 403) {
unstable_batchedUpdates(() => {
// to force react state changes outside of React components
useSnackBarStore.getState().show({
message: `${i18n.t('forbidden')}: ${error.toJSON().config.url}`,
severity: 'error',
})
})
}
return Promise.reject(error)
}
)
I want this behavior all the time except when I make this specific call or at least except every head call
export const companiesQueries = {
headCompany: {
name: 'headCompany',
fn: async (companyId) => {
return await axios.head(`/companies/${companyId}`)
},
},
fixed by applying these changes to the api call:
const uninterceptedAxiosInstance = axios.create()
headCompany: {
name: 'headCompany',
fn: async (companyId) => {
return await
uninterceptedAxiosInstance.head(`/companies/${companyId}`)
},
}

How can I write conditional interceptor in Axios

I am able to add an interceptor for the Axios pipeline. Also, I need the loader to be conditional based. The situation is some requests can run in the background and don't need a loader to be blocking the UI. In such cases, I will be able to let the Axios know by sending an extra parameter saying isBackground call. How can I achieve this?
axios.interceptors.request.use((config) => {
this.isLoading = true; // Or trigger start loader
return config
}, (error) => {
this.isLoading = false // Or trigger stoploader
return Promise.reject(error)
})
axios.interceptors.response.use((response) => {
this.isLoading = false // Or trigger stoploader
return response
}, function(error) {
this.isLoading = false // Or trigger stoploader
return Promise.reject(error)
})
Just use your own custom property isBackground on the config like this:
axios.interceptors.request.use((config) => {
console.log(config.isBackground)
return config
}, (error) => {
console.log(error.config.isBackground)
return Promise.reject(error)
})
axios.interceptors.response.use((response) => {
console.log(response.config.isBackground)
return response
}, function(error) {
console.log(error.config.isBackground)
return Promise.reject(error)
})
const config = {
isBackground: true
}
axios.get('https://httpbin.org/get', config)
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error)
})
Note that there is a bug in current release 0.19.0 waiting to be fixed, which breaks this functionality. Works ok in version 0.18...
Fiddle

How to set axios token for client side in nuxt server init?

I'm trying to authenticate my user when the page is loading. So I have the following code :
actions: {
nuxtServerInit ({dispatch, commit, app}, context) {
return new Promise((resolve, reject) => {
const cookies = cparse.parse(context.req.headers.cookie || '')
if (cookies.hasOwnProperty('x-access-token')) {
app.$axios.setToken(cookies['x-access-token'], 'Bearer')
api.auth.me2()
.then(result => {
commit('setUser', result.data.user)
resolve(true)
})
.catch(error => {
commit('resetUser')
resetAuthToken()
resolve(false)
})
} else {
resetAuthToken()
resolve(false)
}
})
}
However I have the following error :
Cannot read $axios property of undefined. What is wrong with my code ?
App should come from context e.g. from second argument.
So your code should be
context.app.$axios.setToken(cookies['x-access-token'], 'Bearer')
Another way. You could pass app in the second argument such that
nuxtServerInit ({dispatch, commit}, {app}) {
The complete code:
actions: {
nuxtServerInit ({dispatch, commit}, {app}) {
return new Promise((resolve, reject) => {
const cookies = cparse.parse(context.req.headers.cookie || '')
if (cookies.hasOwnProperty('x-access-token')) {
app.$axios.setToken(cookies['x-access-token'], 'Bearer')
api.auth.me2()
.then(result => {
commit('setUser', result.data.user)
resolve(true)
})
.catch(error => {
commit('resetUser')
resetAuthToken()
resolve(false)
})
} else {
resetAuthToken()
resolve(false)
}
})
}
}