State is undefined, when I setState() inside component did mount - javascript

I am new to react and unable to set state after fetching data from firebase in component did mount function ,keeps getting error contact is not defined inside the component did mount.
constructor(props) {
super(props);
this.state = {
contacts: [
{ name: 'sachin', number: '445343' },
{ name: 'nihil', number: '33335555' },
{ name: 'chicken', number: '434355' },
]
}
this.componentDidMount = this.componentDidMount.bind(this);
}
componentDidMount = () => {
let currentComponent = this;
fire.auth().onAuthStateChanged(function (user) {
if (user) {
console.log(user.uid)
var uid = user.uid;
db.collection(uid)
.get()
.then((querySnapshot) => {
const contacts = querySnapshot.forEach((doc) => {
const data = doc.data()
contacts.push(data)
})
console.log(contacts)
this.setState({contacts});
});
} else {
// No user is signed in.
}
});
}

Try this!
constructor(props) {
super(props);
this.state = {
contacts: [
{ name: 'sachin', number: '445343' },
{ name: 'nihil', number: '33335555' },
{ name: 'chicken', number: '434355' },
]
}
this.componentDidMount = this.componentDidMount.bind(this);
}
componentDidMount = () => {
let currentComponent = this;
fire.auth().onAuthStateChanged(function (user) {
if (user) {
console.log(user.uid)
var uid = user.uid;
db.collection(uid)
.get()
.then((querySnapshot) => {
const contacts = [];
querySnapshot.map(item => contacts .push(item.data));
this.setState({contacts});
});
} else {
// No user is signed in.
}
});
}

First of all you should not bind componentDidMount method in constructor
And second, you are pushing in contacts before initializing it. So Try something like below:-
componentDidMount() {
fire.auth().onAuthStateChanged((user) => {
if (user) {
console.log(user.uid)
var uid = user.uid;
db.collection(uid)
.get()
.then((querySnapshot) => {
const contacts = []; // initialize contants here
querySnapshot.forEach((doc) => {
contacts.push(doc.data())
})
console.log(contacts)
this.setState({contacts});
});
} else {
// No user is signed in.
}
});
}

componentDidMount() {
var user = fire.auth().currentUser;
if (user) {
console.log(user.uid)
var uid = user.uid;
} else {
// No user is signed in.
}
console.log('comp: ' + uid)
this.getData(uid);
}
getData = (e) => {
db.collection(e)
.get()
.then((querySnapshot) => {
const contacts = []; // initialize contants here
querySnapshot.forEach((doc) => {
contacts.push(doc.data())
})
console.log(contacts)
this.setState({contacts});
});
} ``` this worked I think putting the fire.auth().currentuser inside getData function did not let it set the state .

Related

Trying to figure out how to use socket.io the correct way in a useEffect that is using an axios get request to fetch messages

So far i'm stuck on my useEffect that fetches all the current messages and renders the state accordingly. as of right now it doesn't render the new state until page is refreshed.
const Home = ({ user, logout }) => {
const history = useHistory();
const socket = useContext(SocketContext);
const [conversations, setConversations] = useState([]);
const [activeConversation, setActiveConversation] = useState(null);
const classes = useStyles();
const [isLoggedIn, setIsLoggedIn] = useState(false);
const addSearchedUsers = (users) => {
const currentUsers = {};
// make table of current users so we can lookup faster
conversations.forEach((convo) => {
currentUsers[convo.otherUser.id] = true;
});
const newState = [...conversations];
users.forEach((user) => {
// only create a fake convo if we don't already have a convo with this user
if (!currentUsers[user.id]) {
let fakeConvo = { otherUser: user, messages: [] };
newState.push(fakeConvo);
}
});
setConversations(newState);
};
const clearSearchedUsers = () => {
setConversations((prev) => prev.filter((convo) => convo.id));
};
const saveMessage = async (body) => {
const { data } = await axios.post("/api/messages", body);
return data;
};
const sendMessage = (data, body) => {
socket.emit("new-message", {
message: data.message,
recipientId: body.recipientId,
sender: data.sender,
});
};
const postMessage = async (body) => {
try {
const data = await saveMessage(body);
if (!body.conversationId) {
addNewConvo(body.recipientId, data.message);
} else {
addMessageToConversation(data);
}
sendMessage(data, body);
} catch (error) {
console.error(error);
}
};
const addNewConvo = useCallback(
(recipientId, message) => {
conversations.forEach((convo) => {
if (convo.otherUser.id === recipientId) {
convo.messages.push(message);
convo.latestMessageText = message.text;
convo.id = message.conversationId;
}
});
setConversations(conversations);
},
[setConversations, conversations],
);
const addMessageToConversation = useCallback(
(data) => {
// if sender isn't null, that means the message needs to be put in a brand new convo
const { message, sender = null } = data;
if (sender !== null) {
const newConvo = {
id: message.conversationId,
otherUser: sender,
messages: [message],
};
newConvo.latestMessageText = message.text;
setConversations((prev) => [newConvo, ...prev]);
}
conversations.forEach((convo) => {
console.log('hi', message.conversationId)
if (convo.id === message.conversationId) {
const convoCopy = { ...convo }
convoCopy.messages.push(message);
convoCopy.latestMessageText = message.text;
console.log('convo', convoCopy)
} else {
return convo
}
});
setConversations(conversations);
},
[setConversations, conversations],
);
const setActiveChat = useCallback((username) => {
setActiveConversation(username);
}, []);
const addOnlineUser = useCallback((id) => {
setConversations((prev) =>
prev.map((convo) => {
if (convo.otherUser.id === id) {
const convoCopy = { ...convo };
convoCopy.otherUser = { ...convoCopy.otherUser, online: true };
return convoCopy;
} else {
return convo;
}
}),
);
}, []);
const removeOfflineUser = useCallback((id) => {
setConversations((prev) =>
prev.map((convo) => {
if (convo.otherUser.id === id) {
const convoCopy = { ...convo };
convoCopy.otherUser = { ...convoCopy.otherUser, online: false };
return convoCopy;
} else {
return convo;
}
}),
);
}, []);
// Lifecycle
useEffect(() => {
// Socket init
socket.on("add-online-user", addOnlineUser);
socket.on("remove-offline-user", removeOfflineUser);
socket.on("new-message", addMessageToConversation);
return () => {
// before the component is destroyed
// unbind all event handlers used in this component
socket.off("add-online-user", addOnlineUser);
socket.off("remove-offline-user", removeOfflineUser);
socket.off("new-message", addMessageToConversation);
};
}, [addMessageToConversation, addOnlineUser, removeOfflineUser, socket]);
useEffect(() => {
// when fetching, prevent redirect
if (user?.isFetching) return;
if (user && user.id) {
setIsLoggedIn(true);
} else {
// If we were previously logged in, redirect to login instead of register
if (isLoggedIn) history.push("/login");
else history.push("/register");
}
}, [user, history, isLoggedIn]);
useEffect(() => {
const fetchConversations = async () => {
try {
const { data } = await axios.get("/api/conversations");
setConversations(data);
} catch (error) {
console.error(error);
}
};
if (!user.isFetching) {
fetchConversations();
}
}, [user]);
const handleLogout = async () => {
if (user && user.id) {
await logout(user.id);
}
};
return (
<>
<Button onClick={handleLogout}>Logout</Button>
<Grid container component="main" className={classes.root}>
<CssBaseline />
<SidebarContainer
conversations={conversations}
user={user}
clearSearchedUsers={clearSearchedUsers}
addSearchedUsers={addSearchedUsers}
setActiveChat={setActiveChat}
/>
<ActiveChat
activeConversation={activeConversation}
conversations={conversations}
user={user}
postMessage={postMessage}
/>
</Grid>
</>
);
};
this is the main part im working on, the project had starter code when i began and was told not to touch the backend so i know its something wrong with the front end code. i feel like im missing something important for the socket.io
import { io } from 'socket.io-client';
import React from 'react';
export const socket = io(window.location.origin);
socket.on('connect', () => {
console.log('connected to server');
});
export const SocketContext = React.createContext();
this is how i have the socket.io setup, if anyone could point me in the right direction that would be cool. I have been reading up on socket.io as much as I can but am still pretty lost on it.
Based on the assumption the backend is working properly...
const addNewConvo = useCallback(
(recipientId, message) => {
conversations.forEach((convo) => {
if (convo.otherUser.id === recipientId) {
convo.messages.push(message);
convo.latestMessageText = message.text;
convo.id = message.conversationId;
}
});
setConversations(conversations);
},
[setConversations, conversations],
);
setConversations(conversations);
This is an incorrect way to set a state using the state's variable, and such it wont do anything. Likely why your code wont change until refresh.
Suggested fix:
const addNewConvo = useCallback(
(recipientId, message) => {
setConversations(previousState => previousState.map(convo => {
if (convo.otherUser.id === recipientId) {
convo.messages.push(message)
convo.latestMessageText = message.text;
convo.id = message.conversationId;
return convo
}
return convo
}))
},
[setConversations, conversations],
);
note: even above could be done more efficiently since I made a deep copy of messages

Firebase unsubscribe to onSnapshot is not working

So I'm trying to load multiple elements (RoomItem) and each RoomItem has an onSnapshot listener that listens to real-time changes. Now when I change my Workspace which loads a new set of RoomItems, the previous listeners don't unsubscribe and if there's any update in that RoomItem then react renders that list and not the one which should've been there coming from currentWorkspace.
const [roomLiveStatus, setRoomLiveStatus] = useState(false);
const [unsubscribe, setUnsubscribe] = useState(null);
const getRoomData = (currentWorkspace) => {
const {
roomData,
workspace,
allChannels,
setChannels,
index,
currentUser,
} = props;
const { workspaceId } = workspace;
const workspaceIdLowerCase = workspaceId.toLowerCase();
const { roomId } = roomData;
const roomIdLowerCase = roomId.toLowerCase();
const now = new Date().valueOf();
const query = firebase
.firestore()
.collection(`workspaces/${currentWorkspace.workspaceId}/rooms/${roomId}/messages`);
let unsub;
unsub = query.onSnapshot(
{
includeMetadataChanges: true,
},
function (doc) {
doc.docChanges().forEach((change) => {
if (change.type === "added") {
if (change.doc.data().timestamp >= now) {
console.log("message added ", change.doc.data());
let prevAllChannels = allChannels;
firebase
.firestore()
.collection(`workspaces/${currentWorkspace.workspaceId}/rooms/`)
.doc(`${roomId}`)
.get()
.then((doc) => {
if (doc.exists) {
console.log("updated room data", {
...doc.data(),
roomId: doc.id,
});
prevAllChannels.splice(index, 1, {
...doc.data(),
roomId: doc.id,
lastMessage: change.doc.data(),
});
if(currentWorkspace?.workspaceId === workspaceId) {
switchSort(prevAllChannels);
}
}
});
}
}
if (change.type === "modified") {
console.log("message modified: ", change.doc.data());
let prevAllChannels = allChannels;
firebase
.firestore()
.collection(`workspaces/${workspaceId}/rooms/`)
.doc(`${roomId}`)
.get()
.then((doc) => {
if (doc.exists) {
prevAllChannels.splice(index, 1, {
...doc.data(),
roomId: doc.id,
});
// console.log(prevAllChannels,"prevallchannels",prevSortType,"prevsorttype", props.sortType,"currentsorttype")
if(currentWorkspace?.workspaceId === workspaceId) {
switchSort(prevAllChannels, props.sortType);
}
}
});
}
if (change.type === "removed") {
console.log("message removed: ", change.doc.data());
}
});
}
);
setUnsubscribe(() => unsub);
}
useEffect(() => {
getRoomData(props.currentWorkspace);
}, []);
useEffect(() => {
getRoomData(props.currentWorkspace);
}, [props.sortType, props.currentWorkspace]);
const usePrevious = (value) => {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
const prevCurrentWorkspace = usePrevious(props.currentWorkspace)
useEffect(() => {
if(unsubscribe) {
unsubscribe();
setUnsubscribe(null);
}
},[props.workspace, props.currentWorkspace])
useEffect(() => {
return(() => {
if(unsubscribe)
unsubscribe()
setUnsubscribe(null);
})
},[])

why componentdidmount called two times

I have React Component in componentDidMount fetch data from the server. The issue is componentDidMount called twice also the API called twice. I have a view increment API like youtube video views increment twice in the database because of twice API calling.
class SingleVideoPlay extends React.Component {
constructor(props) {
super(props);
this.player = React.createRef();
}
state = {
autoPlay: true,
relatedVideos: [],
video: null,
user: null,
comments: [],
commentInput: {
value: '',
touch: false,
error: false
},
following: false,
tab: 'comments'
};
_Mounted = false;
componentDidMount() {
this._Mounted = true;
if (this._Mounted) {
const videoId = this.props.match.params.id;
this.getVideoDetails(videoId);
}
}
componentWillUnmount() {
this._Mounted = false;
try {
clearInterval(this.state.videoInterval);
this.props.videoEditUrl('');
} catch (error) {}
}
captureVideoTime = async () => {
const { video } = this.state;
const result = await updateWatchTime({
id: video._id,
time: 1
});
if (result.status === 200) {
const updateVideo = {
...video,
secondsWatched: video.secondsWatched + 1
};
this.setState({ video: updateVideo });
}
};
videoEnded = () => {
clearInterval(this.state.videoInterval);
};
videoPause = () => {
clearInterval(this.state.videoInterval);
};
loadVideo = () => {
clearInterval(this.state.videoInterval);
};
playingVideo = () => {
const interval = setInterval(this.captureVideoTime, 1000);
this.setState({ videoInterval: interval });
};
getVideoDetails = async (videoId) => {
const video = await getVideo(videoId);
if (video.status === 200) {
let response = video.data;
if (this.props.userId)
if (response.user._id === this.props.userId._id)
this.props.videoEditUrl(`/video/edit/${response.media._id}`);
this.setState({
relatedVideos: response.videos.docs,
video: response.media,
user: response.user
});
this.checkIsFollowing();
this.updateVideoStat(response.media._id);
}
};
updateVideoStat = async (id) => videoView(id);
checkIsFollowing = async () => {
const { userId } = this.props;
const { video } = this.state;
if (userId && video) {
const response = await isFollow({
follower: userId._id,
following: video._id
});
if (response) {
this.setState({ following: response.following });
}
}
};
addOrRemoveFollowing = async () => {
this.checkIsFollowing();
const { following, video } = this.state;
const { userId } = this.props;
if (userId) {
if (following) {
const response = await removeFollow({
follower: userId._id,
following: video._id
});
this.setState({ following: false });
} else {
const response = await addFollow({
follower: userId._id,
following: video._id
});
this.setState({ following: true });
}
}
};
submitCommentHandler = async (event) => {
const { userId } = this.props;
event.preventDefault();
if (userId) {
const result = await saveComment({
mediaId: this.state.video._id,
parentId: '0',
userID: userId._id,
userName: userId.username,
comment: this.state.commentInput.value
});
console.log(result);
if (result.status === 200) {
this.getVideoComments();
this.setState({ commentInput: { value: '', touch: false, error: false } });
}
}
};
render() {
const { autoPlay, relatedVideos, video, user, comments, commentInput, following, tab } = this.state;
const { userId } = this.props;
return (
<div className="container-fluid">
some coponents
</div>
);
}
}
const mapStateToProps = (state) => ({
userId: state.auth.user
});
export default connect(mapStateToProps, { videoEditUrl })(SingleVideoPlay);
I don't know why componentDidMount called two times alse it shows memmory lecage issue.
How to Fix it.
Multiple componentDidMount calls may be caused by using <React.StrictMode> around your component. After removing it double calls are gone.
This is intended behavior to help detect unexpected side effects. You can read more about it in the docs. It happens only in development environment, while in production componentDidMount is called only once even with <React.StrictMode>.
This was tested with React 18.1.0
I think the issue exists on the parent component that used SingleVideoPlay component. Probably that parent component caused SingleVideoPlay component rendered more than once.
Also, there is an issue on your code.
componentDidMount() {
this._Mounted = true;
if (this._Mounted) {
const videoId = this.props.match.params.id;
this.getVideoDetails(videoId);
}
}
Here, no need to check if this._Mounted, because it will always be true.
1.Install jQuery by
npm i jquery
import $ from 'jquery'
create your function or jwuery code after the export command or put at the end of the file

Repurpose Firebase's Facebook Login ID as a Data Key?

How can I utilize a user's Firebase ID ("id" : "1234567890") as a key for a separate data set within the same db?
the following would be my login detail where I would pull the ID from:
"users": {
"1234567890abcdefghijklmnopqrstuvwxyz" : {
"dp" : "https://www.profilePic.com",
"first_name" : "John",
"id" : "1234567890",
"last_name" : "Doe",
"token" : "abcdefghijklmnopqrstuvwxyz1234567890",
"uid" : "987654321"
}
}
Within Firebase Functions I currently have my code as:
admin.database().ref('location_config/{id}').set({current_location:[34.047220, -118.443534]})
The result currently comes out as:
"location_config": {
"{id}": {
"current_location": [34.047220, -118.443534]
}
}
But this is how I would like the data to appear with the ID being the key:
"location_config": {
"1234567890": {
"current_location": [34.047220, -118.443534]
}
}
The screenshot below illustrates how the UID is dynamic while the ID is constant.
Here is the code within Firebase:
let fbLocation;
module.exports = (event) => {
event.geoFire = functions.database.ref('users').onUpdate(event => {
admin.database().ref('/portal_coordinates_all').once('value', snapshot =>{
fbLocation = snapshot.val();
console.log ("snapshot", fbLocation);
}).then(() => {
// Create a Firebase reference where GeoFire will store its information
let firebaseRef = admin.database().ref('geofire');
// Create a GeoFire index
let geoFire = new GeoFire(firebaseRef);
geoFire.set(fbLocation)
.then(() => {
console.log("Provided key has been added to GeoFire");
}).catch(err => console.log(err))
.then(() => {
let geoQuery = geoFire.query({
center: [34.047220, -118.443534],
radius: 2
});
let locations = [];
let onKeyEnteredRegistration = geoQuery.on("key_entered", function(key, location, distance) {
locations.push(location);
});
// fires once when this query's initial state has been loaded from the server.
let onReadyRegistration = geoQuery.on("ready", function() {
console.log("GeoQuery has loaded and fired all other events for initial data");
console.log(locations);
// ******* here is where I'm having the issue *******
admin.database().ref( 'location_config/`${id}`' ).set( {current_location: locations} )
// **************************************************
// Cancel the "key_entered" callback
onKeyEnteredRegistration.cancel();
});
}).catch(err => console.log(err))
})
})
}
And here is the code within React Native:
import React, { Component } from 'react';
import {
StyleSheet,
View,
ActivityIndicator,
Button
} from 'react-native';
import firebase from 'firebase';
import { connect } from 'react-redux';
import { loginSuccess } from '../actions/AuthActions';
const FBSDK = require('react-native-fbsdk');
const { LoginManager, AccessToken } = FBSDK;
class Login extends Component {
constructor(props) {
super(props);
this.state = {
showSpinner: true,
};
}
componentDidMount() {
this.fireBaseListener = firebase.auth().onAuthStateChanged(auth => {
if (auth) {
this.firebaseRef = firebase.database().ref('users');
this.firebaseRef.child(auth.uid).on('value', snap => {
const user = snap.val();
if (user != null) {
this.firebaseRef.child(auth.uid).off('value');
this.props.loginSuccess(user);
}
});
} else {
this.setState({ showSpinner: false });
}
});
}
onPressLogin() {
this.setState({ showSpinner: true })
LoginManager.logInWithReadPermissions([
'public_profile',
'user_birthday',
'email',
'user_photos'
])
.then((result) => this.handleCallBack(result),
function(error) {
alert('Login fail with error: ' + error);
}
);
}
handleCallBack(result) {
let that = this;
if (result.isCancelled) {
alert('Login canceled');
} else {
AccessToken.getCurrentAccessToken().then(
(data) => {
const token = data.accessToken
fetch('https://graph.facebook.com/v2.8/me? fields=id,first_name,last_name&access_token=' + token)
.then((response) => response.json())
.then((json) => {
const imageSize = 120
const facebookID = json.id
const fbImage = `https://graph.facebook.com/${facebookID}/picture?height=${imageSize}`
this.authenticate(data.accessToken)
.then(function(result) {
const { uid } = result;
that.createUser(uid, json, token, fbImage)
});
})
.catch(function(err) {
console.log(err);
});
}
);
}
}
authenticate = (token) => {
const provider = firebase.auth.FacebookAuthProvider;
const credential = provider.credential(token);
return firebase.auth().signInWithCredential(credential);
}
createUser = (uid, userData, token, dp) => {
const defaults = {
uid,
token,
dp
};
firebase.database().ref('users').child(uid).update({ ...userData, ...defaults });
}
render() {
return (
this.state.showSpinner ? <View style={styles.container}><ActivityIndicator animating={this.state.showSpinner} /></View> :
<View style={styles.container}>
<Button
onPress={this.onPressLogin.bind(this)}
title="Login with Facebook"
color="#841584"
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
}
});
const mapStateToProps = (state) => {
console.log('mapStateToProps', state);
return {
logged: state.auth.loggedIn,
user: state.auth.user
};
};
export default connect(mapStateToProps, { loginSuccess })(Login);
After banging my head against the wall as to why I couldn't grab the Facebook ID, it turned out that there isn't a need to grab this ID since Firebase's UID stays constant. I was unaware that the Firebase ID didn't change because in my test environment I would always login resulting in a new UID being created.

Why isn't my pushKey being added to my User's post object?

When a user makes a post, I want to take the pushKey of the post and then add it in to the User object in order to store in a list of posts they have made.
I had assumed that my logic was correct but it seems not. Everything is console logging so far.
Here is the Create Post action:
export const createPostNoImage = (text, firstName, university, avatar) => {
const timeDate = new Date().getTime();
const { currentUser } = firebase.auth();
const { uid } = currentUser;
const anonAvatarKey = Math.floor(Math.random() * 10);
return (dispatch) => {
const pushKey = firebase.database().ref('/social/posts/').push().key;
const postObject = {
text,
comment_count: 0,
vote_count: 0,
author: {
uid,
anon_avatar_key: anonAvatarKey,
first_name: firstName,
photo_avatar: avatar,
university
},
created_at: timeDate,
};
firebase.database().ref(`/social/posts/${pushKey}`)
.update(postObject)
.then(() => {
postObject.uid = pushKey;
addPostIdToProfile(pushKey);
dispatch({ type: CREATE_POST, payload: postObject });
});
};
};
The above works just fine, I then call the addPostIdToProfile() function:
const addPostIdToProfile = (pushKey) => {
const { currentUser } = firebase.auth();
console.log(pushKey);
firebase.database().ref(`/social/users/${currentUser.uid}`)
.update((user) => {
if (!user.posts) {
user.posts = {};
}
user.posts[pushKey] = true;
return user;
});
};
It is this that is not updating in the database. Can someone please tell me why?

Categories

Resources