所以我试图在React中构建一个Notifications
组件。组件的状态包含一组通知,这些通知是对象。他们的关键之一是“看到”。您可能会猜到,看到的目的主要是视觉。每次用户单击通知时,我都会运行一个功能,该功能应该设置为在数据库(为了保持一致性)和在本地状态(对于UI)中看到的通知。
数据库部分工作得很好,但是由于某种原因,状态更改不起作用。当我放置一些console.log
时,很奇怪的是,我什至在调用this.setState
之前,将'seen'属性更改为1。我已经呆了几个小时了,我不知道发生了什么。
现在,一些代码:
import React, {Component} from 'react';
import {connect} from 'react-redux';
import classes from './Notifications.module.css';
import * as actions from '../../../store/actions';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import moment from 'moment';
import {Redirect} from 'react-router-dom';
class Notifications extends Component {
constructor(props) {
super(props);
// Set an interval to update notifications every 4 minutes.
this.update = setInterval(() => {
this.props.fetchNotifications(this.props.username)
}, 240000)
}
state = {
opened: false,
count: 0,
notifications: []
}
componentDidUpdate(prevProps, prevState) {
if (!prevProps.username && this.props.username) this.props.fetchNotifications(this.props.username);
if (!prevProps.notifications && this.props.notifications) {
this.setState({notifications: this.props.notifications, count: this.props.notifications.filter(not => !not.seen).length});
}
if (this.props.notifications) {
if (JSON.stringify(this.state.notifications) !== JSON.stringify(prevState.notifications)) {
this.setState({count: this.state.notifications.filter(not => !not.seen).length})
}
}
}
componentWillUnmount() {
// Clear the update interval
clearInterval(this.update);
}
redirect(model, model_id) {
switch (model) {
case 'sealant_customer':
return <Redirect to={`/profile/sealant-customer/${model_id}`} />;
case 'unapproved_business':
return <Redirect to={`/profile/unapproved-business/${model_id}`} />;
case 'business':
return <Redirect to={`/profile/business/${model_id}`} />;
case 'missed_call':
return <Redirect to={`/data/missed-calls`} />;
default: return null;
}
}
render() {
let content = (
<React.Fragment>
<div className={classes.svgWrapper}>
<p className={classes.counter} style={{opacity: this.state.count === 0 ? '0' : '1'}}>{this.state.count}</p>
<FontAwesomeIcon icon='bell' onClick={() => this.setState(prevState => ({opened: !prevState.opened}))} />
</div>
{this.state.opened && <div className={classes.notificationsWrapper}>
<ul className={classes.notificationsList}>
{this.state.notifications.length !== 0 ? Array.from(this.state.notifications).map(notifi => {
let icon;
switch (notifi.model) {
case 'sealant_customer':
case 'person':
icon = 'user';
break;
case 'business':
case 'unapproved_business':
icon = 'warehouse';
break;
default: icon = 'user';
}
let classArray = [classes.notification];
if (!notifi.seen) classArray.push(classes.unseen);
return (
<li key={notifi.id} className={classArray.join(' ')} onClick={ () => {
// If already seen, simply redirect on click.
if (notifi.seen) return this.redirect(notifi.model, notifi.model_id);
let newNotifications = [...this.state.notifications];
// If not seen, mark as seen in State & in Database.
let index = newNotifications.findIndex(not => notifi.id === not.id);
newNotifications[index].seen = 1;
this.setState({ notifications: newNotifications});
this.props.markSeen(notifi.id, this.props.username);
// Redirect.
return this.redirect(notifi.model, notifi.model_id);
}}>
<div className={classes.iconWrapper}>
<FontAwesomeIcon icon={icon} />
</div>
<div className={classes.textWrapper}>
<p className={classes.msg}>
{notifi.message}
</p>
<label className={classes.ago}>
{moment(notifi.date).fromNow()}
</label>
</div>
</li>
)
}) : <li className={classes.notification} style={{cursor: 'default'}}><p style={{whiteSpace: 'nowrap'}}>No notifications to show...</p></li>}
</ul>
</div>}
</React.Fragment>
)
return (
<div className={classes.wrapper}>
{content}
</div>
)
}
}
const mapStateToProps = state => {
return {
username: state.auth.username,
notifications: state.data.notifications
};
};
const mapDispatchToProps = dispatch => {
return {
fetchNotifications: username => dispatch(actions.fetchNotifications(username)),
markSeen: (id, username) => dispatch(actions.markSeen(id, username))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Notifications);
任何帮助将不胜感激。
答案 0 :(得分:0)
对于未来的读者-解决方案:
问题在于,当我呼叫let newNotifications = [...this.state.notifications];
行时,确实确实创建了一个通知副本,但是却在其中保留了原始notification
对象。
一旦我将行newNotifications[index].seen = 1
更改为newNotifications[index] = {...newNotifications[index], seen: 1}
,一切都会像魅力一样。