【问题标题】:Where to set up notifications/status updates for app?在哪里为应用设置通知/状态更新?
【发布时间】:2019-07-23 14:43:56
【问题描述】:

我正在实施状态更新/通知,但我不知道从哪里拨打电话。现在我正在我的容器应用程序 AsyncApp.js 中制作它们,该应用程序包含所有导航栏和我的组件(除了登录/注销等......)问题是,当我去一个新组件时,我的通知从新开始,这是错误的,因为我希望它在所有页面中保持连续。

AsyncApp.js

class AsyncApp extends Component {

  constructor(props){
    super(props)

    this.startTimer = this.startTimer.bind(this)
    this.handleEvent = this.handleEvent.bind(this)
    this.handleClose = this.handleClose.bind(this)

    this.state = {
      redirect: false,
      maxSessionInactivity: null,
      showAlert: false,
      sinceLastCheck: ''
    }
  }

  async componentDidMount() {
    this.show = null
    let self = this
    let messages;
    const { dispatch } = this.props
    await document.body.addEventListener("keypress", this.handleEvent);
    await document.body.addEventListener("click", this.handleEvent);

    await fetch('/api/getStatus').then(res => res.json()).then(function(res){
      // if(!res.data.is_active){
      //   self.setState({redirect: true})
      // }
      console.log("IN GET STATUS ", res)
    })
    .catch(err => self.setState({redirect: true}))

    await fetch('/api/getFirstNotification')
    .then(res => res.json())
    .then(function(res){
      // if(res.status.errorOccured){
      //   self.setState({redirect: true})
      // }
      messages = res.data.messages
      dispatch(updateMessages(res.data.messages))
      self.setState({sinceLastCheck: res.data.since_last_check})
    })
    .catch(err => self.setState({redirect: true}))

    //await fetch('/api/getStatus').then(res => res.json()).then(res => this.setState({maxSessionInactivity: res.data.session_inactivity_minutes - 1 * 1000}));
    await this.startTimer()
    await console.log("STATE J", this.state)
    await this.interval(messages)
    await this.notifications()
  }

  startTimer() {
     this.firstTimer = setTimeout(function() {
        this.setState({showAlert: true})
     }.bind(this), 100000);
     this.lastTimer = setTimeout(function() {
        this.setState({redirect: true})
     }.bind(this), 600000)
  }

  handleEvent(e){
    console.log("event", e)
    clearTimeout(this.firstTimer)
    clearTimeout(this.lastTimer)
    this.startTimer()
  }

  async interval(messages){
    this.intervalStatus = await setInterval(async () => {
      await this.notify(messages)
    }, 15000)
  };

  async notifications(){

    const { dispatch } = this.props

    this.newNotifications = await setInterval( async () => {

      let data = { since_last_checked : this.state.sinceLastCheck }
      let res1 = await fetch('/api/getNotifications', {
        method:'POST',
        headers: {
          'Content-type': 'application/json',
          'accept': 'application/json'
        },
        body:JSON.stringify(data)
      })
      .then(res => res.json())
      .catch(err => console.log(err))

      console.log("NOTIFICATIONS NEXTTT", res1)

      if(res1 === undefined || res1.data === undefined || res1.data === null){
        this.setState({redirect: true})
      }

      if(res1 != undefined && res1.data != null) dispatch(updateMessages(res1.data.messages))

      let res2 = await fetch('/api/getStatus')
      .then(res => res.json())
      .catch(err => console.log(err))

      console.log("STATUSS", res2)

      if(res2 === undefined || res2.data === undefined || res2.data === null || res2.data.is_active === 'N' || res2.data.status === 'closed'){
        this.setState({redirect: true})
      }

    }, 5000)

  }

  handleClose(event){
   this.setState({showAlert: false})
  }

  componentWillUnmount(){
    console.log("componentWillUnmount!!!!")
    clearInterval(this.newNotifications)
    clearInterval(this.intervalStatus)
    clearTimeout(this.firstTimer)
    clearTimeout(this.lastTimer)
    document.body.removeEventListener("keypress", this.handleEvent);
    document.body.removeEventListener("click", this.handleEvent);
  }

  notify(arr){
    if(arr === undefined) return null

    if(typeof arr === 'string'){
      return toast.success(`${arr}`)
    }

    if(arr.length < 4){
      let messages = arr.map(message => toast.success(`${message.message_text}`))
      return messages
    } else {
      return toast.success(`You have ${arr.length} new Notifications!`)
    }
  };

  render() {
    const { classes } = this.props

    if (this.state.redirect) return <Redirect to="/logout" />

    return (
      <div>
        <ToastContainer />
        <Snackbar
          anchorOrigin={{
            vertical: 'bottom',
            horizontal: 'left',
          }}
          open={this.state.showAlert}
          autoHideDuration={6000}
          onClose={this.handleClose}
        >
          <MySnackbarContentWrapper
            onClose={this.handleClose}
            variant="warning"
            message="Your session will expire in one minute!"
          />
        </Snackbar>

        <ThemeProvider theme={theme}>
          <div className={classes.root}>
            <CssBaseline />
            <nav className={classes.drawer}>
              <Hidden xsDown implementation="css">
                <Navigator PaperProps={{ style: { width: drawerWidth } }} />
              </Hidden>
            </nav>
            <div className={classes.appContent}>
              <Header onDrawerToggle={this.handleDrawerToggle} />
              <main className={classes.mainContent}>
                <div>
                  <Switch>
                    <Route exact path="/EditContracts/:contractId/sections/:section" component={EditSection} />
                    <Route exact path="/EditContracts/:contractId" component={EditContract} />
                    <Route exact path="/EditUsers/:userId" component={EditUser} />
                    <Route exact path="/EditEndpoints/:epId" component={EditEndpoint} />
                    <Route exact path="/EditContracts/:contractId/addSection" component={CreateSection} />
                    <Route exact path="/Contracts/List" component={Contracts} />
                    <Route exact path="/Contracts/Create" component={CreateContract} />
                    <Route exact path="/Contracts/Import" component={ImportContract} />
                    <Route exact path="/Users/List" component={Users} />
                    <Route exact path="/Users/Create" component={CreateUser} />
                    <Route exact path="/Endpoints/Create" component={CreateEndpoint} />
                    <Route exact path="/Endpoints/List" component={Endpoints} />
                    <Route exact path="/Pug_Community" component={PugCommunity} />
                    <Redirect exact from="/Users" to="/Users/List" />
                    <Redirect exact from="/Endpoints" to="/Endpoints/List" />
                    <Redirect exact from="/Contracts" to="/Contracts/List" />
                  </Switch>
                </div>
              </main>
            </div>
          </div>
        </ThemeProvider>
      </div>
    )
  }
}

App.js

export default class App extends Component {
  render() {
    return (
      <Switch>
        <Route exact path="/signin" component={SignIn} />
        <Route exact path="/changePassword" component={ChangePassword} />
        <Route exact path="/logout" component={Logout} />
        <Redirect exact from="/" to="/signin" />
        <Route path="/" component={AsyncApp} />
      </Switch>
    )
  }
}

Root.js

const store = configureStore()

export default class Root extends Component {
  render() {
    return (
      <Provider store={store}>
        <Router>
         <App />
       </Router>
      </Provider>
    )
  }
}

【问题讨论】:

    标签: node.js reactjs express redux


    【解决方案1】:

    乍一看,您在 AsyncApp 中使用计时器所做的工作似乎比您需要的要多。我可以想到两种可能的解决方案来简化您的工作流程:

    1. 由于您已经在使用 redux,我会将您的 fetch() 调用完全从 AsyncApp 组件中取出,并使用 redux-thunk 将它们存储在 async redux action creators 中。

      • 在 AsyncApp 上只设置一个计时器,每隔一段时间调用你的异步操作方法,如 updateNotifications() 和 updateStatus(),然后检查返回的数据是否在 redux reducer 中是新的。
      • 然后您的 reducer 也可以成为确定通知是否已“读取”的地方。如果您可以控制服务器和数据库,您将需要一个服务器端属性来存储通知是否已被读取,以便总缓存刷新不会将旧通知重新显示为新通知。
    2. 虽然第一个建议对于您当前的应用程序设计可能更直观,但我认为更优雅的解决方案将使用某种 websocket 实现。使用socket.ioPusherJS 订阅套接字通道。实现这种更改也需要更改服务器,因此对您来说可能不可行,但它会消除对计时器的需求。例如,可以将 Pusher 配置为订阅通知通道,并在收到新通知时使用新数据启动 redux 操作。请参阅:pusher-redux

    以下是关于如何组织第一个建议的代码的一些想法:

    AsyncApp.js

    
    import { updateNotifications, updateStatus } from './actionCreators.js'
    
    
    class AsyncApp extends Component {
    
        // ... other necessary code     
    
        public interval: any;
    
        public getData = () => {
            const { dispatch } = this.props
            dispatch(updateNotifications())
            dispatch(updateStatus())
        }
    
        public componentWillMount(){
             //Grab data when the component loads the first time
            this.getData()
    
            // Then set a single interval timer to handle both data updates.
            this.interval = setInterval(()=>{
                this.getData()
            },30000)
    
        }
    
        public componentWillUnMount(){
            // clear the interval when the component unmounts.
            clearInterval(this.interval)
        }
    }
    

    actionCreators.js

    // action type constants
    
    export const RECEIVE_NOTIFICATIONS = 'RECEIVE_NOTIFICATIONS'
    
    function receiveNotifications(notifications){
        return({
            type: RECEIVE_NOTIFICATIONS,
            notifications,
        })
    }
    
    // Here's the part that uses thunk:
    export function updateNotifications() {
      return function(dispatch) {
        return fetch(`api/getNotifications`)
          .then(
            response => response.json(),
            error => console.log('An error occurred.', error)
          )
          .then(json =>
            dispatch(receiveNotifications(json))
          )
      }
    }
    
    // ... use the same pattern for updateStatus()
    
    

    reducers.js

    
    import { RECEIVE_NOTIFICATIONS } from './actionCreators.js'
    
    const notifications = (state=[], action) => {
        switch(action.type){
    
        case RECEIVE_NOTIFICATIONS:
            /* Depending on the structure of your notifications object, you can filter
               or loop through the list to determine whether or not any items in the 
               list are new.  This is also where you can toast if there are N number of 
               new notifications. */
            return action.notifications    
    
        default:
            return state
        }
    }
    
    

    【讨论】:

    • 感谢您的输入只是一个简单的问题,为什么要在运行之前添加 public。我以为那只能在 JAVA 中完成。我还没有看到它在 Javascript 中使用过。
    • 抱歉,我主要使用 Typescript 编写代码,现在“公众”已成为习惯。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-23
    • 2013-08-10
    • 2020-01-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多