Pass data to Task Manager in react-native - react-native

Currently i have this task created:
import * as TaskManager from 'expo-task-manager';
export const BACKGROUND_FETCH_TASK = 'background-fetch';
TaskManager.defineTask(BACKGROUND_FETCH_TASK, async ({ error, data, executionInfo }) => {
// Can i pass args from recoil to here which are needed for backend call?
console.log('Error: ', error);
console.log('Data: ', data);
console.log('Execution info: ', executionInfo);
const now = Date.now();
console.log(`Got background fetch call at date: ${new Date(now).toISOString()}`);
// Be sure to return the successful result type!
return BackgroundFetch.BackgroundFetchResult.NewData;
});
How i can pass data from outside can't find any relative info

Related

React Native, Redux - How to execute async action after successfully executing another async action

In a react native application, I need to update a list item by executing an async action and after successfully executing the particular update async action, I need to reload the list item respectively with the changes of the above update action. Here I'm reloading the list by executing an async action.
I would like to know how to execute two async actions(A and B) sequentially after successfully executing the first one(A) and then the second one (B)
I have implemented a react-native app with redux. Basically it is communicating with an API using web services. I have used Fetch API for implementing async calls and have used a custom implemented Http middleware to handle async calls as a common method(I have not used thunk)
The custom middleware looks like below
export const commonHttpAction = (action) => {
const commonHttpActionTemplate = {
type: '',
urlParam: null,
httpMethod: action.requestMethod == undefined ? 'GET' : action.requestMethod,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + accessToken
},
body: action.requestBody == undefined ? undefined : action.requestBody,
payload: null
};
return {
HTTP_ACTION: Object.assign({}, commonHttpActionTemplate, action)
};
};
const httpMiddleware = store => next => action => {
if(action[HTTP_ACTION]) {
const actionInfo = action[HTTP_ACTION];
const fetchOptions = {
method: actionInfo.httpMethod,
headers: actionInfo.headers,
body: actionInfo.body || actionInfo.requestBody || actionInfo.payload || null
};
next({
type: actionInfo.type + "_REQUEST"
});
fetch(getHostUrl() + '/' + actionInfo.urlParam, fetchOptions)
.then(response => response.json())
.then(responseJson => next({
type: actionInfo.type + "_SUCCESS",
payload: responseJson
}))
.catch(error => next({
type: actionInfo.type + "_FAILURE",
payload: error
}));
} else {
return next(action);
}
}
export default httpMiddleware;
Then I have dispatched the async actions through mapDispatchToProps and connect() functions in react-native components/screens using the above custom middleware.
Then reducers will handle the response according to action types.
eg:
ACTION_TYPE_REQUEST, ACTION_TYPE_SUCCESS and ACTION_TYPE_FAILURE
Then in components/screens, I have used "mapStateToProps" function to use the paylod from reducers
As the above-described way, I have fetched data to my screens and Imagine if I have created a Flatlist by dispatching an async action to load the data to the list and I will update one of the list items by dispatching another async action.
I need to re-render the Flatlist after successfully finishing update async action.
So far I have attempted a callback function. but with my implementation, the list loading async action is not dispatching (Simply the Flatlist is not reloading after one of list items updating).
I have written the callback as below
class ExampleComponent extends Component {
componentDidMount() {
this.props.listDataLoadingAction()
}
render() {
return(
<View>
<Flatlist
renderItem={(item) =>
<View>
<TouchableOpacity onPress={() => {this.updateAction,
()=> this.props.listDataLoadingAction()}}>
</TouchableOpacity>
</View>
}
/>
</View>
);
}
updateActon =(callback) => {
this.props.updateListRecordAction();
callback();
}
}
const mapStateToProps = state => {
return{
//get the reducer data
}
}
const mapDispatchToProps = dispatch => {
return {
istDataLoadingAction: () => dispatch(istDataLoadingAction()),
updateListRecordAction: () => dispatch(updateListRecordAction())
}
}
export default connect(mapstateToProps, mapDispatchToProps)(ExampleComponent)
It will be much appreciated if anyone can come up with a solution
It would really help if you had a code snippet of what it is you're trying to do.
In general though you can use async/await
async function () {
await firstAction();
await secondAction();
}
If the first action does not impact the second then I would dispatch and await both
async function () {
await Promise.all([
firstAction(),
secondAction(),
]);
}

Redux Saga yield call(someAPI) is not waiting for API call to complete

I'm making an app that fetch events object from firestore to display it on a map.
I implemented redux-saga to do the async call to firestore API on componentDidMount in order to display the results on the map.
I have 3 actions (LOAD_EVENTS_LOADING / LOAD_EVENTS_SUCCESS / LOAD_EVENTS_ERROR) so that I can display a loading component before rendering the results.
saga.js :
> import { put, call, takeLatest } from 'redux-saga/effects' import {
> getEventsFromGeoloc } from '../../firebaseAPI/APImethods'
>
> function* fetchEvents(action) {
> try {
>
> const events = yield call(getEventsFromGeoloc, {latMarker : action.payload.latMarker, longMarker: action.payload.longMarker,
> circleRadius: action.payload.circleRadius});
> yield put({type: 'LOAD_EVENTS_SUCCESS', fetchedEvents: events});
> } catch (e) {
> yield put({type: 'LOAD_EVENTS_ERROR', error: e.message});
> } }
>
> export function* eventsSaga() {
> yield takeLatest('LOAD_EVENTS_LOADING', fetchEvents); }
>
> export default eventsSaga;
My problem is that in my saga the action "LOAD_EVENTS_SUCCESS" is dispatched before the API call ends.
How do I make sure the API call is completed before dispatching the "LOAD_EVENTS_SUCCESS" action ?
Thanks for your help !
Here is my API method :
import firebase from 'react-native-firebase';
import { GeoFirestore } from 'geofirestore';
export const getEventsFromGeoloc = (payload) => {
let events =[]
const geoFirestore = new GeoFirestore(firebase.firestore());
const geoCollection = geoFirestore.collection('events');
const query = geoCollection.near({
center: new firebase.firestore.GeoPoint(payload.latMarker, payload.longMarker),
radius: payload.circleRadius
});
query.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
const idEvent = doc.id
const eventData = doc.data()
events.push({idEvent, eventData})
});
})
.catch(function(error) {
console.log("Error getting documents: ", error);
});
return events;
}
getEventsFromGeoloc is not returning a promise for redux-saga to await, that's the problem
Just return a promise that resolve with the events:
export const getEventsFromGeoloc = (payload) => {
// ...
return query.get()
.then(function(querySnapshot) {
const events = []
querySnapshot.forEach(function(doc) {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
const idEvent = doc.id
const eventData = doc.data()
events.push({idEvent, eventData})
});
return events
})
.catch(function(error) {
console.log("Error getting documents: ", error);
});
}
call(fn, ...args) fn: Function - A Generator function, or normal function which either returns a Promise as result, or any other value. Refer this docs In your case you are returning events from your getEventsFromGeoloc func even when your response has not yet arrived.
Firebase works differently than you would expect. The firebase queries return immediately, they don't wait for the query to finish and be processed by your code before it returns.
What I ended up doing in my own apps, was instead of calling the success/fail action from the original saga function, I called them from within my query callback, something like this:
query.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
const idEvent = doc.id
const eventData = doc.data()
events.push({idEvent, eventData})
});
yield put({type: 'LOAD_EVENTS_SUCCESS', fetchedEvents: events});
})
.catch(function(error) {
console.log("Error getting documents: ", error);
yield put({type: 'LOAD_EVENTS_ERROR', error: error.message});
});
NOTE: This will also require you make your getEventsFromGeoloc function a generator function, or a saga, to use the yield statements as well as import the put saga effect.
OTHER NOTE: You could make the function more generic by passing in a callback function for success and failure and just call the callback instead of hard coding the action you are dispatching like this. Or you could pass in the action type and payload keys to build the action inside the completion callback for firebase.
Let me know if that wasn't helpful enough, or if you have questions about what I've put up here.
thanks all for your answers. I did a saga with my getEventsFromGeoloc and with a yield for the query its working like I expected : It waits for the complete query to run and then return the array of events and dispatch the LOAD_EVENTS_SUCCESS action.
Here is my saga :
import { put, takeLatest } from 'redux-saga/effects'
import firebase from 'react-native-firebase';
import { GeoFirestore } from 'geofirestore';
function* getEventsFromGeoloc(action) {
try{
let events =[]
const geoFirestore = new GeoFirestore(firebase.firestore());
const geoCollection = geoFirestore.collection('events');
const query = geoCollection.near({
center: new firebase.firestore.GeoPoint(action.payload.latMarker, action.payload.longMarker),
radius: action.payload.circleRadius
});
yield query.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
const idEvent = doc.id
const eventData = doc.data()
events.push({idEvent, eventData})
})})
yield put({type: 'LOAD_EVENTS_SUCCESS', fetchedEvents: events});
}
catch(error) {
console.log("Error getting documents: ", error);
yield put({type: 'LOAD_EVENTS_ERROR', error: error.message});
}
}
export function* eventsSaga() {
yield takeLatest('LOAD_EVENTS_LOADING', getEventsFromGeoloc);
}
export default eventsSaga;

redux-thunk: actions are not dispatching

I am trying to build an app in react native that is suppose to take take two inputs by a user and then make a query to an api and get information about the two inputs. I have been having trouble with redux and redux-thunk and specifically with async actions.
This is the code in my app that i am specifically having trouble with
export const fetchData = url => {
console.log("start Fetching");
return async dispatch => { // this is where the problem is
dispatch(fetchingRequest());
try {
const response = await fetch("https://randomuser.me/api/?results=10");
const json = await response.text();
if (response.ok) {
dispatch(fetchingSuccess(json));
console.log("JSON", json);
} else {
console.log("fetch did not resolve");
}
} catch (error) {
dispatch(fetchingFailure(error));
}
};
console.log("Fetched data");
};
Upon debugging the function, I have ended with finding that when the fetchData function is called the function will execute but the async dispatch that is being returned has undefined behavior.
The output in the debugger when the function is called should be
start Fetching
JSON file information/Error
but the output in the debugger is actually
start Fetching
This is the function in which fetchData is called in
_onPress = () => {
let url = "https://randomuser.me/api/?results=10";
fetchData(url);
console.log("should have fetched");
};
this is the mapDispatchToProps function that I have added. The problem is i do not know what to add inside the function.
const mapStatetoDispatch = (url, dispatch) => {
return {dispatch(fetchData(url))}; // do not know what to place in body of function
};
i have connected it in the component with
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
these are the action creators that I import, if needed
import {
fetchingSuccess,
fetchingRequest,
fetchingFailure,
fetchData
} from "../data/redux/actions/appActions.js";
Assuming you have added redux-thunk as a middleware, it looks like the errors are here:
_onPress = () => {
const { fetchData } = this.props;
let url = "https://randomuser.me/api/?results=10";
fetchData(url);
console.log("should have fetched");
};
and
const mapStatetoDispatch = dispatch => ({
fetchData: url => dispatch(fetchData(url)),
}};

Jest Vue Expected mock function to have been called with, but not called

I am trying to mock an api call using Jest and Vue but I get the error "Expected mock function to have been called with: ... but not called"
I have tried to find a solution but haven't found anything yet.
import DocumentService from "../../src/services/document";
import mockedData from "../__mockData__/documents";
import axios from "axios";
it("should call Document Service and download a document", () => {
let catchFn = jest.fn(),
thenFn = jest.fn();
DocumentService.downloadDocumentById(jwtToken, DocumentURL, id)
.then(thenFn)
.then(catchFn);
// expect(axios.get).toHaveBeenCalledWith(DocumentURL + "/" + id + "/content", {
// headers: { Authorization: "Bearer " + jwtToken, "X-role": "SuperUser" }
// });
expect(axios.get).toHaveBeenCalledWith(DocumentURL);
let responseObj = { data: mockedData };
axios.get.Mockresponse(responseObj);
expect(thenFn).toHaveBeenCalledWith(mockedData);
expect(catchFn).not.toHaveBeenCalled();
});
The test runs synchronously and the expect runs and fails before the Promise callbacks have a chance to run.
Make sure you await the Promise returned by DocumentService.downloadDocumentById to give the callbacks a chance to run:
it("should call Document Service and download a document", async () => { // use an async test function
let catchFn = jest.fn(),
thenFn = jest.fn();
const promise = DocumentService.downloadDocumentById(jwtToken, DocumentURL, id)
.then(thenFn)
.then(catchFn); // remember the Promise
expect(axios.get).toHaveBeenCalledWith(DocumentURL);
let responseObj = { data: mockedData };
axios.get.Mockresponse(responseObj);
await promise; // wait for the Promise
expect(thenFn).toHaveBeenCalledWith(mockedData); // SUCCESS
expect(catchFn).not.toHaveBeenCalled(); // SUCCESS
});
Had the same trouble, made it this way:
import axios from 'axios';
in test axios.get = jest.fn();
expect( axios.get ).toBeCalledWith( yourUrl );

React Native redux saga yield not working on first action

I am using redux saga in my React Native app. It all works except for one thing. For one of my actions when it is dispatched the first time, it stops at 'const newUserLogAction = yield take('CREATE_USER_LOG')' in the code below. So the console log 'saga callCreateUserLog take' is not printed, nothing happens. But if I dispatch the same action again, it works. I have other actions and they all work fine the first time.
saga file:
function * createUserLog () {
yield takeEvery('CREATE_USER_LOG', callCreateUserLog)
}
export function * callCreateUserLog () {
try {
console.log('saga callCreateUserLog start')
const newUserLogAction = yield take('CREATE_USER_LOG')
console.log('saga callCreateUserLog take')
console.log('newUserLogAction' + FJSON.plain(newUserLogAction))
const data = newUserLogAction.data
const newUserLog = yield call(api.createUserLog, data)
yield put({type: 'CREATE_USER_LOG_SUCCEEDED', newUserLog: newUserLog})
} catch (error) {
yield put({type: 'CREATE_USER_LOG_FAILED', error})
}
}
action file:
export const createUserLog = (data) => {
console.log('action create user log data = ' + FJSON.plain(data))
return ({
type: 'CREATE_USER_LOG',
data}
)
}
Even on the first dispatch, the data is printed correctly here, so the payload is correct.
react native functions doing the dispatching:
clickContinue = () => {
var event = {
'userId': this.props.user.id,
'eventDetails': {
'event': 'Agreed to the ISA Declaration'
}
}
this.props.dispatch(createUserLog(event))
}
You don't need to use take effect. Action object will be passed by takeEvery.
export function * callCreateUserLog (newUserLogAction) {
try {
console.log('saga callCreateUserLog start')
console.log('newUserLogAction' + FJSON.plain(newUserLogAction))
const data = newUserLogAction.data
const newUserLog = yield call(api.createUserLog, data)
yield put({type: 'CREATE_USER_LOG_SUCCEEDED', newUserLog: newUserLog})
} catch (error) {
yield put({type: 'CREATE_USER_LOG_FAILED', error})
}
}