Fetch more data in RxJs - api

I have some problem with apply fetching "more" data using fromFetch from rxjs.
I have project with React and RXJS. Currently I'm using something like this:
const stream$ = fromFetch('https://pokeapi.co/api/v2/pokemon?limit=100', {
selector: response => response.json()
}).subscribe(data => console.log(data));
But! I would like to change limit dynamically, when I click button or even better - when I scroll to the very bottom of my website. How to make something like this?
So that, based on some interaction, the limit would change?

The way your observable work in your case it's a request-response. You're declaring stream$ to be an observable that when someone subscribes it will make a request with limit=100.
There are different ways of solving this... The most straightforward would be:
const getPokemon$ = limit =>
fromFetch('https://pokeapi.co/api/v2/pokemon?limit=' + limit, {
selector: response => response.json()
});
const MyComponent = () => {
// ...
useEffect(() => {
const sub = getPokemon$(limit).subscribe(res => console.log(res));
return () => sub.unsubscribe();
}, [limit])
// ...
}
Another option, probably a bit more reactive but harder to follow for others, would be to declare another stream which sets the limit:
const limit$ = new BehaviorSubject(100)
const pokemon$ = limit$.pipe(
switchMap(limit => fromFetch('https://pokeapi.co/api/v2/pokemon?limit=' + limit, {
selector: response => response.json()
}))
);
// In your component
const MyComponent = () => {
// ...
useEffect(() => {
const sub = pokemon$.subscribe(res => console.log(res));
return () => sub.unsubscribe();
}, [])
changeLimit = (newLimit) => limit$.next(newLimit)
// ...
}
In this other solution, you're declaring how pokemon$ should react to changes on limit$, and you can set limit$ from any other component you want.

Related

How to mock expo-camera requestCameraPermissionsAsync() response?

I am trying to do a unit test to one of my react native components.
One scenario requires me to mock expo-camera requestCameraPermissionsAsync() method but don't know how. What I'm trying to do is to mock the status to always have the granted value.
Initial approach, below:
jest.mock('expo-camera', () => {
const PermissionsCamera = jest.requireActual('expo-camera');
return {
...PermissionsCamera,
requestCameraPermissionsAsync: () =>
new Promise(resolve => resolve({granted: true, status: 'granted'})),
};
});
But that doesn't work. Need help, is there something wrong with the code above? Thank you
Update:
How I implemented in the component:
import {Camera} from 'expo-camera'
useEffect(() => {
(async () => {
const {status} = await Camera.requestCameraPermissionsAsync();
// additional logic when status is equal to 'granted'
})();
}, []);

Unable to set useState variable in async method and console log it

Im working with my friends on a app project and we find an issue many times when we tring to set a use state and the console log the variable, i've looking for a solution in the website and saw that the reason is that the usestate is an async awiat which means the variable that i set in the use state isn't immidatly set in it, so i tried many solution that i found in the websites but none of them work for me.
in the screenShot you can see that the json variable is in console log before and the set varaible doesn't show after the setActiveUser , any help?
Thanks!
If you want to do something upon setting state then your best bet is to use the useEffect hook from React and adding your state in the dependency array. This will then call the function in your useEffect every time the state changes. See below a rough example:
import { Text } from 'react-native';
const MyComponent = () => {
const [activeUser, setActiveUser] = useState(null);
useEffect(() => {
// This should log every time activeUser changes
console.log({ activeUser });
}, [activeUser]);
const fetchAuthentication = async user => {
var flag = false;
await fetch('/api/authUser/', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(user),
})
.then(res => {
res.ok && (flag = true);
return res.json();
})
.then(json => {
if (flag) {
setActiveUser(json);
}
})
.catch(error => console.error(error));
return flag;
};
return <Text>Hi</Text>;
};
Full documentation: https://reactjs.org/docs/hooks-effect.html

React Native setTimeout - How to clearTimeout

I have a setTimeout call outside of useEffect, how do I clearTimeout when the screen unmounts?
For instance, I have a functional component with this in it...
...
useEffect(() => {
return () => clearTimeout(myTimeout)
}, [])
_getData = () => {
fetch()
.then(data => {
let myTimeout = setTimeout(() => setSomething(!something), 5000)
})
}
So somewhere later in the code I call _getData() - I do not want this to run with useEffect when the page first loads, only when certain action is taken. After I get the data I set the timeout. But the useEffect is not aware of this timeout.
I have tried setting the timeout like this...
_getData...
setTimeoutVar(setTimeout(() => setSomething(!something), 5000))
useEffect...
return () => clearTimeout(setTimeoutVar)
I have tried a few other weird ideas, nothing is working, I can't figure this one out.
Thoughts?
All day working on this - write a question on stackoverflow and figure it out in two minutes. Crazy!
The answer to this question is to set a variable to false, then, change the variable when you get the data back. Then have a separate useEffect() function that only deals with this. When the variable changes it runs. If the variable is true - sets the timeout and the useEffect function returns the clearTimeout...
const [refresh, setRefresh] = useState(false)
useEffect(() => {
let timeoutVariable
if(refresh){
timeoutVariable = setTimeout(() => setRefresh(false), 5000)
}
return () => clearTimeout(timeoutVariable)
}, [refresh])
_getData = () => {
fetch()
.then(data => {
setRefresh(true)
})
}

RamdaJS - How can I use pipe with sync and async functions?

I'm trying to learn Ramda and how to use it on my daily work. So I have a quick question. "How can I use pipe with sync and async functions?" or best, how can I improve the following code?
const AuthService = () => ({
async signIn(credentials: Credentials): Promise<AuthSession> {
const result = await api.signIn(credentials)
return R.pipe(
signInResultToAuthSession,
saveAuthSession
)(result)
}
})
[EDITED]: A second alternative that I think be better.
const AuthService = () => ({
async signIn(credentials: Credentials): Promise<AuthSession> {
return api.signIn(credentials).then(
R.pipe(
signInResultToAuthSession,
saveAuthSession
)
)
}
})
pipeWith was added for just such cases. It wraps the functions in a common interface (here then) and passes the results along, just like pipe.
const api = {signIn: ({pwd, ...creds}) => Promise.resolve({...creds, signedIn: true})}
const signInResultToAuthSession = (creds) => Promise.resolve({...creds, auth: true})
const saveAuthSession = (creds) => Promise.resolve({...creds, saved: true})
const AuthService = {
signIn: pipeWith(then)([
api.signIn, // [1]
signInResultToAuthSession,
saveAuthSession
])
}
AuthService.signIn({name: 'fred', pwd: 'flintstone'})
.then(console.log)
// [1]: `bind` might be necessary here, depending on the design of `api.signIn`
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {pipeWith, then} = R </script>
Do note that pipeWith wraps the functions in an array. One day we'd like to do the same with pipe, but that's a hugely breaking change.
You could create a function like this:
const then = f => p => p.then(f)
And then your pipe would look like:
const AuthService = () => ({
async signIn(credentials: Credentials): Promise<AuthSession> {
return R.pipe(
api.signIn,
then(signInResultToAuthSession),
then(saveAuthSession),
)(credentials)
}
})
You could even catch exceptions with:
const pCatch = f => p => p.catch(f)
R.pipe(
api.signIn,
pCatch(err => console.error(err)),
then(signInResultToAuthSession),
then(saveAuthSession),
)(credentials)

redux-observable, How to do an operator like promise.all()?

I have two async requests, want to write a epic do the job like promise.all()
const fetchData1 = () => (action$: ActionsObservable<any>, store: any) => (
ajax.getJSON('../../mockData/promiseAll/data1.json').map((data: any) => {
return requestData1Success(data);
})
);
const fetchData2 = () => (action$: ActionsObservable<any>, store: any) => (
ajax.getJSON('../../mockData/promiseAll/data2.json').map((data: any) => {
return requestData2Success(data);
})
)
const requestAllDataEpic = (action$: ActionsObservable<any>, store: any) => {
return action$.ofType(t.REQUEST_ALL_DATA)
.map((action) => action.payload)
.switchMap((names: string[]) => {
console.log(names);
return Observable.forkJoin([
fetchData1()(action$, store),
fetchData2()(action$, store)
])
.map((results: any[]) => {
const [action1, action2] = results;
requestData1Success(action1);
requestData2Success(action2);
});
});
};
But when I dispatch the action, the console give me an error:
Uncaught TypeError: Cannot read property 'type' of undefined
I think the reason is I do not give the middleware an action object, but undefined.
How can I do this correctly?
In the provided example, you are not actually returning your two actions, you're returning nothing:
.map((results: any[]) => {
const [action1, action2] = results;
// v----- not returning either of these
requestData1Success(action1);
requestData2Success(action2);
});
map can't used to emit two actions sequentially because it's 1:1 not 1:many (mergeMap, switchMap, concatMap, etc are 1:many). However, in your example you are already converting the responses to the actions inside your fetchData helpers--doing it again would wrap an action inside another action, not what you want. This looks like a bug when you were refactoring.
Other than that, it's actually not clear what you intended to do. If you have further questions you'll need to describe what you want you'd like to achieve.