I want to make 2 request in a row, kinda in a waterfall fashion. I want to first request a specific pokemon, and then based on the returned obj's payload's type, I want to request more information. I thought it would be best to separate this out to several action creators but feel weird that fetchPokemon ends with another fetch. Is this best practice?
export const fetchPokemon = function (pokemonName) {
return function (dispatch) {
dispatch(requestPokemon(pokemonName))
const requestURL = `http://pokeapi.co/api/v2/pokemon/${pokemonName}/`
return $.ajax({
url: requestURL,
}).done(function (data) {
dispatch(receivePokemon(data))
fetchPokeTypeInfo(data.types[0].type.url)
})
}
}
...
export const fetchPokemonTypeInfo = function (url) {
return function (dispatch) {
dispatch(requestPokemonTypeInfo(url))
return $.ajax({
url: url,
}).done(function (data) {
dispatch(receivePokemonTypeInfo(data))
})
}
}
I don't think there's anything particularly wrong with breaking these two up. One question I'd ask is: "Would I ever call fetchPokemonTypeInfo() directly, not from fetchPokemon()?". If not, then I'd just return the second .ajax call from the .done() function in the first. If the first call is always a dep, it seems easier to reason about what is happening if they just nest. Also, if you do want to keep them separate you'll need to pass the dispatch function as well as the url to the second function, otherwise dispatch is undefined in fetchPokemonTypeInfo().
Update:
You could nest the second call in the first like this:
export const fetchPokemon = function (pokemonName) {
return function (dispatch) {
dispatch(requestPokemon(pokemonName));
const requestURL = `http://pokeapi.co/api/v2/pokemon/${pokemonName}/`;
return $.ajax({
url: requestURL,
}).done(function (data) {
dispatch(receivePokemon(data));
dispatch(requestPokemonTypeInfo(data.types[0].type.url));
return $.ajax({
url: data.types[0].type.url,
}).done(function (data) {
dispatch(receivePokemonTypeInfo(data));
});
});
}
}
There is a way, which provides clean and predictable solution.
If you are using redux, you can make use of middleware to make your API calls. In addition, in your middleware you can extend its functionality by allowing taking multiple requests (perhaps in array), and resolving them altogether before returning success Promise.
Check this link for reference:
https://github.com/reactjs/redux/blob/master/examples/real-world/middleware/api.js
This is an functional middleware, but you have to extend it to support multiple requests :) Good luck!
Use redux-saga https://github.com/yelouafi/redux-saga
Note: the following code is just a concept, you would need to adjust it per your needs.
function* fetchPokemon(action) {
try {
//fetch1
const pokemon = yield call(Api.fetchPokemon, action.payload.pokemonId);
//this action will execute only after fetch1 is successful
yield put({type: "FETCH_POKEMON_SUCCEEDED", payload: pokemon});
//fetch2
const pokemonInfo = yield call(Api.fetchPokemonInfo, types[0].type.url)
// execute after fetch2 is successful
yield put({type: "FETCH_POKEMON_INFO_SUCCEEDED", payload: pokemonInfo})
} catch (e) {
yield put({type: "FETCH_FAILED", message: e.message});
}
}
// wait for an action and fire a saga
function* watchFetchPokemonRequest() {
yield* take("FETCH_POKEMON_REQUESTED", fetchPokemon);
}
Sagas use Generators, which "make" your async code synchronous. That way you don't need to deal with callback in promises etc. It's a nice and clean way of describing side-effect of your application.
Related
my code outputs everytime different numbers. Is this a proper way I am using it?
Here is the code:
export class GetPlanetsService {
url='https://swapi.co/api/planets/?page=';
planets:Planet[]=[];
headers: HttpHeaders = new HttpHeaders()
.set('Accept', 'application/json');
constructor(private http:HttpClient) { }
getPlanet(pageIndex){
return this.http.get<Planets>(`${this.url}${pageIndex}`,{headers:this.headers});
}
getAllPlanets(){
let numberOfPages=7; // Tried to do it dynamically but got infinite loop
for(let j=1;j<=numberOfPages;j++){
this.getPlanet(j).subscribe(value=>{
for(let i=0;i<value.results.length;i++){
this.planets.push(value.results[i]);
if(j==numberOfPages && i==(value.results.length-1)){
console.log(this.planets); //There is outputted everytime different number
}
}
});
}
}
Have you got any tips and could you explain it in simple words?
Regards
You can use forkJoin for this, Dont forget to include
import { forkJoin } from 'rxjs';
forkJoin waits for each HTTP request to complete and group’s all the
observables returned by each HTTP call into a single observable array
and finally return that observable array.
getPlanet(pageIndex) {
return this.http.get < Planets > (`${this.url}${pageIndex}`, {
headers: this.headers
});
}
getAllPlanets() {
const response = [...Array(7).keys()].map(i => this.getPlanet(i));
return forkJoin(response);
}
in your component you can call getAllPlanets
this.getPlanetsService.getAllPlanets()
.subscribe(res => {
console.log(res);
}, err => {
console.log(err);
});
There are few ways you can control your async behavior.
Promises.all
Async Await
Async library
Ok there is deeper problemes here.
First, why are you trying to call the server 7 times in a row? What if I want page 200? You will make 200 Http requests? The server should return the entire list. It will increase performance and reduce complexity on client side.
Also, why getAllPlanets() return void? It's not intuitive. Instead, getAllPlanets() should return Observable<Planet[]>. All functions should either return of modify (it's part of the CQS principle) here the purpose it to return data so you can't notify your object state e.g. this.planets.push(value.results[i]). What if a invoke the function twice? Then, this.planets will contain the result of both requests.
I want to post data to a server.. my action is like this:
export function addNewTodo(text) {
return {
type: 'ADD_NEW_TODO',
payload: addNewTodoApi(text)
};
}
let addNewTodoApi = function(text) {
return new Promise(function(resolve, reject) {
//implement fake method for get data from server
setTimeout(function () {
resolve({
text: text,
done: ??,
assignTo: ??,
Project: ??,
Category: ??,
id: ??
});
}, 10);
});
};
I have three way. The first way is import store and and call getState method in my action and the second way is dispatch action in reducer and the last way is pass all data in my action as argument. Which one is correct? I read this question and I'm worried about antipatterns.
You should consider using import { connect } from 'react-redux' and using containers.
You access your store on function mapStateToProps, you send all your data for your action from the container.
The signature for it is
const mapStateToProps = (state, ownProps) => {}
There you get the state which a part could be passed to your action.
The action perform post to your api using ajax with data you passed from state.
In your action your could consider using isomorphic-fetch.
Quick example:
import 'isomorphic-fetch'
const postData = ()=> ({
type: types.POST_YOURACTION,
payload: new Promise((resolve, reject) => {
fetch(api.postData(), {method: 'POST', }).then(response => {
resolve(response.json())
})
})
})
In case if you need to access the store directly you could use .getState(), accessing state directly could be considered an anti-pattern but it really depends on your architecture design.
I would suggest to look look at mapStateToProps.
Example of how to access your store.
import store from 'app/store'
store.getState().yourReducer.data
I always use the third way, pass data to the action. The data can be the data in store.getState() or from params or whatever you need to get data from server.
So, i have a small API interaction code that looks like this:
function load_posts() {
return $http
.get('/posts')
.then(on_success);
function on_success(response) {
return response.data;
}
}
function get_posts() {
if (blog.posts) {
return $q.when(blog.posts);
}
return load_posts().then(function (posts) {
blog.posts = posts;
return blog.posts;
});
}
I do this to avoid hitting the API for the same results all the time. I have several separate directives and components that might need to call this API endpoint, but they don't need a fresh result everytime. But this results in an ugly race condition: if two or more components call the get_posts method before the load_posts response arrives, then they all issue API requests. There are no side-effects, because this is just a cache attempt, but it defeats the whole purpose.
Any ideas on how to proceed with this one?
The $http service can cache requests. See here or the docs for a deeper explanation of how the caching works.
The default $http cache can be particularly useful when our data
doesn’t change very often. We can set it like so:
$http({
method: 'GET',
url: '/api/users.json',
cache: true
});
// Or, using the .get helper
$http.get('/api/users.json', {
cache: true
});
Now, every request that is made through $http to the URL
/api/users.json will be stored in the default $http cache. The key for
this request in the $http cache is the full-path URL.
This isn't really a race-condition problem, it's just a matter of memoizing the function. You can use something like memoize() from Underscore.js or just implement it yourself:
var load_posts = () => {
const p = $http
.get('/posts')
.then(response => response.data);
load_posts = () => p;
return p;
};
1) Extract data retrieval into separate "blogService" service;
2) Cache promise in your service that does the request;
3) Return the same promise for all clients, you can manipulate results if you dont want to expose whole response object;
var promise = null;
function loadBlogs() {
promise = promise || $http.get("/posts").then(function(response){return reponse.data;});
return promise;
}
4) Then just call service method and wait for promise to resolve wherever you need (controller, directive, etc):
function getPosts() {
blogService.loadBlogs().then(function (posts) {
vm.posts = posts;
});
I'm learning to build a pokedex app that gives you info about a pokemon after you type in a name and hit submit. It has to go through 2 calls to get the right info, with the 1st getting its height and weight, and then 2nd getting its description (ex. "Charmander can usually be found in hot areas..."). Below is my action breakdown.
export const fetchPokemon = function (pokemonName) {
return function (dispatch) {
dispatch(requestPokemon(pokemonName))
const requestURL = `http://pokeapi.co/api/v2/pokemon/${pokemonName}/`
return $.ajax({
url: requestURL,
}).done(function (data) {
dispatch(receivePokemon(data))
return fetchPokemonDescription(pokemonName)
}).done(function (res) {
dispatch(receivePokemonDescription(res))
})
}
}
...
export const fetchPokemonDescription = function (pokemonName) {
return function (dispatch) {
dispatch(requestPokemonDescription(pokemonName))
const requestURL = `http://pokeapi.co/api/v2/pokemon-species/${pokemonName}/`
return $.ajax({
url: requestURL,
})
}
}
Should I have a separate reducer for each call? Looking at the docs about reducer composition I'm not sure if it would make it cleaner to have 1 reducer vs 2 reducers. The 2 calls are not dependent on each other per say, but each different pokemon input will need to make both calls and the data coming back from both belongs to one pokemon so I was thinking it should be in one reducer handling one part of the state.
I would use 1 reducer in your case.
It would be useful to see your state structure, but I think that you have something like:
currentPokemon: {
name: ...
height: ...
weight: ...
description: ...
}
If this is the case, I would use 1 reducer, because you manage just 1 branch of the state (currentPokemon).
The reducer will switch the action: in one case it will update height and weight, in the other the description:
export default function currentPokemonReducer (state = initialState, action) {
switch(action.type) {
case DATA_RECEIVED:
return {
...state,
height: action.payload.height,
weight: action.payload.weight
}
case DESCRIPTION_RECEIVED:
return {
...state,
description: action.payload
}
...
default:
return state;
}
}
The AJAX call you are making should be paired with an action type. You should then return the the type and your payload, which I'm assuming based on your code, you intend to be to request responses.
//Now sure where these functional are define or what they do, but I'll assume they are selectors of some sort?
export const fetchPokemon = function (pokemonName) {
return function (dispatch) {
dispatch(requestPokemon(pokemonName))
const requestURL = `http://pokeapi.co/api/v2/pokemon/${pokemonName}/`
return $.ajax({
url: requestURL,
}).done(function (data) {
dispatch(receivePokemon(data))
return fetchPokemonDescription(pokemonName)
}).done(function (res) {
dispatch(receivePokemonDescription(res))
})
}
}
you're action might look something like this:
const data = fetchPokemon(Charmander);
//this can be defined somewhere else in your app, should not be in your actions file.
export const pokemonAction(data) {
return {
type: "GET_POKEMON_NAME",
payload: data.name
}
}
//repeat for any other call
Best practice is to have your types defined separately so that they can be pulled into you actions files and reducer files. A lot of how you build your actions and reducers will depend on your response objects.
export const getPokemonReducer(state= {}, action) {
if(action.type === "GET_POKEMON_NAME") {
return {
...state,
[pokemonName]: action.payload
}
if(action.type === "GET_POKEMON_DESC") {
return {
...state,
[pokemonDesc]: action.payload
}
}
Reducers can be used in many different ways depending on how you want to shape your state. You should think about how you would like to use this information through out your application. The above example would give a single property, lets call it "Pokemon", and that properties value is an object that the above two properties.
For example if you want to pull in just the name and don't want to pull it in as this.props.pokemon.pokemonName then maybe you would consider having a separate reducer. I would also consider looking into abstractions such as axios and redux-promise which make asynchronous calls much easier to manage in redux.
I'm pretty inexperienced with Q, promises, angular, and typescript - so it seemed the obviously intelligent choice was to try using them all together!
Basically I have some methods setup to return Q.IPromise<T> so that I can chain a then(f) statement onto them. Both of them work fine;
public Clear = (): Q.IPromise<IArticle> => {
var deferred = this.$q.defer();
this.$http({
url: String.format('{0}{1}', this.$settings.uri(), '/api/cancel/article'),
responseType: 'json',
method: 'GET',
withCredentials: true
}).then((response: IArticleRequest) => {
deferred.resolve(response.data.article);
}, (response) => {
deferred.reject(null);
});
return deferred.promise;
}
public Fetch = (id: string):Q.IPromise<IArticleRequestData> => {
var deferred = this.$q.defer();
this.$http({
url: String.format('{0}{1}{2}', this.$settings.uri(), '/api/articles/fetch/', id),
responseType: 'json',
method: 'GET',
withCredentials: true
}).then((response: IArticleRequest) => {
deferred.resolve(response.data);
}, (response) => {
deferred.reject(null);
});
return deferred.promise;
}
So I can use them like this;
$articles.Fetch(1).then((r: IArticleRequestData) => {
$articles.Clear().then((n:IArticle) => {
console.log('completed!');
})
});
That works fine - and as I expect it to.
But now, I need another step - a last call sort of. Another then(f) function that goes on the outermost function but doesn't occur until the innermost one completes.
So in short, I need this;
$articles.Fetch(1).then((r: IArticleRequestData) => {
$articles.Clear().then((n:IArticle) => {
// inner function completed
})
}).then((l:any)=> {
// occurs after everything else is done and inner
// functions are finished
});
and I'm having a very difficult time figuring out how to make this happen. Is this even possible with the way promises work?
extra notes
adding another function to the inner section is allowable but it feels like the wrong approach. this is my go to "fix" if I absolutely cannot figure out the way I'm asking about, or if the way I'm trying to do it is just wrong.
I want the extra then on the outside because the inner methods are actually responsible for resolving certain things, and as such they may split off into other tangents or deferred methods as well.
Just return the promise from the inner function like this:
$articles.Fetch(1).then((r: IArticleRequestData) => {
return $articles.Clear()
}).then((l:any)=> {
// occurs after everything else is done and inner
// functions are finished
});