I have a function which I want to make a point free with converge:
const getProfileAndRepos = R.converge(R.merge, [
x => getProfile(x),
x => getRepos(x)
])
My getProfile and getRepos are async functions.
When getProfileAndRepos('john') is invoked I get back:
f.apply(...).then is not a function
UPDATE:
I kinda find a solution;
const getProfileAndRepos = R.converge(R.identity, [
x => Promise.all([getProfile(x), getRepos(x)])
])
but now I get [object, object] but I want to unnest it so I get [profile: {}, repos: []]
This now might be a bit off the topic... sorry
I got it done making outer function:
const getData = async player => {
const [profile, repos] = await Promise.all([
getProfile(player),
getRepos(player)
])
return {
profile,
repos
}
}
const getProfileAndRepos = R.converge(R.identity, [x => getData(x)])
Related
I am using firebase for my app and the data i read i want to put that in state to use it in different places.
it kinda works but when i want to console.log the state it updates like 30 times a second, am i doing something wrong?
this is my code
const db = firebase.firestore();
const [PBS1Detail, setPBS1Detail] = useState();
db.collection('Track').get().then((snapshot) => {
snapshot.docs.forEach(doc => {
renderTracks(doc)
}
)
});
const renderTracks = (doc) => {
let data = doc.data().data[0].Module;
return setPBS1Detail(data);
}
console.log(PBS1Detail)
i already tried to store it in a variable instead of state but thats not working for me, i can't get the variable from the function global
i am a noob i get it xd
You don't need a return statement when setting state. Also, it looks like you're performing some async function which means that when your component renders for the first time, PBS1Detail will be undefined since the db is a pending Promise.
You could/should put your async functions in useEffect hook:
useEffect(()=> {
db.collection('Track').get().then((snapshot) => {
snapshot.docs.forEach(doc => {
renderTracks(doc)
}
)
});
}, [])
const renderTracks = (doc) => {
let data = doc.data().data[0].Module;
setPBS1Detail(data);
}
Finally, your renderTracks function doesn't seem correct as it appears you're looping over docs and resetting your state each time.
Instead, maybe consider having an array for PBS1Detail
const [PBS1Detail, setPBS1Detail] = useState([]);
Then modify your async call:
useEffect(()=> {
db.collection('Track').get().then((snapshot) => {
let results = []
snapshot.docs.forEach(doc => {
results.push(renderTracks(doc))
}
)
setPBS1Detail(results)
});
}, [])
const renderTracks = (doc) => {
return doc.data().data[0].Module;
}
This way you're only setting state once and thus avoiding unnecessary re-renders and you're saving all of your docs instead of overwriting them.
I am trying to test my mapDispatchToProps actions when an async function is dispatched. I almost tried every possible solution I found and nothing worked so far. I'm always getting the same error:
I'm getting this error:
TypeError: store.dispatch(...).then is not a function
I tried the solution included in redux-mock-store https://github.com/dmitry-zaets/redux-mock-store. I included my middlewares to my mockStore, but it didn't fix the issue.
I tried the solution proposed by Michael Peyper here Testing dispatched actions in Redux thunk with Jest.
We created a function to build the mockStore so I tried to create my mockStore directly within my test file instead, but they both returned the same error.
I can't put all the solutions I tried here because it would take me weeks, but it gives you an idea.
Here's the code for my test:
describe('Component async actions', () => {
const middlewares = [thunk, queryMiddleware];
const createMockStore = configureStore(middlewares);
const store = createMockStore();
afterEach(() => {
jest.clearAllMocks();
});
const someData = {};
const expectedActions = {
type: ADD_DATA,
payload: someData
};
it('should handle addData', () => {
return store.dispatch(actions.addData(someData)).then(() => {
expect(store.getActions()[0]).toEqual(expectedAction);
});
});
});
Here's my mapDispatchToProps:
function mapDispatchToProps(dispatch) {
return {
addData: data => dispatch(addData(data))
.then(({ status }) => {
dispatch(showNotification({ status }));
}),
};
};
I would like to at least be able to get to the expect part and fix this if there's any error in my test, but I can't get passed the dispatch().then
Again, here's the error I get each time: TypeError: store.dispatch(...).then is not a function
Thanks in advance!
I don't know if anyone will get this problem, but I found a solution.
First of all, I had to add my thunk middleware to my createStore from redux-mock-store.
import thunk from 'redux-thunk';
...
const createMockStore = createStore([thunk]);
Then I did a mock of my addData function like this:
import { addData } from 'path/to/addData';
...
jest.mock('path/to/addData');
and I added this code within my test:
addData.mockReturnValue(() =>
new Promise((resolve) => resolve({ status: 200 }));
));
It works!
For example comparing the two codes below, the first one using async/await and the other calling axios with .then.
What code is recommended?
const BASE_URL = "https://jsonplaceholder.typicode.com"
// async await syntax
export const fetchPosts = () => async dispatch => {
const response = await axios.get(BASE_URL + "/posts")
dispatch({ type: "FETCH_POSTS", payload: response })
}
// using .then instead
export const fetchPosts2 = () => dispatch => {
axios.get(BASE_URL + "/posts").then(response =>
dispatch({
type: "FETCH_POSTS",
payload: response
})
)
}
They're both essentially identical. The only thing it comes down to is pure preference. I personally prefer the async/await syntax because it can save you some potential headaches when doing multiple calls, avoiding some particually nasty nested calls:
// async await syntax
export const fetchPosts = () => async dispatch => {
const posts = await axios.get(BASE_URL + "/posts")
const users = await axios.get(BASE_URL + "/users", {
params: posts.map(p => p.author_id)
})
dispatch({ type: "FETCH_POSTS", payload: {
posts, users
}})
}
vs:
// async await syntax
export const fetchPosts = () => dispatch => {
axios.get(BASE_URL + "/posts").then(posts =>
axios.get(BASE_URL + "/users", {
params: posts.map(p => p.author_id)
}).then(users => {
dispatch({ type: "FETCH_POSTS", payload: {
posts, users
}})
})
)
}
Don't forget about the try/catch syntax as well. You can try/catch entire blocks of code, and then dispatch an error as well. In the later case (Not using async/await), you would need to chain the .then()'s into 2 separate error handlers.
// #flow
type Deferred = Promise<any> & {
reject: Function;
resolve: Function;
};
/** Deferred based on Promise
#return {Promise} */
export default (): Deferred => {
let res, rej;
let deferred: Deferred = Object.assign(
new Promise( ( resolve, reject ) => {
res = resolve;
rej = reject;
}),
{ 'resolve': res, 'reject': rej }
);
return deferred;
};
Right now this code gives such errors:
Cannot assign Object.assign(...) to deferred because:
property reject is missing in Promise [1] but exists in object type [2].
property resolve is missing in Promise [1] but exists in object type [2].
Property reject is missing in Promise [1].
Property resolve is missing in Promise [1].
And the question is: how to correctly document this code without errors?
It's not a good idea to use any.
It's not a good idea to use Function because it's just an alias for any.
It's better not to use &: it's broken. Use type spread instead.
If you really want to extend Promise, just extend it like this https://flow.org/try/#0PTAEAEDMBsHsHcBQBjaBDAzh0ARAppHgE5F4AmAPACoB8oeAHgC54B2Z2ACkbALYCWGPNToBvRKFABiUhljQAbngBcoABRUAlKAC8dfIRLkRoAD6gFsfmQDcE6aQBWeZE1VqBDctr24CxUkpaMwsrW3t7ZFhWDCYiAFdXWCI1SFYAfndZeSVM9S1dOk9yABpQJxcmPI9+LzIfItrvQtDrbXFJSSiYplAABx4GAE9dUFEAXztO0Ax4vuI1NWzFPDKK1wax+2mB2GGAOmWlUaO8Ken+waHDvGdXE9vK8+n+SHU07TSlvDkVtceNs9QONNECmAALQT7GQ-HJ4Ua7A6nMGQjDQ9a9HSXPbXDHncYRSSnb6zaBuUBaVQGALGYIdToQqEw35KEnxMmg7blPBMeJEVigRkYfGIewYtQBZKqYr1Kn+IxBMRcoXogFMCUkZKc6akXn8wWokWSRCTUXdWKgMgEUasPDwPyGQJqTmIECgeDJADWGEQVsgNxZeDUAEZNPsIWxFgxVKx4rwAEbETbm+R4fZwADmagYmk5bqiRlc0BGGHBCGwaAFkqIvoIAbhIbDEdYUdUsSI-FYGeT0V+acz2dzdkQjD6yV6frQ7N61IVdiAA
class Deferred<T> extends Promise<T> {
#resolve: (T) => Deferred<T> | void;
#reject: (mixed) => Deferred<T> | void;
constructor(fn?: (resolve?: (T) => mixed, reject?: (mixed) => mixed) => void) {
const proxy = {};
super((resolve, reject) => {
proxy.resolve = resolve;
proxy.reject = reject;
if (fn) fn(resolve, reject);
});
this.#resolve = proxy.resolve;
this.#reject = proxy.reject;
}
resolve(result: T): Deferred<T> {
this.#resolve(result);
return this;
}
reject(error: mixed): Deferred<T> {
this.#reject(error);
return this;
}
};
i'm trying to compose some functions together:
compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));
checkAuthorization returns a promise that check if a user is authorized.
buildParams receives someRequestData, and pipes the result to searchItem.
checkAuthorization()
.then(() => {
compose(
searchItem,
buildParams
)(someRequestData)
}, (e) => {
handleError(e)
})
I think it's OK, but I wish to have a more elegant look for readability, something like:
compose(
searchItem,
checkAuthorization
buildParams
)(someRequestData)
so what will happen is:
1) build params
2) checkAuth
3) search item
Any suggestions?
No, that's not possible, since checkAuthorisation does not receive and pass through the params. And even if you would rewrite it to do that, it still would be weird and a reader would assume that you're building the params whose authorisation should be checked. So don't do that - you have a non-linear flow, and trying to force it into some linear composition is no good.
Btw, I would recommend to avoid compose when you're calling the function immediately anyway:
checkAuthorization().then(() =>
searchItem(buildParams(someRequestData))
, e =>
handleError(e)
);
or maybe
checkAuthorization().then( compose(searchItem, buildParams, ()=>someRequestData)
, handleError ); // ^^^^ "const"
Here's a composer to handle both sync functions and Promises. Looks like it works correctly maintaining the order:
// Async pipe try. Pass functions left to right
const pipePromises = (...fns) => x => fns.reduce((p, fn) => p.then(fn), Promise.resolve(x));
// functions for the test
const me = x => new Promise(function(resolve, reject) {
setTimeout(() => resolve(x), 10)
})
const double = x => new Promise(function(resolve, reject) {
setTimeout(() => resolve(x * 2), 30)
})
const inc = x => new Promise(function(resolve, reject) {
setTimeout(() => resolve(x + 1), 400)
})
const log = x => { console.log('log: ', x); return x }
const syncTriple = x => x * 3; // sync function
// let's call our chain
pipePromises(
me, log, // 3
double, log, // 6
syncTriple, log, // 18 -- SYNC
inc, log, // 19
double, log, // 38
inc, log, // 39
syncTriple, log, // 117 -- SYNC
inc, log // 118
)(3) // 3
I just made an npm module to handle elegant Promise composition.
It's still in early stage, but you're welcome to check out the code and change it as it fits your needs and standards.
Basically it offers two methods which might meet your needs:
Combine
With Promise.combine({...}) you can combine several Promises by providing an object with a series of functions returning Promises and accepting the result of previous ones as input like this:
Promise.combine({
item: () => searchItem,
auth: ({item}) => checkAuth,
params: ({item, auth}) => buildParams
}).then(({item, auth, params}) => {
// here you can do what you need
})
Reduce
With Promise.reduce([...]) you can chain Promises in an array of functions returning Promises and accepting as input the output of the previously executed Promise:
Promise.reduce([
() => searchItem,
(item) => checkAuth,
(auth) => buildParams
]).then((params) => {
// here you can do what you need
})
Notice in this case you won't have access to item in the .then() function, but you could always compose the result of the checkAuth Promise in order to pass the item downstream as well:
Promise.reduce([
() => searchItem,
(item) => checkAuth.then((auth) => {
return {auth, item}
}),
({auth, item}) => buildParams.then((params) => {
return {params, item}
}),
]).then(({params, item}) => {
// here you can do what you need
})
Input
You can also add some input data from the request like this:
Promise.reduce([
(requestData) => searchItem,
(item) => checkAuth,
(auth) => buildParams
], requestData).then((params) => {
// here you can do what you need
})
See I passed the requestData as second parameter of Promise.reduce([...], requestData) and it gets passed as parameter to the first function.
Here you can see the functions code.
Hope this helps.