useSWRMutation stale state in onSuccess callback expo - reactjs

When I console.log phoneNumber in onSuccess, it shows an empty string instead of the correct value. phoneNumber is stale. When I type something in my editor which makes the app rerender, phoneNumber has the correct value. It seems the function in onSuccess sort of gets captured by the closure for some reason? I'm not sure why.
const verifyPhoneNumber = async (url: string, { arg: phoneNumber }) => {
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
phone_number: phoneNumber,
}),
});
const json = await response.json();
return json;
} catch (e) {
console.log(e);
}
};
const SignUp = (props: Props) => {
const navigation = useNavigation();
const [phoneNumber, setPhoneNumber] = useState("");
const url = "https://jsonplaceholder.typicode.com/posts/";
// const url = "http://192.168.0.142:8000/user/auth/otp-code/";
const { trigger, isMutating } = useSWRMutation(url, verifyPhoneNumber, {
onSuccess: () => {
// The problem is here, phoneNumber is an empty string
console.log(phoneNumber)
navigation.navigate("VerifyCode", {
phoneNumber: phoneNumber,
});
},
onError: (error) => console.log(error),
});
const sendOTP = () => {
trigger(phoneNumber);
};
// phoneNumber shows the proper string
console.log(phoneNumber);
return (
<AppBox>
<InputFieldForAuth
inputIsInvalid={false}
value={phoneNumber}
onChangeText={(newText: string) => setPhoneNumber(newText)}
placeholder={`Enter your phone number here`}
/>
</HyperlinkText>
<Button
text="Continue"
onPress={sendOTP}
/>
</AppBox>
);
};
I even tried something like this to make the function get recreated with a new phoneNumber each render, and phoneNumber is still stale
const onSuccess = useCallback(() => {
console.log(phoneNumber);
navigation.navigate("VerifyCode", {
phoneNumber: phoneNumber,
});
}, [phoneNumber]);
const { trigger, isMutating } = useSWRMutation(url, verifyPhoneNumber, {
onSuccess: onSuccess,
onError: (error) => console.log(error),
});
A snack is attached at the bottom demonstrating the issue.
https://snack.expo.dev/#meg_hidey/uplifting-churros

Related

firebase react cloud messaging push notification [duplicate]

I was working on a project using Firebase cloud messaging react. I was sending this to my server, but it doesn't work. Surely I have tried, but I don't know what's wrong again.
Below is the code.
Here it sends a POST request to Firebase, and it should send a notification to the user.
async function sendNotification(id, userMessage) {
const headers = {
'Authorization': `key=${code}`,
'Content-Type': 'application/json'
}
const message = {
'to': `${id}`,
'content_available': true,
'apns_priority': 5,
'notification': {
body: `${userMessage}`
},
const url = 'https://fcm.googleapis.com/fcm/send'
//console.log(code)
await axios.post(url, message, {
headers: headers
})
}
const sendMessageToServer = async (e) => {
//e.preventDefault();
toggle()
const docRe = doc(database, "help", mailer);
const data = {
email: user.email,
user: newMessage,
}
//console.log(data, 'not clear')
setNewMessage('')
//console.log(data, newMessage, 'cleared')
setShow(false)
if(newMessage === '') {
}
else {
const docRef = doc(database, "users", mailer);
await updateDoc(docRe, {
msg: arrayUnion(data)
})
.then(() => {
async function p() {
const id = await getDoc(docRef)
//console.log(id.data())
sendNotification(id.data().notice, `Admin : ${data.user}`)
}
p()
})
}
Sometimes it sends to my localhost because I tested there, but it doesn't work on my Netlify app. Secondly, I noticed that it keeps generating the same token for each user, but that's not the issue, but if you can help in both I would be grateful.
export default function Dashboard() {
async function callToken() {
await getToken(messaging, {vapidKey: process.env.NOTIFICATION})
.then((code) => {
//console.log(code)
async function docRef() {
const dc = doc(database, "users", auth.currentUser.email);
await updateDoc(dc, {
notice: code
});
}
docRef()
})
}
async function requestPermission() {
await Notification.requestPermission()
.then((permission) => {
if (permission === 'granted') {
console.log('Notification permission granted.')
callToken()
}
})
}
const goTo = useNavigate();
useEffect(() => {
onAuthStateChanged(auth, (data) => {
if(!data) {
goTo('/login')
}
else {
currentBalance();
requestPermission()
}
})
})
}
Please know I imported all required modules.

HTTP put and get(id) request ReactQuery

I change the redux in my project to ReactQuery,and i got some problem with put req in my code.
this is my code
const { dispatch } = store;
const editClientDataAsync = async ( id,data ) => {
await axiosObj().put(`clients/${id}`,data);
}
const { mutateAsync: editClientData, isLoading } = useMutation(['editClientData'], editClientDataAsync, {
onSuccess: () => dispatch({ type: SUCCESS_DATA, payload: { message: "Success" } }),
onError: () => dispatch({ type: ERROR_DATA, payload: { message: "Error" } })
});
return { editClientData, isLoading }
}
same problem with when i try to get some req with id
const id = useSelector((state) => state?.clientData?.clientInfo?.data.id)
const getClientDetails = async ({ queryKey }) => {
const [_, { id }] = queryKey;
console.log(queryKey)
if (!id)
return;
const { data } = await axiosObj().get(`clients/${id}`)
console.log(data)
return data;
}
const { data: clientDetails, isLoading } = useQuery(['ClientId', { id }], getClientDetails)
return { clientDetails, isLoading }
Mutation functions only take 1 argument
Check where you use the editClientData mutation and pass the arguments in one object.
const editClientDataAsync = async ({ id, data }) => {
await axiosObj().put(`clients/${id}`,data);
}
return useMutation(['editClientData'], editClientDataAsync, ...);
Are you sure you get an id passed to the function?
You can disable the query until you get that id with the enabled option, so you don't make an unnecessary http call.
const id = useSelector((state) => state?.clientData?.clientInfo?.data.id)
const getClientDetails = async (id) => {
const { data } = await axiosObj().get(`clients/${id}`)
return data;
}
return useQuery(['client', id], () => getClientDetails(id), { enabled: !!id })
Disable/pausing queries

Socket works the for a few seconds then fails

I've managed to establish a connection using socket. It works great for the first few seconds after that it runs super slow takes like almost 2-3 mins to complete a request. And then it produces this error continuously. The app doesn't crash it just runs slowly with the error displaying countless times.
Firefox can’t establish a connection to the server at ws://localhost:5000/socket.io/?EIO=4&transport=websocket&sid=9S6kqHJdHHXQgrobAAHp..
Error on console.
Main.js
function Home(props) {
const [Username , setUsername] = useState("")
const [SearchedData, setSearchedData] = useState()
const [Data, setData] = useState()
const socket = io('http://localhost:5000')
React.useEffect(() => {
// socket.current = io('http://localhost:5000')
socket.emit("content","yada")
socket.on("get-data", data => {
setData(data)
})
})
function NavBar(props){
const handleClick = (e) => {
const {id} = e.target
if(id === "Post-btn"){
if(Content.length > 0){
let data = {
Username: "yada", Content
}
props.socket.emit("store-data", data)
}
}
return(
Tags....
)}
function Content (props) {
const onLike = (e) => {
const { id } = e.target.dataset
const data = {
username: "yada",
id : id
}
// console.log(data)
props.socket.emit("like", data)
}
return(
Tags.....
)
}
server.js
mongoose.connect(process.env.MongoDB,
{ useNewUrlParser: true, useUnifiedTopology: true }).then(() => {
console.log("Database Connected")
}).catch(err => {
console.log(err)
});
const server = app.listen(process.env.Port, () => {
console.log("Connected on " + process.env.Port)
})
const io = socket(server, {
cors:{
origin: "http://localhost:3000",
credential: true,
}
})
let cuid;
io.on("connection", (socket) => {
socket.on("content", username => {
Comments.find({},(err, data) => {
if(!err)
socket.emit("get-data", data)
})
})
socket.on("store-data", data => {
const {Username, Content} = data
const newdata = new Comments({
userName: Username,
content: Content,
createdAt: new Date().toDateString(),
replies: []
})
newdata.save().then(data => {
for(const d in data)
if(d === "_id"){
Users.findOneAndUpdate({username: Username}, {$push: {UserContent: data[d]}}, {new: true}, (err, save) => {
if(err)
console.log(err)
else
console.log(save)
})
}
})
})
socket.on("like", data => {
const {username, id} = data
Users.findOne({username:username}, (err, data) => {
if(!err){
cuid = data['id']
console.log(cuid)
Comments.findByIdAndUpdate(id, {$set: {score: data['_id']}}, {upsert: true}, (err, d) => {
if(!err){
console.log(d)
}
})
}
})
})
})
Looking at the code provided, I noticed there is an useEffect without params. This may be causing a loop until the application crashes.
React.useEffect(() => {
// socket.current = io('http://localhost:5000')
socket.emit("content","yada")
socket.on("get-data", data => {
setData(data)
})
socket.on("Updated", data => {
setData(data)
})
}, []); <- this is missing
This empty array indicates that the content inside the useEffect will only run once.
More about this https://reactjs.org/docs/hooks-intro.html

i am trying to store the photo in the database (firebase) and in the storage. the photo got store in the Storage, but not added in the firestore

I am trying to store the photo in the database (firebase) and in the storage simultaneously. The photo got store in the Storage, but not added in the firestore. Any Help Please ?
My code below shows what I did to perform the task. Oh it is in reactJs
function Preview() {
const cameraImage = useSelector(selectCameraImage);
const navigate = useNavigate();
const dispatch = useDispatch();
// if there is no Image taken then return to homepage (navigate('../'), {replace:
true})
useEffect(()=>{
if(!cameraImage){
navigate('../', {replace: true});
}
}, [navigate, cameraImage])
const closePreview = ()=>{
dispatch(resetCameraImage())
}
const sendPost = () =>{
const id = uuid();
const uploadTask = storage
.ref(`posts/ ${id}`)
.putString(cameraImage, "data_url");
uploadTask.on('state_changed', null, (error) =>{
//error function
console.log(error);
},
//COMPLETE function**this is the storage*## storage ##
()=>{storage
.ref('post')
.child(id)
.getDownloadURL()
.then((url) => {
db.collection('posts').add({
imageUrl: url,
username: 'yannick Simo',
read: false,
//profilePic
timestamp: firebase.firestore.FieldValue.serverTimestamp(),
});
navigate('./chats', {replace: true});
})
})
}
return (
<div className="preview" >
<div onClick={sendPost} className="preview__footer">
<h2>Send Now</h2>
<SendIcon fontSize="small" className="preview__sendIcon" />
</div>
</div>);
}
export default Preview;
You're calling getDownloadURL on a different ref than you're uploading the image to.
The upload goes to
const uploadTask = storage
.ref(`posts/ ${id}`)
.putString(cameraImage, "data_url");
And then you get the download URL like this:
storage
.ref('post')
.child(id)
.getDownloadURL()
Note posts (plural) vs post (singular) in these references.
To prevent such mistakes, I recommend creating a single variable to hold the reference, and then using that everywhere:
const sendPost = () => {
const id = uuid();
const uploadRef = storage.ref(`posts/ ${id}`); // 👈
const uploadTask = ref.putString(cameraImage, "data_url"); // 👈
uploadTask.on('state_changed', null, (error) => {
//error function
console.log(error);
},
() => {
ref.getDownloadURL() // 👈
.then((url) => {
db.collection('posts').add({
imageUrl: url,
username: 'yannick Simo',
read: false,
//profilePic
timestamp: firebase.firestore.FieldValue.serverTimestamp(),
});
navigate('./chats', {
replace: true
});
})
})
}
As a final improvement, since you're not doing anything in the other callbacks, you can also just the fact that uploadTask is a promise itself, and use then() on it, to shorten the code and make it a bit more idiomatic:
const sendPost = () => {
const id = uuid();
const uploadRef = storage.ref(`posts/ ${id}`);
const uploadTask = ref.putString(cameraImage, "data_url");
uploadTask.then(() => { // 👈
ref.getDownloadURL()
.then((url) => {
db.collection('posts').add({
imageUrl: url,
username: 'yannick Simo',
read: false,
//profilePic
timestamp: firebase.firestore.FieldValue.serverTimestamp(),
});
navigate('./chats', {
replace: true
});
})
}).catch((error) => console.error(error)); // 👈
}
If you then use async/await of modern JavaScript, you can turn this into the even simpler to read:
const sendPost = () => async { // 👈
const id = uuid();
const uploadRef = storage.ref(`posts/ ${id}`);
await ref.putString(cameraImage, "data_url"); // 👈
const url = await ref.getDownloadURL() 👈
db.collection('posts').add({
imageUrl: url,
username: 'yannick Simo',
read: false,
//profilePic
timestamp: firebase.firestore.FieldValue.serverTimestamp(),
});
navigate('./chats', {
replace: true
});
}

How to make Async Await Function in React Native?

I want to create a function that is about uploading photo to Firebase Storage with react-native-fetch-blob. I'm using Redux and you can find action functions below:
My problem is that uploadImage function is not running like asynchronous. Firebase function is running before uploadImage, so application give me an error.
I think i can't make a asynchronous function. How can i fix it ?
uploadImage() function:
const uploadImage = async (imageSource, whereToUpload) => {
let imageURL = '';
const mime = 'image/jpg';
const { Blob } = RNFetchBlob.polyfill;
const { fs } = RNFetchBlob;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;
console.log('URI =>', imageSource.uri);
let imgUri = imageSource.uri;
let uploadBlob = null;
const imageRef = firebase.storage().ref(whereToUpload + '/' + imageSource.fileName);
const uploadUri = Platform.OS === 'ios' ? imgUri.replace('file://', '') : imgUri;
await fs.readFile(uploadUri, 'base64')
.then((data) => Blob.build(data, { type: `${mime};BASE64` }))
.then((blob) => {
uploadBlob = blob;
return imageRef.put(blob, { contentType: mime });
})
.then(() => {
uploadBlob.close();
// eslint-disable-next-line no-return-assign
return imageURL = imageRef.getDownloadURL();
})
.catch((error) => {
console.log(error);
});
return imageURL;
};
and the main action is:
export const addProjectGroup = (
myUser,
groupName,
groupDescription,
groupProfilePic,
) => dispatch => {
const groupProfileFinalPic = async () => {
let finalGroupPicture = { landscape: '' };
if (_.isEmpty(groupProfilePic.src)) {
await uploadImage(groupProfilePic, 'groupPictures').then((imageURL) => {
console.log('İŞLEM TAMAM!');
console.log('SELECTED IMAGE URL =>', imageURL);
finalGroupPicture.landscape = imageURL;
});
} else {
finalGroupPicture.landscape = groupProfilePic.src.landscape;
}
return finalGroupPicture;
};
console.log("final group profile pic =>", groupProfileFinalPic());
// Önce grubu yaratalım..
// eslint-disable-next-line prefer-destructuring
const key = firebase
.database()
.ref()
.child('groups')
.push().key;
firebase
.database()
.ref('/groups/' + key)
.set({
admin: {
email: myUser.email,
name: myUser.name,
uid: myUser.uid,
},
groupName,
groupDescription,
groupProfilePic: groupProfileFinalPic(),
projects: '',
})
.then(() => {
console.log('Groups oluşturuldu.');
})
.catch(e => {
Alert.alert('Hata', 'Beklenmedik bir hata meydana geldi.');
console.log(e.message);
});
dispatch({
type: ADD_PROJECT_GROUP,
});
};
You are not awaiting groupProfileFinalPic(). This should be done before creating the action you want to dispatch.
groupProfileFinalPic().then(groupProfilePic => {
return firebase
.database()
.ref("/groups/" + key)
.set({
admin: {
email: myUser.email,
name: myUser.name,
uid: myUser.uid
},
groupName,
groupDescription,
groupProfilePic,
projects: ""
})
.then(() => {
console.log("Groups oluşturuldu.");
})
.catch(e => {
Alert.alert("Hata", "Beklenmedik bir hata meydana geldi.");
console.log(e.message);
});
});
I have no clue what the last dispatch is for, you might want to do that in one of the callbacks. Your code is to verbose for an SO question, but I hope this helps anyways.
You are using both await and then on the same call. To use await, you can arrange it something like
const uploadImage = async (imageSource, whereToUpload) => {
...
try {
let data = await RNFS.fs.readFile(uploadUri, 'base64')
let uploadBlob = await Blob.build(data, { type: `${mime};BASE64` }))
...etc...
return finalResult
catch (e) {
// handle error
}
}

Resources