React Native Request failed with status code 400 - react-native

Does anyone have experience working with this API:
https://authenticjobs.com/api/docs#introduction
I have been running through a few iterations of implementation in my React Native project and I was originally getting this error:
undefined is not an object (evaluating '_ref.longitude')
And now I am getting this error:
Request failed with status code 400
This is the action creator that is attempting to log the data object of this API request.
import axios from "axios";
import reverseGeoCode from "latlng-to-zip";
import qs from "qs";
import { FETCH_JOBS } from "./types";
const JOB_ROOT_URL = "https://authenticjobs.com/api/?";
const JOB_QUERY_PARAMS = {
api_key: "5634cc46389d0d872723b8c46fba672c",
format: "json"
// latlong: 1,
// radius: 10,
// q: "javascript"
};
const buildJobsUrl = zip => {
const query = qs.stringify({ ...JOB_QUERY_PARAMS, l: zip });
return `${JOB_ROOT_URL}${query}`;
};
export const fetchJobs = region => async dispatch => {
try {
let zip = await reverseGeoCode(region);
const url = buildJobsUrl(zip);
let { data } = await axios.get(url);
dispatch({ type: FETCH_JOBS, payload: data });
console.log(data);
} catch (e) {
console.log(e);
}
};

I got it to work with this refactor, although this is going to kind of take my application in a different direction:
import axios from "axios";
import { FETCH_JOBS } from "./types";
const JOB_ROOT_URL = "https://authenticjobs.com/api/?api_key=";
const JOB_QUERY_PARAMS = {
key: "a446a0eefe6f5699283g34f4d5b51fa0",
method: "aj.jobs.getLocations",
format: "json",
category: "javascript"
};
export const fetchJobs = region => async dispatch => {
try {
const url =
JOB_ROOT_URL +
JOB_QUERY_PARAMS.key +
"&method=" +
JOB_QUERY_PARAMS.method +
"&category=" +
JOB_QUERY_PARAMS.category +
"&format=" +
JOB_QUERY_PARAMS.format;
let { data } = await axios.get(url);
dispatch({ type: FETCH_JOBS, payload: data });
console.log(data);
} catch (e) {
console.log(e);
}
};

Related

Cant use SecureStore in Service Middleware React Native App with Expo

Here is my code
import axios from "axios";
import AuthService from "./auth";
import * as SecureStore from "expo-secure-store";
const API_URL = "MYURL";
const instance = axios.create({
baseURL: API_URL,
token: await SecureStore.getItemAsync("token"),
timeout: 1000,
});
const ServiceMiddleware = {
post: async (url, data) => {
const res = await axios.post(API_URL + url, data);
return res.data;
},
get: async (url, req) => {
const res = await instance.get(url, { params: req });
return res.data;
},
put: async (url, data) => {
const res = await axios.put(API_URL + url, data);
return res.data;
},
delete: async (url) => {
const res = await axios.delete(API_URL + url);
return res.data;
},
};
export default ServiceMiddleware;
I would like to call Expo Secure Store in service middleware to get the token, but when I try to launch the application it gives me error.

Can't upload image using react-native, GraphQL and urql

So I'm trying to send an image to our server with react native using GraphQL query and I don't know why but it always return an error : [CombinedError: [Network] Network request failed].
The query :
import { graphql } from '../../gql';
import { gql, useMutation } from 'urql';
const AddProfilePicture_Mutation = graphql(`
mutation AddPicture_Mutation ($userId: ID!, $picture: Upload!) {
uploadProfilePicture(input: {
userId: $userId
picture: $picture
}) {
id
}
}`);
export const useAddProfilePicture = () => {
const [{fetching, error}, execute] = useMutation(AddProfilePicture_Mutation);
return {
error: !!error,
fetching,
addProfilePicture: execute,
}
}
and the code :
const pictureHandler = async () => {
const options = {
mediaType: 'photo' as MediaType,
includeBase64: true,
selectionLimit: 1,
};
const profilePicture = await launchImageLibrary(options);
if (profilePicture.assets?.[0].fileSize && profilePicture.assets?.[0].fileSize > MAXFILESIZE) {
showError(t('profileScreen.PictureSize'));
}
if (profilePicture.assets?.[0].uri && profilePicture.assets[0].fileName && profilePicture.assets[0].type) {
// const myBlob = await fetch(profilePicture.assets[0].uri).then(r => r.blob());
const blob = new Blob([profilePicture.assets[0].base64 as BlobPart], {
type: profilePicture.assets[0].type,
});
const file = new File([blob], profilePicture.assets[0].fileName, { type: `${profilePicture.assets[0].type}`});
const {error} = await addProfilePicture(
{ userId: userId!, picture: file},
{ fetchOptions: { headers: { 'graphql-require-preflight': '' } } }
);
if (!error) {
showSuccess(t('profileScreen.PictureSuccessAdded'));
navigation.navigate('UserProfile');
} else {
console.log(error);
showError(t('profileScreen.PictureErrorAdded'));
}
};
};
I've been trying everything I found on the web, Formdata, react-native-blob-util and rn-fetch-blob. If I try sending anything else then a File, the server reject it and says for exemple:
Variable 'picture' has an invalid value: Expected type org.springframework.web.multipart.MultipartFile but was java.util.LinkedHashMap]
Update :
After long research and help from other programmers. We never did found the answer. We open a new access point in the backend specifically for the uploaded picture and used a regular fetch post.

change toolkit redux state inside of fetch

I just want to update state outside of component after server give me failed response.
the project is react native .
After a lot of online search on website like this , I learn that i need to import my store and use dispatch method. but when i import storage and dispatch it, it give me this error: Property 'crypto' doesn't exist .
this is my feachService.js code:
import { URL } from 'react-native-url-polyfill';
import { store } from '../store/index'
import { errorSliceActions } from '../store/slices/global/errorSlice'
export default (url, isGet) => {
let headers = {
Referer: new URL(url).origin,
}
headers['X-Requested-With'] = 'XMLHttpRequest'
return fetch(
url,
{
method: (isGet ? 'GET' : 'POST'),
headers: headers
})
.then(function (res) {
return res.json();
})
.then(function (res)
{
if(res && res.isSuccess == false && res.message) {
store.dispatch(errorSliceActions.add(res.message));
}
return {json: function() { return res; }};
})
.catch(function (error) { console.log(error.message); })
};
this is my storage js file:
import { configureStore } from '#reduxjs/toolkit';
import errorSliceReducer from './slices/global/errorSlice';
export const store = configureStore({
reducer: {
errorSlice: errorSliceReducer
}
});
this is my error slice.js file:
import { createSlice } from "#reduxjs/toolkit"
function uuidv4() {
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
);
}
const errorSlice = createSlice({
name: "errorSlice",
initialState: {
errors: [
]
},
reducers:
{
add: (state, action) => {
const newItem = {
id: uuidv4(),
message: action.payload
};
return [...state, newItem];
},
remove: (state, action) => {
return { errors: state.errors.filter(function(item) { return item.id != action.payload; }) }
}
}
})
export const errorSliceActions = errorSlice.actions;
export default errorSlice.reducer
and this is my code for executing fetch request
async function loadInfo (inputSearch, inputPage) {
if(config.dataurl) {
console.log(inputSearch);
setIsLoading(true);
const hasQuestionMark = config.dataurl.indexOf('?') > 0;
let targetUrl = '/test/test' + config.dataurl + (!hasQuestionMark ? '?' : '&');
targetUrl += 'page=' + inputPage;
targetUrl += '&search=' + inputSearch;
console.log(targetUrl);
const response = await feachService(appJSon.baseUrl + targetUrl , true);
const responseData = await response.json();
setIsLoading(false);
if(responseData.pagination)
setPagination(responseData.pagination);
if(!responseData.results)
setItems([]);
else
setItems(responseData.results);
}
}
useEffect(() => {
loadInfo('', 1).catch(function (error) { console.log(error.message); });
}, [])
what am i doing wrong ?

React redux - passing parameters to url - error - Actions must be plain objects

I want to attach params to react redux fetch action and I searched for many days the redux docs, but even after trying out a few things i am getting this error:
[Unhandled promise rejection: Error: Actions must be plain objects. Use custom middleware for async actions.]
https://codesandbox.io/s/fast-framework-ct2fc?fontsize=14&hidenavigation=1&theme=dark
The original action looks like this:
export function fetchArticleDetails() {
return apiAction({
url: "http://myurl/appApi/2.0.0/getData/1", //1 should be an optional value
onSuccess: setArticleDetails,
onFailure: () => console.log("Error occured loading articles"),
label: FETCH_ARTICLE_DETAILS
});
}
function setArticleDetails(data) {
console.log(data);
return dispatch({
type: SET_ARTICLE_DETAILS,
payload: data
});
}
i tried to set the param directly
export function fetchArticleDetails(id)
...
url: `http://myurl/appApi/2.0.0/getData/${id}`,
or some variations to put the params in the payload directly
function setArticleDetails(data) {
console.log(data);
return dispatch({
type: SET_ARTICLE_DETAILS,
payload: data,
userid: id
});
}
All this results in the same error. Anyone have an idea where to place the dynamic data to solve it?
Another idea could be to set the params in my reducer maybe?
Update store/index.js
import { createStore, applyMiddleware } from "redux";
import rootReducer from "../reducers";
import apiMiddleware from "../middleware/api";
const store = createStore(rootReducer, applyMiddleware(apiMiddleware));
window.store = store;
export default store;
update: middleware/api.js
import axios from "axios";
import { API } from "../actions/types";
import { accessDenied, apiError, apiStart, apiEnd } from "../actions/api";
const apiMiddleware = ({ dispatch }) => next => action => {
next(action);
if (action.type !== API) return;
const {
url,
method,
data,
accessToken,
onSuccess,
onFailure,
label,
headers
} = action.payload;
const dataOrParams = ["GET", "DELETE"].includes(method) ? "params" : "data";
// axios default configs
axios.defaults.baseURL = process.env.REACT_APP_BASE_URL || "";
axios.defaults.headers.common["Content-Type"] = "application/json";
axios.defaults.headers.common["Authorization"] = `Bearer ${accessToken}`;
if (label) {
dispatch(apiStart(label));
}
axios
.request({
url,
method,
headers,
[dataOrParams]: data
})
.then(({ data }) => {
dispatch(onSuccess(data));
})
.catch(error => {
dispatch(apiError(error));
dispatch(onFailure(error));
if (error.response && error.response.status === 403) {
dispatch(accessDenied(window.location.pathname));
}
})
.finally(() => {
if (label) {
dispatch(apiEnd(label));
}
});
};
export default apiMiddleware;
function apiAction()
function apiAction({
url = "",
method = "GET",
data = null,
accessToken = null,
onSuccess = () => {},
onFailure = () => {},
label = "",
headersOverride = null
}) {
return {
type: API,
payload: {
url,
method,
data,
accessToken,
onSuccess,
onFailure,
label,
headersOverride
}
};
}
There are a couple of issues with the code. apiMiddleware should only pass the action to the next middleware in the chain if it's not of type API.
const apiMiddleware = ({ dispatch }) => (next) => (action) => {
if (action.type !== API) {
return next(action)
}
// do stuff
}
Since the apiMiddleware dispatches what onFailure returns, the function has to return an object. In fetchArticleDetails, you're passing () => console.log("Error occured loading articles") causing apiMiddleware to dispatch undefined.
export function fetchArticleDetails(id) {
return apiAction({
url: `https://jsonplaceholder.typicode.com/todos/${id}`,
onSuccess: setArticleDetails,
onFailure: (error) => ({
type: FETCH_ARTICLE_ERROR,
payload: error
}),
label: FETCH_ARTICLE_DETAILS
})
}
CodeSandbox
I would strongly recommend using React Query to simplify data fetching, managing, and syncing server state.

async/await actions in Vuex

I am wondering how to use async/await actions in Vuex. The docs provide this syntax as an example:
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // wait for `actionA` to finish
commit('gotOtherData', await getOtherData())
}
}
Following this example, I have:
import Vue from 'vue';
import Vuex from 'vuex';
import * as firebase from 'firebase';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
// other state vars here
resource: null
},
mutations: {
// saveValues
setResource(state, payload) {
state.resource = payload;
}
},
actions: {
async getResource({ commit, dispatch }) {
var resource
console.log('resource1: ' + resource)
Vue.http.get('https://mysite/api/getResource')
.then((response) => {
console.log('get resource')
var data = response.body;
resource = data.access_resource;
console.log('resource2: '+ resource)
commit('setResource', resource);
var foo = store.getters.resource;
console.log('resource3: ' + foo);
}, (error) => {
console.log(error);
});
},
async getSomeApi({ commit, dispatch }) {
console.log('getting api');
await dispatch('getResource');
var resource = store.getters.resource;
console.log('resource4: ' + resource);
Vue.http.get('https://somesite/api/someapi?resource=' + resource)
.then((response) => {
console.log("got something from somesite")
var data = response.body;
// do something with data -> payload
dispatch('saveValues', payload);
}, (error) => {
console.log(error);
});
}
},
getters: {
resource(state) {
return state.resource;
}
}
});
However, even following the syntax example found in the docs, when I run this code, the async/await seem to be completely ignored. When I look at the logs, I see, in the following order:
getting api
resource1: undefined
resource4: null
get resource
resource2: <expected-value>
resource3: <expected-value>
I expect the console.log statements to print out in numerical order. I would appreciate if someone could clarify what I am doing wrong.
You're not awaiting the Vue.http.get() promise in the getResource() method, so await dispatch('getResource') will resolve before the HTTP request has resolved.
Trimmed down:
async getResource() {
let response
try {
response = await Vue.http.get('https://mysite/api/getResource')
} catch (ex) {
// Handle error
return
}
// Handle success
const data = response.body
}