on my React Redux app, I am receiving the raw data from the API on my action creator, which I then manipulate further before dispatching the action with the refined data to Reducer. However, when running, the code seems to never make it down to point at dispatches the action to reducer, since I try to log texts and it never comes up on the console. What am I missing? I appreciate any help. Relevant action creators code below...
export const getTrainerAvailability = (trainerId) => {
console.log('getting here', trainerId)
return async (dispatch) => {
dispatch(getTrainerAvailabilityRequest());
const token = await AsyncStorage.getItem('token');
fetch(url + 'gettraineravailability/' + trainerId, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `${token}`
}
})
.then(res => res.json())
.then(res => {
console.log('RES', res) // <-- logs as expected
if (res.error) {
console.log('error')
throw(res.error);
}
dispatch(getTrainerAvailabilitySuccess(res));
})
.catch(error => {
dispatch(getTrainerAvailabilityError(error));
})
}
};
export const getTrainerAvailabilitySuccess = (data) => {
console.log('RAW DATA: ', data[0]) // <-- this is logged as expected
const rawAvailability = data[0]
let mondayAvailability = [];
let tuesdayAvailability = [];
let wednesdayAvailability = [];
let thursdayAvailability = [];
let fridayAvailability = [];
let saturdayAvailability = [];
let sundayAvailability = [];
for (let item of rawAvailability) {
if (item.week_day === 'Monday') {
mondayAvailability.push(item);
}
else if (item.week_day === 'Tuesday') {
tuesdayAvailability.push(item);
}
else if (item.week_day === 'Wednesday') {
wednesdayAvailability.push(item);
}
else if (item.week_day === 'Thursday') {
thursdayAvailability.push(item);
}
else if (item.week_day === 'Friday') {
fridayAvailability.push(item);
}
else if (item.week_day === 'Saturday') {
saturdayAvailability.push(item);
}
else if (item.week_day === 'Sunday') {
sundayAvailability.push(item);
}
}
console.log('im here', mondayAvailability) // this is not logged
const interval = moment.duration(30, 'minutes');
let startTime;
let endTime
let arrayLength;
let slotTime;
let refinedData;
// further array manipulation...
console.log('REFINED DATA: ', refinedData) // <- this is not logged
return {
type: 'GET_TRAINER_AVAILABILITY_SUCCESS',
payload: refinedData
}
};
This isn't really an answer, but I can't post it easily in the comments.
1) Add some logging middleware so you can see what's going on. Mine is this:
const logger = store => next => action => {
//LOTS OF LOGGING
//console.log('[DISPATCHING] %s', action.type, JSON.stringify(action) )
//SOME LOGGING
console.log('[DISPATCHING] %s', action.type)
const result = next(action)
console.log('[NEXT STATE]', store.getState())
return result
}
2) If you're using a set of asynchronous actions (waiting/retrying a fetch for instance) you might want to check out redux-saga. It was basically built to deal with asynchronous workflows.
3) This isn't a complete example so I can't give you any feedback on other problem points. there are missing methods getTrainerAvailabilityRequest & getTrainerAvailabilityError for example
4) Add more logging, it is the best way to debug this if you can't attach a debugger. Also, are you using a debugger? Chrome development tools work quite well if you build with source maps. If not, drop in a lot of log-statements. You'll figure out what's hanging. There's also a really good chance there's an error in your success method. I would definitely put a logging line in that for loop to make sure it is actually running and exiting. You're processing data from a fetch call, that is always a failure point.
And onto the less opinionated stuff...
The problem point I see here is:
for (let item of rawAvailability) { What is rawAvailability? Is it actually an array?
Basically I'd try this:
for (let item of rawAvailability) {
console.log(`[DEBUG]`, JSON.stringify(item, null, 2) );
if (item.week_day === 'Monday') {
mondayAvailability.push(item);
}
else if (item.week_day === 'Tuesday') {
tuesdayAvailability.push(item);
}
else if (item.week_day === 'Wednesday') {
wednesdayAvailability.push(item);
}
else if (item.week_day === 'Thursday') {
thursdayAvailability.push(item);
}
else if (item.week_day === 'Friday') {
fridayAvailability.push(item);
}
else if (item.week_day === 'Saturday') {
saturdayAvailability.push(item);
}
else if (item.week_day === 'Sunday') {
sundayAvailability.push(item);
} else {
console.log(`[INVALID_ITEM]`, JSON.stringify(item, null, 2) );
}
}
Depending on your setup an error thrown in redux may not show up. Adding logging so you can see all your actions and data as well as logging inside possible failure points should show you the problem.
Good luck! This isn't really an answer, it is just how I would approach it.
Related
Now I am aware that there are many of questions that asked the same thing. But I also found many that implemented the right methods but nothing worked for them even peoples' answers
Basically, I wanted to use AsyncStorage to save a few user preferences. At first everything worked and was saved correctly, but then suddenly nothing worked anymore.
I kept trying and trying, and made a very interesting finding.
First here's my code:
My import:
import AsyncStorage from '#react-native-async-storage/async-storage';
Default State:
state : AppState = {
messages: [],
isMuted: false
}
This is my getter. It works on init:
componentDidMount() {
this.getSettings();
}
async getSettings() {
try {
AsyncStorage.getItem("muted").then((muted)=> {
if (muted != null) {
this.setState({"isMuted": eval(muted)});
console.log("init! "+this.state.isMuted.toString());
} else {
console.log("init! found null");
}
})
} catch(e) {
// error reading value
}
}
Here's my setter, it works onPress of a button
onPressSpeaker = async () => {
var muted = !this.state.isMuted;
this.setState({"isMuted": muted});
try {
await AsyncStorage.setItem("muted", this.state.isMuted.toString());
console.log("saved! "+this.state.isMuted.toString());
const muted = await AsyncStorage.getItem('muted');
if(muted !== null) {
console.log("data found! "+this.state.isMuted.toString());
}
} catch (e) {
console.log("error")
}
};
I believe I set everything correctly.
But here's my log (from Flipper)
20:57:41.654
init! true
20:57:44.247
saved! false
20:57:44.256
data found! false
20:58:04.788
Running "Voice Message" with {"rootTag":51}
20:58:05.800
init! true
The last init was supposed to return the new value but it keeps returning the old value again and again, everytime I refresh (restart) the application.
Did I do something wrong? Am I missing something? Is there something I need to know about react-native-async-storage?
I think the problem that you are storing the this.state.isMuted value before the state mutates
To better understand you can try this code
onPressSpeaker = async () => {
var muted = !this.state.isMuted;
this.setState({"isMuted": muted});
try {
//Here we are trying to log the state before Add it to Storage
console.log('State => Before AsyncStorage.setItem', this.state.isMuted)
await AsyncStorage.setItem("muted", this.state.isMuted.toString());
console.log("saved! "+this.state.isMuted.toString());
const muted = await AsyncStorage.getItem('muted');
if(muted !== null) {
console.log("data found! "+this.state.isMuted.toString());
}
} catch (e) {
console.log("error")
}
};
Your log will now be like this
20:57:41.654
init! true
20:57:44.247
'State => Before AsyncStorage.setItem' true
20:57:44.247
saved! false
20:57:44.256
data found! false
Solution: So you need to write the function in the callback to the setState function
storeIsMuted = async () => {
try {
console.log("before setItem", this.state.isMuted.toString());
await AsyncStorage.setItem("muted", this.state.isMuted.toString());
console.log("saved! " + this.state.isMuted.toString());
//
const muted = await AsyncStorage.getItem("muted");
if (muted !== null) {
console.log("data found! " + this.state.isMuted.toString());
}
} catch (e) {
console.log("error");
}
};
onPressSpeaker = () => {
var muted = !this.state.isMuted
this.setState({ isMuted: muted }, async () => this.storeMuted());
};
Documentation
SetState
I have built NFT staking website and it is working properly except that I forced to show the image update when staking or unstaking. That is because immediate update of image cannot be done with moralis getnftforcontract api and opensea api. The code is following.
const get_balanceUrls = async () => {
await Moralis.Web3API.initialize({ apiKey: WEB3APIKEY });
const options = { chain: CHAINETHID, address: walletAddress, token_address: G_ADDRESS, };
let ownedNFTs = await Moralis.Web3API.account.getNFTsForContract(options);
console.log("owning NFTS", ownedNFTs.result);
let stakedNFTs = Object.values(balanceofstakes);
if(ownedNFTs.result.length == 0 && nft_unstakeBalance.length == 0) {
setBalanceImageUrl([]);
return;
}
let token_ids = "";
ownedNFTs.result.map((value, index) => {
if(stakedNFTs.indexOf(value.token_id) < 0) {
token_ids += 'token_ids=' + value.token_id + "&";
}
})
nft_unstakeBalance.map((value, index) => {
token_ids += 'token_ids=' + value + "&";
});
if(token_ids == "") {
setBalanceImageUrl([]);
return;
}
console.log("token_ids", token_ids)
let assets = await Axios.get(`${OPENSEA_URL}assets?
order_direction=desc&offset=0&limit=20&${token_ids}asset_contract_address=${G_ADDRESS}`)
.then(res => {
console.log("balanceurl", res);
return res.data.assets;
})
.catch(err => {
console.log(err);
return [];
});
setBalanceImageUrl(assets);
}
I think there should be more convenient way to solve this issue. If you know, feel free to answer or discuss with me.
I think it takes a bit time for opensea to change the owner of NFT asset.
And what's more, I have used both of opensea api and moralis api but it took longer to use opensea api and I couldn't update the frontend images immediately.
I think to force update(add) the images with unstaked images with redux state variable when you unstake like this is the best way. But that is only my opinion.
-in reducer
case "nft_unstakeBalance":
return({
...state,
nft_unstakeBalance : action.payload
})
in frontend
nft_unstakeBalance.map((value, index) => {
token_ids += 'token_ids=' + value + "&";
});
Despite looking and following numerous answers here at stackoverflow,I have still failed to refactor this code to abide by the ESLint no-loop-func.
I keep getting the following warning, despite my efforts to refactor the code:
Compiled with warnings.
Function declared in a loop contains unsafe references to variable(s) 'lastResult', 'biologyBooks', 'page' no-loop-func
Here's the code:
import React from 'react';
import { apiFullCall } from '../../apiHelper';
const MyComponent = props => {
const [state, setState] = React.useState({ total: 0, biologyBooksByAuthor: [] });
let isLoaded = React.useRef(true);
const token = sessionStorage.getItem('token');
const authorID = sessionStorage.getItem('author_id');
const getBooks = async() => { // fetch items
let page = 1;
let scienceBooks, biologyBooks;
// create empty arrays to store book objects for each loop
let scienceBooks = biologyBooks = [];
// create a lastResult object to help check if there is a next page
let lastResult = { next: null };
do { // the looping - this is what I have failed to refactor
try {
await apiFullCall( // Make API calls over paginated records
'',
token,
'get',
`books/?author_id=1&page=${page}`
).then(res => {
if (res) {
const { status, body } = res;
if (status === 200 || status === 201) {
lastResult = body; // assign lastResult to pick "next"
body &&
body.results &&
body.results.map(eachBook => { // we map() over the returned "results" array
// the author with queried "author_id" writes science books;
// so we add each book (an object) into the science category
scienceBooks.push(eachBook);
// We then filter the author's biology books (from other science books)
biologyBooks = scienceBooks.filter(
({ is_biology }) =>
typeof(is_biology) === "boolean" && is_biology === true
);
return null;
}
);
// increment the page with 1 on each loop
page++;
}
}
}).catch(error => console.error('Error while fetching data:', error));
} catch (err) { console.error(`Oops, something went wrong ${err}`); }
// keep running until there's no next page
} while (lastResult.next !== null);
// update the state
setState(prevState => ({
...prevState, total: scienceBooks.length, biologyBooksByAuthor: biologyBooks,
}));
};
React.useEffect(() => { // fetch science books by author (logged in)
if (isLoaded && authorID) {
getBooks();
};
return function cleanup() {...}; // clean up API call, on unmount
}, [isLoaded, authorID]);
return (
// render the JSX code
);
}
Please note that I actually declared the said variables lastResult, biologyBooks and page outside the "do-while".
Any help or clues will be greatly appreciated.
The function the warning is referring to is the .then callback, if you're using async/await stick to it, try removing the .then part by assigning the result to a variable instead and remove the unnecessary .map, you can concatenate previous results with spread operator or .concat.
import React from 'react';
import { apiFullCall } from '../../apiHelper';
const MyComponent = props => {
const [state, setState] = React.useState({
total: 0,
scienceBooksByAuthor: [],
});
const isLoaded = React.useRef(true);
const token = sessionStorage.getItem('token');
const authorID = sessionStorage.getItem('author_id');
const getBooks = async () => {
// fetch items
let page = 1;
let scienceBooks = [];
// create a lastResult object to help check if there is a next page
let lastResult = { next: null };
do {
// the looping - this is what I have failed to refactor
try {
const res = await apiFullCall(
// Make API calls over paginated records
'',
token,
'get',
`books/?author_id=1&page=${page}`,
);
if (res) {
const { status, body } = res;
if (status === 200 || status === 201) {
lastResult = body; // assign lastResult to pick "next"
// concatenate new results
scienceBooks = [
...scienceBooks,
...((lastResult && lastResult.results) || []),
];
// increment the page with 1 on each loop
page += 1;
}
}
} catch (err) {
console.error(`Oops, something went wrong ${err}`);
}
// keep running until there's no next page
} while (lastResult.next !== null);
const biologyBooks = scienceBooks.filter(
({ is_biology }) =>
typeof is_biology === 'boolean' && is_biology === true,
);
// update the state
setState(prevState => ({
...prevState,
total: scienceBooks.length,
scienceBooksByAuthor: scienceBooks,
}));
};
React.useEffect(() => {
// fetch science books by author (logged in)
if (isLoaded && authorID) {
getBooks();
}
return function cleanup() {...}; // clean up API call, on unmount
}, [isLoaded, authorID]);
return (
// render the JSX code
);
};
I'm working in a project where I have to modify a value certain number of times before I make a request to an API. The problem is that when I'm using hooks to update the value, when I'm trying to update the value, as useState is asynchronous, the update of the value stays in a past value. However the value get modified after doing the request.
How can I make that the value updated before my request?
Here is the code:
useEffect(() => { // I'm using a useEffect hook to verify that my variable is updated. But that update is done late.
console.log(valorTotal);
}, [valorTotal]);
const agregarPlato = async () => {
if(validarCamposPlato()){
try{
let valorParcial = 0;
let platoExist = await encontrarPlato(config, rvPlato);
if(platoExist === true){
setAgregadoMin(true);
platoCodigo = await obtenerCodigoPlato(config, rvPlato);
platosRegVent.push({codigoPlato: platoCodigo, cantidad: rvCantidad});
let costoPlato = await obtenerValorPlato(config, rvPlato);
valorParcial = valorTotal;
setValorTotal(valorParcial += (costoPlato * parseInt(rvCantidad))); // Here is where I'm changing the value of my variable.
setRvPlato('');
setRvCantidad('');
}
else{
toast.error('The object wasn't found.');
setRvPlato('');
}
}
catch(error){
toast.error('An unexpected error has ocurred');
props.handleSubmit();
}
}
}
const finalizarRegVent = async () => {
console.log(agregadoMin);
if(validarCampos()){
try{
if(rvPlato !== '' || rvCantidad !== ''){
agregarPlato(); // Here I'm calling the function above
}
if(agregadoMin === true){
rvCodigo = await crearRegistroVenta(config, valorTotal, fechaActual, regVentMesa); // Here I'm doing the request to save the value
platosRegVent.forEach( (plato : any) => {
crearRegVentPlato(config, rvCodigo, platosRegVent.codigoPlato, platosRegVent.cantidad);
});
valorFinal = true;
}
else{
toast.error('You have to add an object before doing this option.');
}
}
catch(error){
toast.error('An unexpected error had happened.');
props.handleSubmit();
}
}
}
Thank you for your help.
Please try to use await in front of the calling function.
if(rvPlato !== '' || rvCantidad !== ''){
await agregarPlato();
}
And write below code inside of hook event.
useEffect(() => {
if(agregadoMin === true){
rvCodigo = await crearRegistroVenta(config, valorTotal, fechaActual, regVentMesa);
...
} else {
toast.error('You have to add an object before doing this option.');
}
}, [agregadoMin])
Then if agregadoMin is changed, hook will monitor changes and execute accordingly
Hope this helps you to understand.
I'm new to Ionic and Angular, and coming from years of .NET development. I'm trying a few examples online to build login prototype with Ionic 2.
I got WebAPI working in the background simply returning JSON true or false depending if credentials passed is correct or not.
I got authentication provider looking like this:
public login(credentials) {
if (credentials.email === null || credentials.password === null) {
return Observable.throw("Please insert credentials");
} else {
this.result = this.http.post(this.CONST.APIUrl, JSON.stringify(credentials), new RequestOptions({headers: this.contentHeader})).map(res => res.json())
if (this.result)
{
this.currentUser = new User('Simon', 'saimon#devdactic.com');
}
return this.result;
}}
and login page looking like this:
public login() {
this.showLoading()
this.auth.login(this.registerCredentials).subscribe(allowed => {
if (allowed) {
setTimeout(() => {
this.loading.dismiss();
this.nav.setRoot(HomePage)
});
} else {
this.showError("Access Denied");
}
},
error => {
this.showError(error);
});
}
At the moment it always logs person in. I understand that this is happening because this.result always has a value. But how would I check data returned from the API before allowing person to login?
You can use Observable.do to create side effects for an Observable.
In login function:
public login(credentials) {
if (credentials.email === null || credentials.password === null) {
return Observable.throw("Please insert credentials");
} else {
this.result = this.http.post(this.CONST.APIUrl,
JSON.stringify(credentials), new RequestOptions({headers: this.contentHeader}))
.map(res => res.json())
.do(userData=>{
//check userData and set current user
this.currentUser = new User('Simon', 'saimon#devdactic.com');
return userData;//make sure to return the value to subscribe
});
return result;
}}