I hope you can help me.
I'm trying to get a response from an API and use that information in another file.
I have 3 files:
api.jsx
import axios from 'axios';
export const api = (url, data) => {
const { path, method } = url;
let result ={};
axios({
method: method,
url: path,
data: data
})
.then(res => {
result = res.data;
console.log(res.data);
})
.catch(err => console.error(err));
return result;
};
url.jsx
export const URL = {
users:
{
getAllUsers: { path:'/users', method: 'post'},
login: { path:'/login', method: 'post'},
register: { path:'/register', method: 'post'},
version: { path:'/', method: 'get'},
}
}
app.js (within the render)
const data = {
email: 'hello#world.com',
password: '12345',
};
let result = api(URL.users.login, data);
console.log(result);
In the api file i get the proper response but in the react component no. I am aware that It's a problem of sync as i get first the console of app.jsx and later on the console of the api.jsx but i would like to respect the current structure or make something similar.
Any idea how to fix this without many changes?
PS. sorry about the mess. I tried to highlight all the code but for some reason it is not working fine.
You want to return a Promise in api.jsx
api.jsx
export const api = (url, data) => {
const { path, method } = url
return axios({ // the axios call returns a promise because of its .then; you can just return it
method: method,
url: path,
data: data
})
.then(res => {
return res.data;
})
.catch(err => {
console.error(err)
})
}
Related
I built a React web app that allows users the calculate their meal's nutrition.
Users enter an ingredient in the IngredientForm component, triggering the fetchFirebaseNutrition function.
const fetchFirebaseNutrition = (ingredientName) => {
const axios = require('axios');
const options = {
method: 'GET',
url: 'http://localhost:5001/nutrition-calculator-6db9d/us-central1/fetchAPINutrition',
params: { ingredient: ingredientName },
};
return axios
.request(options)
.then((response) => {
console.log(response);
})
.catch((error) => {
console.error(error);
return setIsAPIConnectionDown(true);
});
};
fetchFirebaseNutrition makes a get request to to my Firebase function, fetchAPINutrition, with the relevant ingredient. Then, fetchAPINutrition makes a get request to an API for the nutrition data. I'm using the Firebase function to conceal the API key for when I deploy my web app.
exports.fetchAPINutrition = functions.https.onRequest((req, res) => {
const axios = require('axios');
const ingredient = req.query.ingredient;
const options = {
method: 'GET',
url: 'https://calorieninjas.p.rapidapi.com/v1/nutrition',
params: { query: ingredient },
headers: {
'X-RapidAPI-Host': 'calorieninjas.p.rapidapi.com',
'X-RapidAPI-Key': process.env.REACT_APP_RAPID_API_KEY,
},
};
return axios
.request(options)
.then((response) => {
console.log(response.data);
return response.data;
})
.catch((error) => {
console.error(error);
});
});
fetchAPINutrition works... kind of. When I make a request to the function from the front-end, I can see on the Firebase Emulator that fetchAPINutrition is successfully getting the data from the API. See the
Firebase Emulator Logs.
However, the function just times out afterwards. So, fetchFirebaseNutrition never receives the API data and just returns an Axios Network Error message.
How can I stop my Firebase function from timing out and return the API data to the front-end?
Any help would be immensely appreciated!
HTTPS functions expect a return, which you don't. Instead, you are returning a promise without waiting for it to complete.
You need to return a result.
res.status(200).send(response.data);
In this case;
axios
.request(options)
.then((response) => {
res.status(200).send(response.data);
})
.catch((error) => {
console.error(error);
res.status(500).send("Error");
});
I'm trying to implement the Stripe payment API in my website. However, I'm not very familiar with JavaScript, which is leading to some issues.
Most of my code is taken from Stripe's website, with some modifications made to better suit my own site.
I have a function CreateCustomer, which is intended to create a customer based on input from a form:
async function createCustomer () {
var emailJSON = {
"email": `${document.querySelector('#email-field').value}`
};
const output = await fetch('/create-customer', {
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(emailJSON)
})
.then((response) => {
return response.json();
})
.then((result) => {
return result;
});
return output;
}
This function is called by another function, and the output is stored because I have another function which requires the customer's Id.
var handleForm = function () {
// other code
const customerPromise = createCustomer();
var customer = customerPromise.data;
// more code
}
The fetch call in CreateCustomer calls the following C# code:
[Route ("create-customer")]
[ApiController]
public class CustomerCreationController : Controller
{
[HttpPost]
public ActionResult Create(CustomerCreateRequest request)
{
var customers = new CustomerService();
var customer = customers.Create(new CustomerCreateOptions
{
Email = request.Email,
});
return Json(new { _customer = customer.Id });
}
public class CustomerCreateRequest
{
[JsonProperty("email")]
public string? Email { get; set; }
}
}
From what I understand about Promises, if I call on the promise before it's resolved, I will get "undefined". But I also think I understand that if I await the promise (as I have done with the fetch in CreateCustomer), this shouldn't be an issue. However, whenever I run this code, I end up saving undefined in my customer variable in handleForm.
Like I said, I'm pretty unfamiliar with JavaScript, so the problem is probably some quirk of the language and/or Promises that I've overlooked. Thanks in advance for your help.
If your createCustomer is async, you need to await it, when calling it:
var handleForm = async function () {
// other code
const customerPromise = await createCustomer();
var customer = customerPromise.data;
// more code
}
#Marco is correct.
You also doing await and including .then witch is redundant.
Here are your options for promises:
// Using async/await
async function createCustomer () {
try {
const requestBody = {
email: `${document.querySelector('#email-field').value}`
};
const response = await fetch('/create-customer', {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestBody)
})
const data = await response.json()
return data;
} catch (error) {
console.error(error);
}
}
/*
If you are calling createCustomer somewhere it has to
either be a async function to allow await, or
use .then((data) => console.log(data))
*/
const data = await createCustomer();
// Promise chains
async function createCustomer (callback) {
const requestBody = {
email: `${document.querySelector('#email-field').value}`
};
fetch('/create-customer', {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestBody)
})
.then(response => response.json())
.then(data => callback(data))
.catch(e => console.error(e))
}
createCustomer((data) => {
// do something with data
})
One little thing with fetch is that you also have to await the .json() call. Since that is also a promise.
If you are doing a lot of network requests maybe take a look at axios.
So I am testing this api request inside my react-redux application:
import $ from 'jquery';
window.$ = $;
const API_KEY = '<api-key>';
const ROOT_URL = `https://api.behance.net/v2/users?client_id=${API_KEY}`;
export const FETCH_USER = 'FETCH_USER';
export function fetchUser(users) {
const request = $.ajax({
url: `${ROOT_URL}&q=${users}`,
type: 'get',
data: { users: {} },
dataType: 'jsonp'
})
.done(response => {
console.log('Request:', request);
})
.fail(error => {
console.log('Ajax request fails');
console.log(error);
});
return {
type: FETCH_USER,
payload: request
};
}
However, in the Chrome console for Request: I am getting an object with readyState instead of a Promise, do I even need to have the package redux-promise at this point?
I see what you are trying to do, but I don't think is a good idea to send a promise to a reducer instead I will recommend you to use the middleware redux-thunk.
I will rewrite your action this way
export const fetchUser= ()=>(dispatch,getState)=>{
let params={
method:'get',
body:{ users: {} },
}
fetch( `${ROOT_URL}&q=${users}`, params).then
.then(response => response.json())
.then((response) =>{
dispatch({
type:Const.ON_RESPONSE_OK,
payload:response
})
})
.catch(error => {
dispatch({
type:Const.ON_RESPONSE_ERROR,
payload:Error
})
});
}
and rewrite the reducer to handle the payloads
I am developing an application where there are lots of async actions. I wanted to go with redux-saga but most have insisted to continue with redux-thunk. In redux-thunk, inside each action we have to work with async operation using then, dispatch, catch, etc. This makes looks actions so messy and lots of code will be repeated. I wanted to create a generic dataLoader for the use of redux-thunk and axios but could not consider for both post(might be token or not) and get option.
Here is my attempt:
export class Company {
/**
* Generic api data loader
*/
static dataLoader(apiUri, onSuccess, onError, data, ...actionArguments) {
const requestURL = `${API_BASE}${apiuri}`;
try {
let options;
if (data !== undefined) {
// if we have data to post
options = {
method: 'POST',
url: requestURL,
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
},
};
}
}
return function(dispatch) {
axios(options)
.then(response => {
dispatch({
type: onSucess,
payload: response.data
});
})
.catch(error => {
dispatch({ type: onError, payload: err});
});
}
}
static get(apiUri, onSuccess, onError, ...actionArguments) {
return this.dataLoader(apiUri, onSuccess, onError, undefined, ...actionArguments);
}
/*
* Shorthand POST function
*/
static post(apiUri, onSuccess, onError, data, ...actionArguments) {
return this.dataLoader(apiUri, onSuccess, onError, data, ...actionArguments);
}
}
I want to convert the following code to further this one:
export function showResultofApartment() {
return (dispatch) => {
dispatch({ type: 'APARTMENT_FETCH_START' });
const token = localStorage.getItem('token');
return axios.get(`${API_URL}/newoffers/apartment/`)
.then((response) => {
console.log('response apart', response.data);
dispatch({ type: 'APARTMENT_FETCH_SUCCESS', payload: response.data });
})
.catch((err) => {
dispatch({ type: 'APARTMENT_FETCH_FAILURE', payload: err });
});
};
}
to such or more efficient than this:
export function showResultofApartment() {
return(dispatch) => {
dispatch({ type: APARTMENT_FETCH_START });
const token = localStorage.getItem('token');
return Company.get('/apartments', APARTMENT_FETCH_SUCCESS, APARTMENT_FETCH_ERROR);
// if post then Company.post('/apartment', APARTMENT_POST_SUCCESS, APARTMENT_POST_ERROR, data)
}
}
This way it is considering only post request(if data !== undefined). How should i handle for both get and post efficiently?
Okay, why don't you handle it like this:
Company.js
import { merge } from 'lodash';
import axios from 'axios';
function getHeaders() {
return {
'Content-Type': 'application/json'
};
}
export class Company {
static callAPI(endpoint, extendedOptions, onSuccess, onError) {
const initalHttpData = {
method: 'GET', // By default it's GET in case you didnt specify anything
headers: getHeaders(),
url: `${API_BASE}${endpoint}`
};
// merge takes care of replacing or adding the specific key's provided via the extendedOptions
const options = merge(initalHttpData, extendedOptions);
// Fire the request for the prepared options.
let request = axios(options);
// The request once fired, needs it's success handler and error handler.
return function(dispatch) {
request
.then(response => {
dispatch({
type: onSucess,
payload: response.data
});
})
.catch(error => {
dispatch({ type: onError, payload: err});
});
}
};
}
Then we can use actions to specifically pass things to this api util:
GET API call:
// GET Action
export function showResultofApartment() {
return (dispatch) => {
dispatch({ type: APARTMENT_FETCH_START });
const token = localStorage.getItem('token');
// FOR GET API
return Company.callApi('/apartments', {}, APARTMENT_FETCH_SUCCESS, APARTMENT_FETCH_ERROR);
}
}
POST API call:
// POST Action
export function showResultOfAppartmentPost() {
return (dispatch) => {
dispatch({ type: APARTMENT_FETCH_START });
const token = localStorage.getItem('token');
// This will merge, essentially replace the method=GET once it gets called.
const extendedOptions = {
method: 'POST',
body: JSON.stringify(data),
headers: {
'X-Requested-With': 'XMLHttpRequest',
}
}
// FOR GET API
return Company.callApi('/apartments', extendedOptions, APARTMENT_FETCH_SUCCESS, APARTMENT_FETCH_ERROR);
}
Thus, giving the action, to define it's own set of API body or requests.
I have banged around in this for a while now. Looked at this question but its not quite what I want.
In a nutshell... I have const expression = to a function that is chained to another function that makes an API call in a separate file to LoginContainer but in the same folder -(its called reducer.js but has the actions as well at this stage). If successful it receives a token which it saves in local storage. this works fine.
Here it is.
import { fetch, addTask } from 'domain-task'
import { saveJwt, clearJwt } from '../auth/jwt'
import { handleErrors } from '../utils/http'
const REQUEST_LOGIN_TOKEN = 'REQUEST_LOGIN_TOKEN'
const RECEIVE_LOGIN_TOKEN = 'RECEIVE_LOGIN_TOKEN'
const ERROR_LOGIN_TOKEN = 'ERROR_LOGIN_TOKEN'
const REQUEST_USER = 'REQUEST_USER'
const RECEIVE_USER = 'RECEIVE_USER'
const ERROR_USER = 'ERROR_USER'
// ******************* action
export const requestLoginToken = (username, password) =>
(dispatch, getState) => {
dispatch({ type: REQUEST_LOGIN_TOKEN, payload: username })
const payload = {
userName: username,
password: password,
}
const task = fetch('/api/jwt', {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json;charset=UTF-8'
},
})
.then(handleErrors)
.then(response => response.json())
.then(data => {
dispatch({ type: RECEIVE_LOGIN_TOKEN, payload: data })
saveJwt(data)
})
.catch(error => {
clearJwt()
dispatch({ type: ERROR_LOGIN_TOKEN, payload: error.message })
})
addTask(task)
return task
}
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import Login from './Login'
import { requestLoginToken } from './reducer'
class LoginContainer extends Component {
static contextTypes = {
router: PropTypes.object.isRequired
}
componentWillReceiveProps(nextProps) {
if (nextProps.isAuthorised) {
this.context.router.push('/')
}
}
submit = (values) => {
console.log('got values!', values)
this.props.requestLoginToken(values.username, values.password)
}
render() {
return (
<Login onSubmit={this.submit} />
)
}
}
const mapStateToProps = (state) => ({
isAuthorised: state.login.isAuthorised,
})
const mapDispatchToProps = (dispatch) => ({
requestLoginToken: (username, password) => dispatch(requestLoginToken(username, password)),
//requestSelectData: (values = {}) => dispatch(requestSelectData(values = {})),
})
export default connect(mapStateToProps, mapDispatchToProps)(LoginContainer)
In the loginContainer (above), Once the "userName" and "password" have been entered and the submit button clicked, the expression "requestLoginToken" is called.
My Problem
I want to fetch a significant amount of data based on the above expression "requestLoginToken" successfully saving a JWT token into local storage. It does this successfully now with the right username and password.
I know I can't make another call from within the expression "requestLoginToken" using a ".then" as it specifically needs to retrieve and then save a token first - I have to wait till it finishes to know if I have a token. I need to run a second expression that only gets run if this promise is successful ie via a conditional statement. "If (JWT) etc"
1) Could someone tell me where and how I add this conditional statement. Im thinking its in the Logincontainer in the submit? ..how do would I structure the condition?
2) Where and how do I add the const = function for the retrieval of the data eg if I place it in another separate file do I still or even need to register it in mapDispatchToProps in the loginContainer etc
EDIT
Taking Nate Kimball's answer and running with it. Decided to split it out into its own "const" called "selectData" which I plan to call right underneath the line "saveJwt(data)".
However I find I am now getting an error:
Unexpected Token , expected
Its on the very last line of the following code block below.. (right curly bracket has red under it) checked it for sytax but cant workout why.
I think the approach is correct though.
const selectData = () => {
dispatch({ type: REQUEST_SELECT_DATA })
const token = jwt.access_token
const headers = new Headers({
'Authorization': `Bearer ${token}`
})
const selectData = fetch('/api/SelectData/SelectData', {
method: 'GET',
headers,
})
.then(handleErrors)
.then(response => response.json())
.then(data => {
dispatch({ type: RECEIVE_SELECT_DATA, payload: data })
.catch(error => {
clearJwt()
dispatch({ type: ERROR_SELECT_DATA, payload: error.message })
})
}
}
I don't see any reason why you couldn't nest a second fetch from within your action after a successful call:
export const requestLoginToken = (username, password) =>
(dispatch, getState) => {
dispatch({ type: REQUEST_LOGIN_TOKEN, payload: username })
const payload = {
userName: username,
password: password,
}
const task = fetch('/api/jwt', {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json;charset=UTF-8'
},
})
.then(handleErrors)
.then(response => response.json())
.then(data => {
dispatch({ type: RECEIVE_LOGIN_TOKEN, payload: data })
saveJwt(data)
// Since this is where you receive your login token,
// You can dispatch an action to acknowledge you're fetching:
dispatch({ type: SECOND_DATA_FETCHING })
// This is also where you would make your next call:
fetch('/api/etc', { ...config })
.then(response => {
// You can use your reducer to both inform a successful call &
// store the received data
dispatch({ type: SECOND_DATA_SUCCESS, payload: response.data })
})
.catch(error => {
// Let your app know the call was unsuccessful:
dispatch({ type: SECOND_DATA_FAILED, payload: error.message })
})
// Note: if you don't like the nested ugliness, you could optionally
// put this entire nested fetch chain into a separate action and just
// dispatch that when you get your token.
})
.catch(error => {
clearJwt()
dispatch({ type: ERROR_LOGIN_TOKEN, payload: error.message })
})
addTask(task)
return task
}
At that point, all you need to do is update your mapStateToProps function in your component to receive the data and/or the status of that second layer of fetched data:
// Make sure you have a default status for that second data
// just in case your token call fails.
const mapStateToProps = (state) => ({
isAuthorised: state.login.isAuthorised,
secondData: state.login.secondData,
secondDataStatus: state.login.secondDataStatus
})
You can use requestLoginToken in another action creator:
function loginAndFetch() {
return function(dispatch, getState) {
dispatch(requestLoginToken()).then(token => {
return fetch(...) // use token here
})
}
}
As an alternative, you could save the token you got to the store, then have another component listen to changes to the token and dispatch another action when the token changes.
class Container extends Component {
componentDidUpdate(prevProps, prevState) {
if (this.props.token != prevProps.token) {
dispatch(fetchSignificantAmountOfData())
}
}
}
Container is connected and maps the stored token into props.token
You can write a custom middleware to solve this problem like this:
https://github.com/erikras/react-redux-universal-hot-example/blob/master/src/redux/middleware/clientMiddleware.js
And then, you can use the action like this:
export function myAction() {
return {
types: [LOAD, SUCESS, FAIL],
promise: (client) => client.get('/some_api')
};
}
The middleware will dispatch the LOAD reducer first, then if promise is resolve, it call SUCESS; Otherwise, FAIL is called.