我有这个DashboardNavbar
组件,其中有两个主要状态,notifications
存储所有用户通知,current
存储当前选择的通知。
form这个组件,当openNotificationBox
是true
时,它显示NotificationBox
,当current
不是null
时,它显示ConversationBox
(因为每个通知都有一个messages
数组,我想显示它,所以我把current
传递给ConversationBox
,并在那里显示对话)
dashboard.js:
import NotificationBox from "./notification-box/notification-box";
import ConversationBox from "./notification-box/conversation-box";
export const DashboardNavbar = (props) => {
const socket = useContext(WebsocketContext);
const [notifications, setNotifications] = useState([]);
const [openNotificationBox, setOpenNotificationBox] = useState(false);
const [current, setCurrent] = useState(null); // current notification
console.log('dashboard is rerendering...')
console.log(current);
const handleSetCurrentNotification = (notification) => {
setCurrent(notification);
};
const handleCloseNotificationBox = () => {
setOpenNotificationBox(false);
};
const desactiveNotification = (id) => {
// CREATE 'arr' then
setNotifications(arr);
};
const isNotificationForCurrentUser = (payload) => {
// return TRUE or FALSE
};
const isNotificationIsForCurrentNotification = (payload) => {
// return TRUE or FALSE
};
const sortNotificationsByLastMsg = (array) => {
// return a sorted array
};
async function getUpdatedNotification(id) { // I use this function each time the current notification is updated
// API CALL to get the updated notification then
setCurrent(res.data);
}
const getNotificationsForCurrentUserAndOrderThem = () => {
//API CALL and then
setNotifications(sortNotificationsByLastMsg(res.data));
};
useEffect(() => {
getNotificationsForCurrentUserAndOrderThem();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
socket.on("newNotification", (payload) => {
console.log("newNotification event received !");
if (isNotificationForCurrentUser(payload)) getNotificationsForCurrentUserAndOrderThem(); // This may update "notifications"
});
socket.on("updateConversation", (payload) => {
console.log("updateConversation event received !");
console.log(current); // output current
if (isNotificationForCurrentUser(payload)) getNotificationsForCurrentUserAndOrderThem(); // This may update "notifications"
if (current) { // if current is not null
if (isNotificationIsForCurrentNotification(payload)) getUpdatedNotification(current._id) // this may update "current"
}
});
return () => {
console.log("Unregistering Events...");
socket.off("connect");
socket.off("newNotification");
socket.off("updateConversation");
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<>
{/* ... */}
{
openNotificationBox &&
<NotificationBox
notifications={notifications}
desactivenotification={desactiveNotification}
handleclosenotificationbox={handleCloseNotificationBox}
getupdatednotification={getUpdatedNotification} /*to run getUpdatedNotification and update "current" from the db */
/>
}
{current && (
<ConversationBox
current={current}
getupdatednotification={getUpdatedNotification}
handlesetcurrentnotification={handleSetCurrentNotification}
/>
)}
</>
);
};
notifications-box.js:
const NotificationBox = ({
notifications,
getupdatednotification,
desactivenotification,
handleclosenotificationbox,
}) => {
async function desactiveNotificationAndSelectId(notification, dto) {
// API CALL to update the notification
handleclosenotificationbox(); // this will set "openNotification" false
desactivenotification(notification._id); // this will update "notifications" in dashboard
getupdatednotification(notification._id); // this will update "current" in dashboard from db
}
return (
<div className={classes.notifiBox} id="box">
{notifications.map((notification) => {
//...
return (
<div
key={notification._id}
onClick={() => {
if (conditionOne) {
if (conditionTwo) {
desactiveNotificationAndSelectId(notification, {
status: "NONE",
});
} else {
handleclosenotificationbox();
getupdatednotification(notification._id); // this will update "current" in dashboard from db
}
} else {
handleclosenotificationbox();
getupdatednotification(notification._id); // this will update "current" in dashboard from db
}
}}
>
<img src={image} alt="img" />
<div className={classes.text}>
<h4>{notification.title}</h4>
<p>{notification.messages[notification.messages.length - 1].content}</p>
</div>
</div>
);
})}
</div>
);
};
export default NotificationBox;
conversation-box.js:
const ConversationBox = ({ current, handlesetcurrentnotification }) => {
const socket = useContext(WebsocketContext);
async function sendMessage(content) {
// API CALL TO to UPDATE the notification in the db with the new message then
handlesetcurrentnotification(res.data); // This will update "current" in the dashboard from the response, this endpoint is returning the updated notification, so there is no need to call getUpdatedNotification
socket.emit("newMessage", payload); // emit an event
}
return (
<div >
{/* navtop */}
<div >
<div
onClick={() => {
handlesetcurrentnotification(null); // This will set "current" to null so close the conversation component
}}
>
<img src="left-chevron.svg" />
<p>Close</p>
</div>
{/* ... */}
</div>
{/* conversation */}
<div >
<div >
{current && (
<>
{current.messages.map((message, index) => {
//...
return (
<div key={index}>
<img src="avatar2.jpg" />
<p>{message.content}</p>
</div>
);
})}
</>
)}
</div>
</div>
{/* chatForm */}
<div className={classes.chatForm} onSubmit={handleSubmit}>
<div >
<div ></div>
<div>
<textarea
placeholder="Enter your message here"
></textarea>
</div>
<button
onClick={() => {
sendMessage("test");
}}
>
<img src="send.svg" />
</button>
</div>
</div>
</div>
);
};
export default ConversationBox;
这工作正常,问题出在useEffect
内部的部分,当 Jmeter 板组件监听事件。
尤其是这里:
socket.on("updateConversation", (payload) => {
console.log("updateConversation event received !");
console.log(current); // THIS LINE
if (isNotificationForCurrentUser(payload)) getNotificationsForCurrentUserAndOrderThem(); // this works fine
if (current) { // if current is not null
if (isNotificationIsForCurrentNotification(payload)) getUpdatedNotification(current._id)
}
});
我用两个不同的浏览器尝试了一个场景,比如说两个不同的用户。
经过调试找到导致问题的原因后,似乎“有时”即使current
在接收事件时是null
,current
也会从null
传递到其先前的值,而viseversa有时会从对象传递到null
。
我不断切换openNotificationBox
来重新呈现组件,看看这行输出了什么:
console.log('dashboard is rerendering...')
console.log(current); // <--- output null
那么当接收到一个事件时,useEffect
里面这行:
socket.on("updateConversation", (payload) => {
console.log("updateConversation event received !");
console.log(current); // <-- output a notification object
所以这段代码运行:
if (current) { // if current is not null
if (isNotificationIsForCurrentNotification(payload)) getUpdatedNotification(current._id)
}
我不明白这是怎么可能的,为什么current
在这里改变了值!.
这可能发生在输出current
之前
if (isNotificationForCurrentUser(payload)) getNotificationsForCurrentUserAndOrderThem();
但这根本不影响current
。
我的意思是有时是当我重新加载应用程序并重新安装 Jmeter 板组件并开始侦听/等待事件时,这不会发生,这很奇怪,它只发生在current
设置为某个通知对象然后返回null
时。
感谢您的关注。
1条答案
按热度按时间mspsb9vt1#
在大多数情况下,我们在JSX中使用事件处理程序,如
onClick
,onChange
...所以每次组件重新渲染时,这些函数都会被重新创建,因此我们不会面临这种问题,这里据我所知,“updateConversation”的事件处理函数根本不会被重新创建,因为它在组件首次安装时被设置为事件,所以当current
是null
时,任何状态或 prop 更改都不会被跟踪,这就是原因。我发现,每次
current
更新时,我都需要重新创建这些事件处理程序,方法是将其包含在useEffect
的依赖数组中,以使后者在current
每次获得新值时运行,现在当事件被触发时,我可以从事件处理程序函数访问current
获得的最新值,即当前渲染期间的值:我把这个作为一个解决方案发布,找不到更好的解决方案,但仍然不相信组件应该取消订阅/订阅
setCurrent
触发的每个组件渲染上的事件。