I'm new to React!
I have a function...
export const getBookingData = () => dispatch => {
console.log('ran getBookingData');
return new Promise(async (resolve, reject) => {
})
}
Which I then call in another file by doing (which works fine):
import { getBookingData } from "../actions";
getBookingData(); // logs 'ran getBookingData'
However, I would like to try and call getBookingData from within the same file that it is declared.
I have tried:
const getBookingData = () => dispatch => {
console.log('ran getBookingData');
return new Promise(async (resolve, reject) => {
})
}
const moveVisitor = (visitorId, destination, source) => async (dispatch, getState) => {
console.log('error with post api'); // logs ok
getBookingData(); // doesn't log 'ran getBookingData'
let state = getState();
let { entities: { booking: { booking_id } } } = state;
let removeBed = {};
removeBed.booking_visitor_name_id = visitorId;
removeBed.room_id = destination;
removeBed.booking_id = booking_id;
api.post('/accommodation/room/move-participant', removeBed).then(function (response) {
// ok
}).catch(function (error) {
});
}
export { getBookingData, moveVisitor }
You can say that the getBookingData function is curried, as it is a function (accepting no parameter) returning another function (accepting dispatch object as parameter). What you have by just calling getBookingData() is an anonymous function which accepts the dispatch object as the parameter, so you need to call it once more.
Replacing your non-working call of getBookingData() with getBookingData()(dispatch) should work.
Have you tried exporting as below
const getBookingData = () => dispatch => {
return new Promise(async (resolve, reject) => {
// some stuff here
})
}
const moveVisitor = (visitorId, destination, source) => async (dispatch, getState) => {
getBookingData(); // doesn't work
}
export { getBookingData, moveVisitor }
Related
I have the code below:
service.js
module.exports = {
getUser
};
async function getUser({ data }) {
return new Promise((resolve, reject) => {
const id = data["id"];
const doc = await db.collection('users').where('id', '==', id).get();
if (!doc.exists) {
resolve('No such document!');
} else {
resolve(doc.data());
}
});
}
controller.js
async function getUser(req, res, next) {
userService.getUser({ data: req.body }).then(function (val) {
res.json(val);
});
}
This throws an error: SyntaxError: await is only valid in async functions and the top level bodies of modules. How can I retrieve the data from the await in an effective manner?
You can use await only inside async function.
function dummy() {
return new Promise((res, rej) => {
setTimeout(() => {
res(20)
}, 300)
})
}
let val = new Promise(async(resolve, reject) => {
let v = await dummy()
resolve(v)
})
val.then(value => console.log(value))
I have a websocket interface which I implemented so that I can use to send requests.
The problem is that the response is asynchronous and it initially returns the empty array because retObj is not updated from the callback function that I sent in. How can I make this function so that it will return the populated array when it has been updated.
This is how my Service looks like:
import * as interface from '../webSocket'
const carService = () => {
return {
getCars: () => {
interface.sendRequest(function (returnObject) {
//
}).then(d => d)
}
}
}
export default carService()
And this is how my action looks like:
import { GET_CARS } from '../constants'
import carService from '../carService'
export const getCars = () => async (dispatch) => {
try {
const cars = await carService.getCars()
console.log("At cars actions: ", cars) // logs: Array []
dispatch(getCarsSuccess(cars))
} catch (err) {
console.log('Error: ', err)
}
}
const getCarsSuccess = (cars) => ({
type: GET_CARS,
payload: cars
})
You simply have to wrap your callback into promise, since it was not a promise to begin with, which is why you cannot use then or await
import * as interface from '../webSocket'
const carService = () => {
return {
getCars: () => {
return new Promise(resolve => interface.sendRequest(function (returnObject) {
resolve(returnObject.msg)
}));
}
}
}
export default carService()
The problem is, you cant await a function unless it returns a Promise. So, as you can guess, the problem lies in carService.getCars's definition. Try this:
getCars: () => {
return new Promise((resolve, reject) => {
interface.sendRequest(function(returnObject) {
// if theres an error, reject(error)
resolve(returnObject);
})
})
}
Or, if sendRequest os am async function, simply return the return value of sendRequest:
getCars: () => {
return interface.sendRequest()
}
So, I have two methods in a class. Both returns a promise. The second function calls the first function from inside of the promise it returns.
module.exports = {
funcA: () => {
return new Promise((resolve, reject) => {
something ? resolve(something): reject('nope');
});
}
funcB: () => {
return new Promise(async(resolve, reject) => {
try {
const something = await this.funcA();
} catch(err) {
reject('error');
}
}
}
When I am trying to call funcB() from another class, like this:
let something = await someService.funcB();
I am getting:
TypeError: this.funcA() is not a function
Can you shed some light on why this is happening and how to solve this problem?
one way to make it work is to create the function outside of the module.exports block to get a reference of each function. Then this keyword can be omitted
const funcA = () => {
return new Promise((resolve, reject) => {
// code here
});
};
const funcB = () => {
return new Promise(async(resolve, reject) => {
try {
const something = await funcA();
resolve(something);
} catch(err) {
reject('error');
}
})
};
module.exports = {
funcA,
funcB
}
I think this is what you need to do
module.exports = {
funcA: function() {
return new Promise((resolve, reject) => {
something ? resolve(something): reject('nope');
});
}
funcB: function() {
return new Promise(async(resolve, reject) => {
try {
const something = await this.funcA();
} catch(err) {
reject('error');
}
}
}
I've found using arrow functions inside objects as you've done breaks this, but you can fix it this way.
Im trying to implement a function to make requests which caches the results.
The requirements are:
Can't use any global variable.
Results should be stored in the functions inner scope using closure.
I cant find any way to store a result inside the functions scope without using a class. I tried the following code but I realized this.responses is actually a global variable at window.responses. Any way to do it?
function cachedRequest(url) {
if (!this.responses) this.responses = {} // This is actually a global variable at window.responses, cant use it
return new Promise((resolve, reject) => {
const cachedValue = this.responses[url]
if (cachedValue) {
console.log('returning cached result')
return resolve(cachedValue)
};
fetch(url).then(res => {
console.log('fetching and caching result')
this.responses[url] = res
return resolve(res)
})
})
}
const URL = "https://pokeapi.co/api/v2/pokemon/ditto/"
cachedRequest(URL).then((response) => {
console.log({response})
cachedRequest(URL)
})
You could write a memoization function which keeps track inside a closure of which arguments are already used. You can inject each callback in the memo function to keep a storage on.
It also enables you to inject any amount of arguments and makes your code very flexible.
const memo = (callback) => {
const cache = new Map();
return (...args) => {
const selector = JSON.stringify(args);
if (cache.has(selector)) return cache.get(selector);
const value = callback(...args);
cache.set(selector, value);
return value;
};
};
const cachedRequest = memo(fetch);
const URL = "https://pokeapi.co/api/v2/pokemon/ditto/";
cachedRequest(URL).then((response) => {
console.log(response);
cachedRequest(URL);
});
You can wrap your request function in another function that defines the cache object. The returned function then has access to that object.
function cachedRequest() {
const cache = {}
return function(url) { // returned function has access to `cache`
return new Promise((resolve, reject) => {
const cachedValue = cache[url]
if (cachedValue) {
console.log('returning cached result')
return resolve(cachedValue)
}
fetch(url).then(res => {
console.log('fetching and caching result')
cache[url] = res
return resolve(res)
})
})
}
}
const URL = 'https://pokeapi.co/api/v2/pokemon/ditto/'
const request = cachedRequest() // initialize the request caching function
request(URL).then(response => {
console.log({ response })
request(URL)
})
You can bind cachedRequest to itself as the this context within the function.
cachedRequest = cachedRequest.bind(cachedRequest);
The Promises will keep the same context as arrow functions do not create a new one.
function cachedRequest(url) {
return new Promise((resolve, reject) => {
if (!this.responses) this.responses = {};
const cachedValue = this.responses[url]
console.log("function context => ", this.name);
console.log("this.responses => ", Object.keys(this.responses).length)
if (cachedValue) {
console.log('returning cached result')
return resolve(cachedValue)
};
fetch(url).then(res => {
console.log('fetching and caching result')
this.responses[url] = res
return resolve(res)
})
})
}
cachedRequest = cachedRequest.bind(cachedRequest);
const URL = "https://pokeapi.co/api/v2/pokemon/ditto/"
cachedRequest(URL).then((response) => {
cachedRequest(URL)
console.log("window.responses =>", window.responses != undefined);
})
You could use the module pattern like so:
const myApp = (() => {
let responses = {};
const cachedRequest = (url) => {
if (!this.responses) this.responses = {} // This is actually a global variable at window.responses, cant use it
return new Promise((resolve, reject) => {
const cachedValue = this.responses[url]
if (cachedValue) {
console.log('returning cached result')
return resolve(cachedValue)
};
fetch(url).then(res => {
console.log('fetching and caching result')
this.responses[url] = res
return resolve(res)
})
})
}
const init = (url) => {
cachedRequest(url);
};
return {
init
}
})();
const URL = "https://pokeapi.co/api/v2/pokemon/ditto/";
myApp.init(URL);
This way, only init() would be a public method. Everything else is not accessible.
Morning all... getting Actions must be plain objects. Use custom middleware for async actions. with the following action:
export const addHousehold = user => dispatch =>
new Promise(async (resolve, reject) => {
const hasHousehold = await getUsersHouseholdByUserId(user.uid);
if (hasHousehold) {
return reject(
new Error(
'You have already generated a household. Please complete the setup steps.',
),
);
}
return resolve(dispatch(createHousehold(user)));
});
The Promise should be implicitly returned from this function call, shouldn't it? The action is called on click from a component, the call is here:
addHousehold() {
this.props.addHousehold(this.props.user).then(
() => {
this.props
.addOnboardingStages(this.props.user.uid)
.then(res => {}, err => {});
},
err => this.setState({ errors: { generateHouseholdError: err.message } }),
);
}
This is my store setup, using Redux thunk as middleware.
const store = createStore(
rootReducer,
INITIAL_STATE,
applyMiddleware(thunk),
);
UPDATE:
The createHousehold function looks as follows:
const createHousehold = user => {
const { uid } = user;
const householdId = uniqid.time();
return Promise.all([
setHousehold(uid, householdId),
setUser(uid, householdId),
]);
};
const setHousehold = (uid, householdId) => dispatch =>
new Promise((resolve, reject) => {
console.log(uid)
db
.collection('households')
.doc(householdId)
.set(
{
users: [uid],
},
{ merge: true },
)
.then(() => {
resolve();
})
.catch(() => reject());
});
const setUser = (uid, householdId) => dispatch =>
new Promise((resolve, reject) => {
db
.collection('users')
.doc(uid)
.update({
household: householdId,
})
.then(
() => resolve(dispatch(receiveHousehold(householdId))),
err => console.error(err),
);
});
UPDATE 2
Looks like the issue was dispatch wasn't passed in to createHousehold:
const createHousehold = user => dispatch => {
const { uid } = user;
const householdId = uniqid.time();
return Promise.all([
setHousehold(uid, householdId),
setUser(uid, householdId)(dispatch) // here
]);
};
Thank you :)