I have figured out how to pull the notifications from the database but having trouble creating a filter that allows the user that asked the question to only get notifications when their questions have been answered.
The code I wrote is getting an unmounted error on line 31 with how I'm performing the filter. Here is the error message:
Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
Here is the code:
import React, { useEffect, useState } from "react";
import { makeStyles } from "@material-ui/core/styles";
import Popper from "@material-ui/core/Popper";
import NotificationsIcon from "@material-ui/icons/Notifications";
import "../Style/Header.css";
import db, { auth } from "../firebase";
const useStyles = makeStyles((theme) => ({
paper: {
border: "1px solid",
padding: theme.spacing(1),
backgroundColor: theme.palette.background.paper,
zIndex: "10",
},
}));
export default function SimplePopper() {
const classes = useStyles();
const [anchorEl, setAnchorEl] = React.useState(null);
const [notifications, setNotifications] = useState([]);
const handleClick = (event) => {
setAnchorEl(anchorEl ? null : event.currentTarget);
};
const open = Boolean(anchorEl);
const id = open ? "simple-popper" : undefined;
useEffect(() => {
let mounted = true;
db.collection("notifications")
.where(auth.askerUserId, "==", auth.currentUser.uid)
.orderBy("timestamp", "desc")
.onSnapshot((snapshot) => {
if (mounted) {
setNotifications(
snapshot.docs.map((doc) => ({
id: doc.id,
content: doc.data().content,
}))
);
}
});
return () => (mounted = false);
}, []);
return (
<div className="header__icon">
<NotificationsIcon
aria-describedby={id}
type="button"
onClick={handleClick}
/>
<Popper id={id} open={open} anchorEl={anchorEl} style={{ zIndex: 100 }}>
<div className={classes.paper}>
<ul className="notifications">
{notifications.map((notification) => (
<li key={notification.id}>{notification.content}</li>
))}
</ul>
</div>
</Popper>
</div>
);
}
You should unsubscribe when the component gets unmounted:
import React, { useEffect, useState } from "react";
import { makeStyles } from "@material-ui/core/styles";
import Popper from "@material-ui/core/Popper";
import NotificationsIcon from "@material-ui/icons/Notifications";
import "../Style/Header.css";
import db, { auth } from "../firebase";
const useStyles = makeStyles((theme) => ({
paper: {
border: "1px solid",
padding: theme.spacing(1),
backgroundColor: theme.palette.background.paper,
zIndex: "10",
},
}));
export default function SimplePopper() {
const classes = useStyles();
const [anchorEl, setAnchorEl] = React.useState(null);
const [notifications, setNotifications] = useState([]);
const handleClick = (event) => {
setAnchorEl(anchorEl ? null : event.currentTarget);
};
const open = Boolean(anchorEl);
const id = open ? "simple-popper" : undefined;
useEffect(() => {
let mounted = true;
let unsub=db.collection("notifications")
.where(auth.askerUserId, "==", auth.currentUser.uid)
.orderBy("timestamp", "desc")
.onSnapshot((snapshot) => {
if (mounted) {
setNotifications(
snapshot.docs.map((doc) => ({
id: doc.id,
content: doc.data().content,
}))
);
}
});
return () => {unsub()};
}, []);
return (
<div className="header__icon">
<NotificationsIcon
aria-describedby={id}
type="button"
onClick={handleClick}
/>
<Popper id={id} open={open} anchorEl={anchorEl} style={{ zIndex: 100 }}>
<div className={classes.paper}>
<ul className="notifications">
{notifications.map((notification) => (
<li key={notification.id}>{notification.content}</li>
))}
</ul>
</div>
</Popper>
</div>
);
}