Whats the correct way to invoke a registered task? - react-native

I am using expo version 41.0.0 and i defined task and export the registered task. This is how
the code looks like
import * as BackgroundFetch from "expo-background-fetch";
import * as TaskManager from "expo-task-manager";
import axios from 'axios';
const TASK_NAME = "BACKGROUND_TASK";
TaskManager.defineTask(TASK_NAME, () => {
try {
// fetch data here...
const receivedNewData = "Simulated fetch " + Math.random()
console.log("My task ", receivedNewData)
return receivedNewData
? BackgroundFetch.Result.NewData
: BackgroundFetch.Result.NoData
} catch (err) {
return BackgroundFetch.Result.Failed
}
});
export const RegisterBackgroundTask = async () => {
try {
await BackgroundFetch.registerTaskAsync(TASK_NAME, {
minimumInterval: 5, // seconds,
});
console.log("Task registered")
} catch (err) {
console.log("Task Register failed:", err)
}
}
and this is how I invoke the registered task
import { RegisterBackgroundTask } from '../../helper/background-task';
const SomeComponent = ({ ...props }) => {
RegisterBackgroundTask();
...rest of the code
return (
<View>
other stuff here...
</View>
)
}
PS: I run the app through Expo and I am using IOS
Is this the correct way to invoke registered task, or can it be called inside a lifecycle method like useEffect ?

Related

React Native useEffect not run when app first opened

I am trying to make an app where the USER_ID is loaded from the device's local storage if found, otherwise, a new id is generated and stored in the local storage. I am trying to make use of React useContext() to make the USER_ID visible to the whole app after it is first run.
import React, {useEffect, useState} from 'react';
import AsyncStorage from '#react-native-async-storage/async-storage';
import uuid from 'react-native-uuid';
export const UserIdContext = React.createContext('undef');
export const UserIdProvider = ({children}) => {
const [userId, setUserId] = useState('');
useEffect(() => {
async function getOrInitUserId() {
try {
let temp = await AsyncStorage.getItem('USER_ID');
if (temp == null) {
temp = uuid.v4();
await AsyncStorage.setItem('USER_ID', uuid.v4());
console.log('USER_ID Generated: ' + temp);
} else {
console.log('USER_ID Found: ' + temp);
}
setUserId(temp);
} catch (error) {
console.error(error);
}
}
if (!userId) {
getOrInitUserId();
}
});
return (
<UserIdContext.Provider value={userId}>{children}</UserIdContext.Provider>
);
};
export const useUserId = () => React.useContext(UserIdContext);
The provider is used as below:
const App = () => {
useEffect(() => {
....
});
return (
<UserIdProvider>
...contents of app...
</UserIdProvider>
);
};
export default App;
However, the useEffect() of < UserIdProvider /> is not run as the app is launched for the first time after being installed on a device, as there is no log on the console. After the app is closed/quit and relaunched, the console log a USER_ID found, instead of USER_ID generated.
Add 2nd argument as [] so it will render only once. Otherwise, it will render every time when any state will be updated.
const App = () => {
useEffect(() => {
....
},[]);
return (
<UserIdProvider>
...contents of app...
</UserIdProvider>
);
};
export default App;
In order to trigger useEffect when first run you should enter [] as prop like this:
useEffect(() => {
async function getOrInitUserId() {
try {
let temp = await AsyncStorage.getItem('USER_ID');
if (temp == null) {
temp = uuid.v4();
await AsyncStorage.setItem('USER_ID', uuid.v4());
console.log('USER_ID Generated: ' + temp);
} else {
console.log('USER_ID Found: ' + temp);
}
setUserId(temp);
} catch (error) {
console.error(error);
}
}
if (!userId) {
getOrInitUserId();
}
}, []);

React Native - How to update a value for entire application

What is the best way to find a value in the api request and make it available in the entire application? I need to get the total of unread messages and show it to the user in the application header. Every time you change pages, a new request is made to update the total of unread messages. I'm using a context like this:
/** #format */
import React, { createContext, useContext, useState, useEffect, useRef } from "react";
import AsyncStorage from "#react-native-async-storage/async-storage";
import api from "../services/api";
const BadgeContext = createContext();
export default function BadgeProvider({ children }) {
const [messageCount, setMessageCount] = useState(0);
const [userLogged, setUserLogged] = useState("");
async function getAuthUserFromStorage() {
try {
const dataFromStorage = await AsyncStorage.getItem("#ellot:authUserLogged");
const authUserLogged = JSON.parse(dataFromStorage);
setUserLogged(authUserLogged.user);
} catch (e) {
console.log("ERROR: ", e);
}
}
useEffect(() => {
getAuthUserFromStorage();
}, []);
useEffect(() => {
const getTotalMessageNotRead = async () => {
try {
const response = await api.get(`/messages/total-not-read/${userLogged.id}`);
setMessageCount(response.data.data);
console.log("messageCount context ", messageCount);
} catch (error) {
console.log("Message Not Read Error: ", error);
} finally {
console.log("finally message not reader context", messageCount);
}
};
getTotalMessageNotRead();
}, []);
async function resetCountMessage() {
setMessageCount(0);
}
const store = {
messageCount,
resetCountMessage,
};
return <BadgeContext.Provider value={store}>{children}</BadgeContext.Provider>;
}
export function useBadge() {
const context = useContext(BadgeContext);
const { messageCount, resetCountMessage } = context;
return { messageCount, resetCountMessage };
}

AsyncStorage doesn't return null at first start

I'm making a login using a JWT and deviceStorage, it's working okay but every time I start the app, and with JWT removed from deviceStorage, it start as if already logged in. The problem is that the get method in deviceStorage returns a promise, so I need to either make the get method return null if empty or have my program know if it's a string or a promise
APP.JS
import 'react-native-gesture-handler';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import React, { useState, useEffect } from 'react';
import Login from './src/Pages/Login';
import LoggedIn from './src/Pages/LoggedIn';
import deviceStorage from './src/Service/deviceStorage';
const App = () => {
const [JWT, setJWT] = useState(null);
useEffect(() => {
const checkJWT = () => {
setJWT(deviceStorage.getValue())
}
checkJWT()
}, []
);
const checkJWT = () =>{
if (!JWT || JWT === null || JWT === "") {
return <Login setJWT={setJWT} />
}else if (JWT){
return <LoggedIn JWT={JWT} setJWT={setJWT} />
}
}
return (
<SafeAreaProvider>
{checkJWT()}
</SafeAreaProvider>
)
}
export default App
DEVICESTORAGE
import AsyncStorage from '#react-native-async-storage/async-storage';
const key = 'currentUser';
const deviceStorage = {
async saveItem(value) {
try {
const jsonValue = JSON.stringify(value)
await AsyncStorage.setItem(key, jsonValue);
} catch (error) {
console.log('AsyncStorage Error: ' + error.message);
}
console.log('Done saving Value.')
},
async getValue() {
try {
return await AsyncStorage.getItem(key)
} catch(e) {
console.log('AsyncStorage Error: ' + e.message);
}
console.log('Done getting value.')
},
async removeValue() {
try {
await AsyncStorage.removeItem(key)
} catch(e) {
console.log('AsyncStorage Error: ' + e.message);
}
console.log('Done removing Value.')
}
};
export default deviceStorage;
I hope someone can help me with this
As you pointed out, the get method of deviceStorage returns a promise currently. I think you can do one of these:
If useEffect can accept an async method I think this should work:
useEffect(async () => {
const checkJWT = () => {
setJWT(await deviceStorage.getValue())
}
checkJWT()
}, []);
If not, then something like this should work:
useEffect(() => {
const checkJWT = () => {
deviceStorage.getValue().then(value => setJWT(value));
}
checkJWT()
}, []);
I haven't checked that anywhere if that works, but something along those lines should do the trick. You should make sure that you indeed put a JWT in your setJWT method and not a Promise. Or maybe you could change your setJWT method so it knows, that if it received a promise then you have to wait for the result.
Try out this function :
async getValue() {
try {
let value = null;
value = await AsyncStorage.getItem(key);
return value;
} catch(e) {
console.log('AsyncStorage Error: ' + e.message);
}
console.log('Done getting value.')
}

Error: Actions must be plain objects. Use custom middleware for async actions. (React Native)

I am trying to make it so that if an item called code is not set in state with redux, it is called from AsyncStorage and state is set.
import {AsyncStorage} from 'react-native'
import { connect } from 'react-redux';
import {bindActionCreators} from 'redux'
import {handlePhoneNumber, saveCode} from './../../actions/RegistrationActions';
class EnterPhoneNumberScreen extends React.Component {
componentDidMount() {
let code = this.props.registration.code;
console.log("code is", code);
if(code){
// Do nothing
}else{
console.log("in the else");
this.props.getAndSetCode();
}
}
}
const getAndSetCode = () => dispatch => {
console.log("in get and set Code");
AsyncStorage.getItem('code')
.then((data) => {
console.log("data is ", data);
dispatch(saveCode(data));
console.log("in getAndSetCode method, Code is ", data);
})
}
const mapDispatchToProps = dispatch => (
bindActionCreators({
handlePhoneNumber,
getAndSetCode: () => dispatch(getAndSetCode()),
}, dispatch)
);
export default connect(mapStateToProps, mapDispatchToProps)(EnterPhoneNumberScreen);
The console outputs the following:
LOG code is null
LOG in the else
LOG in get and set Code
LOG data is 3tgvgq
LOG in getAndSetCode method, Code is 3tgvgq
I know thunk is properly installed because it is running elsewhere in the application. saveCode is just a normal action:
export const saveCode = code => ({
type: "SAVE_CODE",
payload: code
})
And this error appears in the iphone11 simulator:
How do I fix this?

Promise isn't working in react component when testing component using jest

Good day. I have the following problem:
I have an item editor.
How it works: I push 'Add' button, fill some information, click 'Save' button.
_onSaveClicked function in my react component handles click event and call function from service, which sends params from edit form to server and return promise.
_onSaveClicked implements
.then(response => {
console.log('I\'m in then() block.');
console.log('response', response.data);
})
function and waits for promise result. It works in real situation.
I created fake service and placed it instead of real service.
Service's function contains:
return Promise.resolve({data: 'test response'});
As you can see fake service return resolved promise and .then() block should work immediatly. But .then() block never works.
Jest test:
jest.autoMockOff();
const React = require('react');
const ReactDOM = require('react-dom');
const TestUtils = require('react-addons-test-utils');
const expect = require('expect');
const TestService = require('./service/TestService ').default;
let testService = new TestService ();
describe('TestComponent', () => {
it('correct test component', () => {
//... some initial code here
let saveButton = TestUtils.findRenderedDOMComponentWithClass(editForm, 'btn-primary');
TestUtils.Simulate.click(saveButton);
// here I should see response in my console, but I don't
});
});
React component save function:
_onSaveClicked = (data) => {
this.context.testService.saveData(data)
.then(response => {
console.log('I\'m in then() block.');
console.log('response', response.data);
});
};
Service:
export default class TestService {
saveData = (data) => {
console.log('I\'m in services saveData function');
return Promise.resolve({data: data});
};
}
I see only "I'm in services saveData function" in my console.
How to make it works? I need to immitate server response.
Thank you for your time.
You can wrap your testing component in another one like:
class ContextInitContainer extends React.Component {
static childContextTypes = {
testService: React.PropTypes.object
};
getChildContext = () => {
return {
testService: {
saveData: (data) => {
return {
then: function(callback) {
return callback({
// here should be your response body object
})
}
}
}
}
};
};
render() {
return this.props.children;
}
}
then:
<ContextInitContainer>
<YourTestingComponent />
</ContextInitContainer>
So your promise will be executed immediately.