React Native - How to update a value for entire application - react-native

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 };
}

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 add items to the set state of useState for getting socket notifications?

I need a function for getting notification from socket (TypeScript) .
For instance ,
when a user click the "Like",the receiver will receive a notice like "You have receive a like from XXX",and I am able to get this message from the below code ,however ,I am not sure how to save those notifications into a list in order to display all the notices ..Could you please take a look how to do it ? Thank you so much in advance !!
I have putted the socket in the useContext :
import React from 'react';
import socketio from 'socket.io-client';
export const socket = socketio.connect(SOCKET_URL);
export const SocketContext = React.createContext();
When I send click a like, the receiver can receive my notification, and then the remark in the below codes,I can't get the notification list :
import {SocketContext} from '../../auth/context';
import React, {useEffect, useState, useContext, useLayoutEffect} from 'react';
const Home = () =>{
const {socket, user} = useContext(SocketContext);
const [notificationCount, setNotificationCount] = useState([]);
const [me, setMe] = useState({});
// init data
const initialData = async () => {
try {
const meResult = await fetchGetMe();
setMe(meResult?.data.data);
} catch (error) {
console.log('initial data get errors:', error);
}
};
useLayoutEffect(() => {
initialData();
}, []);
//get feedback from socket
useEffect(() => {
socket.on('getNotification', data => {
setNotificationCount(pre=> [...pre, data]); //I got the problem here..Can't get the list
console.log('notification data :', notificationCount);
});
return () => {
socket.off('getNotification');
};
}, [socket]);
const onPressLike = ()=>{
socket.emit('sendNotification', {
senderUserId: me?.userId,
senderName: me?.userName,
receiverUserId: 123456,
type: 0, // 0:like 1.gifts 2.sunflower
});
}
<Button onClick={onPressLike}>Like</Button>
}
3.My socket config in the server part :
let onlineUsers = [];
const addUsers = (userId, socketId) => {
!onlineUsers.some((m) => m.userId !== userId) &&
onlineUsers.push({ userId, socketId });
};
const removeUser = (socketId) => {
onlineUsers = onlineUsers.filter((user) => user.socketId !== socketId);
};
const getUser = (receiverId) => {
return onlineUsers.find((m) => m.userId === receiverId);
};
io.on("connection", (socket) => {
console.log("connect now");
socket.on("newUser", (userId) => {
addUsers(userId, socket.id);
console.log("onlineUsers:", onlineUsers);
});
socket.on(
"sendNotification",
({ senderUserId, senderName, receiverUserId, type }) => {
console.log(
`senderName:${senderName},receiverID:${receiverUserId};type:${type},socketId:${socket.id};senderUserId:${senderUserId}`
);
console.log("sendNotification,onlineUsers:", onlineUsers);
let receiver = {};
if (onlineUsers.length > 0) {
receiver = getUser(senderUserId);
console.log("receiver:", receiver);
io.to(receiver.socketId).emit("getNotification", {
senderName,
type,
});
} else {
receiver = null;
console.log("receiver:", receiver);
socket.emit("getNotification", { receiver });
}
}
);
socket.on("disconnect", () => {
console.log("disconnect");
});
});

observable and computed not being reflected in a functional component

I am learning mobx for react-native and not able to see changes to done to observables or computed.
Basically, I want to listen to changes to observable from the component.
My store is simple:
import { observable, action, computed } from 'mobx';
import AsyncStorage from '#react-native-async-storage/async-storage';
class ConfigStore {
rootStore = undefined;
#observable activeConfig = {group: 'starter', TC: false};
constructor(rootStore) {
this.rootStore = rootStore;
}
#computed get termsLoaded(){
return this.activeConfig.TC;
}
#action async loadPreviousConfig() {
const configDetails = { group: 'starter', TC: false};
try {
const response = await AsyncStorage.multiGet([
'group',
'TC'
]);
configDetails.group = response[0][1] || 'starter';
configDetails.TC = response[1][1] === undefined ? false : true;
console.log(configDetails);// shows correct previously saved config
this.activeConfig = configDetails;
} catch (error) {}
}
}
export default ConfigStore;
From my component, I want to load first previous configuration settings and have them reflect in my app. Basically, I want to check the value of TC ater calling loadPreviousConfig, they return false still:
import { inject, observer } from 'mobx-react';
const ConfigComponent = (props) => {
const { store } = props;
const { termsLoaded, activeConfig, loadPreviousConfig } = store.configStore;
useEffect(() => {
const init = async () => {
await loadPreviousConfig();
console.log(termsLoaded); //always false even though console from the store shows it is true.
};
init();
}, []); //tried [props]
return (
<View>
<Text>{activeConfig.group}</Text> //never changes
</View>
);
};
export default inject('store')(observer(ConfigComponent));

Whats the correct way to invoke a registered task?

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 ?

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.')
}