Meteor 1.3 + React + Tracker-React: How to change state


#1

Sorry, I’m really new to this… How come my state is not changing when I, let’s say, press ‘signout’ ? It should be simple for you guys.

import React, {Component} from 'react'
import TrackerReact from 'meteor/ultimatejs:tracker-react'
import update from 'react-addons-update';

import {SiteLogo} from '../components/icons/SiteLogo'
import {NavLink} from '../components/navbars/NavLink'
import {MsgVerify} from '../components/messages/MsgVerify'

export default class HeaderWrapper extends TrackerReact(Component) {
  constructor() {
    super()
    this.state = {
      subscription: {
        userData: Meteor.subscribe('userData')
      },
      render: {
        dashboard: false,
        msgVerify: false,
        signinSignup: false
      }
    }
  }

  componentDidMount() {
    this._getUserStatus()
  }

  componentWillUnmount() {
    this.state.subscription.userData.stop()
  }

  _user() {
    return Meteor.user()
  }

  _getUserStatus() {
    if(this._user()) {
      if(this._user().emails[0].verified) { // authorized
        const newState = update(this.state, {render: {dashboard: {$set: true}}})
        this.setState(newState)
      } else { // waiting
        const newState = update(this.state, {render: {msgVerify: {$set: true}}})
        this.setState(newState)
      }
    } else { // not authorized
      const newState = update(this.state, {render: {signinSignup: {$set: true}}})
      this.setState(newState)
    }
  }

  _signout() {
    Meteor.logout((error) => {
      if(error) {
        Bert.alert(error.reason, 'danger')
      } else {
        FlowRouter.go('/')
      }
    })
  }

  render() {
    return (
      <div>
        <nav className="navbar navbar-default">
          <div className="container-fluid">
            <div className="navbar-header">
              <button type="button" className="navbar-toggle collapsed"
                      data-toggle="collapse" data-target="#navbar"
                      aria-expanded="false">
                <i className="fa fa-chevron-down fa-fw"></i>
              </button>
              <a className="navbar-brand" href="#">
                <SiteLogo width={48} height={24} primaryColor={'#34495e'}
                          secondaryColor={'#e74c3c'} />
              </a>
            </div>

            <div className="collapse navbar-collapse" id="navbar">
              <ul className="nav navbar-nav">
                <NavLink link="#" text="getting started" className="nav-border-bottom" />
                <NavLink link="#" text="guide" className="nav-border-bottom" />
                <NavLink link="#" text="pricing" className="nav-border-bottom" />
                <NavLink link="#" text="community" className="nav-border-bottom" />
                <NavLink link="#" text="marketplace" className="nav-border-bottom" />
              </ul>

              
              {this.state.render.signinSignup ?
                <ul className="nav navbar-nav navbar-right">
                  <NavLink link="/signin" text="signin" className="nav-border-bottom" />
                  <NavLink link="/signup" text="signup" className="nav-border-bottom nav-signup" />
                </ul>
              : null}
                
              {this.state.render.dashboard ?
                <ul className="nav navbar-nav navbar-right">
                  <li className="dropdown">
                    <a href="#" className="dropdown-toggle" data-toggle="dropdown"
                       role="button" aria-haspopup="true" aria-expanded="false">
                       dashboard <span className="caret"></span>
                    </a>
                    <ul className="dropdown-menu">
                      <NavLink link="#" text="build" />
                      <NavLink link="#" text="check" />
                      <NavLink link="#" text="calculate" />
                      <NavLink link="#" text="compile" />
                      <li role="separator" className="divider"></li>
                      <NavLink link="#" text="settings" />
                      <NavLink link="#" text="signout" onClick={this._signout} />
                    </ul>
                  </li>
                </ul>
              : null}
                
            </div>
          </div>
        </nav>
        <div>
          {this.state.render.msgVerify ? <MsgVerify /> : null}
        </div>
      </div>
    )
  }
}

#2

And here’s the publication file:

Meteor.publish('userData', function() {
  if(this.userId) {
    return Meteor.users.find({_id: this.userId},
                             {fields: {'other': 1, 'things': 1}});
  }
});

#3

I “fixed” it… it works but it feels wrong… can someone verify?

import React, {Component} from 'react'
import TrackerReact from 'meteor/ultimatejs:tracker-react'
import update from 'react-addons-update';

import {SiteLogo} from '../components/icons/SiteLogo'
import {NavLink} from '../components/navbars/NavLink'
import {MsgVerify} from '../components/messages/MsgVerify'

export default class HeaderWrapper extends TrackerReact(Component) {
  constructor() {
    super()
    this.state = {
      subscription: {
        userData: Meteor.subscribe('userData')
      }
    }
  }

  componentWillUnmount() {
    this.state.subscription.userData.stop()
  }

  _user() {
    return Meteor.user()
  }

  _signout() {
    Meteor.logout((error) => {
      if(error) {
        Bert.alert(error.reason, 'danger')
      } else {
        FlowRouter.go('/')
      }
    })
  }

  _renderSignInUpOrOut() {
    if(!this._user()) {
      return (
        <ul className="nav navbar-nav navbar-right">
          <NavLink link="/signin" text="signin" className="nav-border-bottom" />
          <NavLink link="/signup" text="signup" className="nav-border-bottom nav-signup" />
        </ul>
      )
    } else {
      if(!this._user().emails[0].verified) {
        return (
          <ul className="nav navbar-nav navbar-right">
            <NavLink link="#" text="signout" className="nav-border-bottom"
                     onClick={this._signout} />
          </ul>
        )
      }
    }
  }

  _renderDashboard() {
    if(this._user()) {
      if(this._user().emails[0].verified) {
        return (
          <ul className="nav navbar-nav navbar-right">
            <li className="dropdown">
              <a href="#" className="dropdown-toggle" data-toggle="dropdown"
                 role="button" aria-haspopup="true" aria-expanded="false">
                 dashboard <span className="caret"></span>
              </a>
              <ul className="dropdown-menu">
                <NavLink link="#" text="build" />
                <NavLink link="#" text="check" />
                <NavLink link="#" text="calculate" />
                <NavLink link="#" text="compile" />
                <li role="separator" className="divider"></li>
                <NavLink link="#" text="settings" />
                <NavLink link="#" text="signout" onClick={this._signout} />
              </ul>
            </li>
          </ul>
        )
      }
    }
  }

  _renderMsgVerify() {
    if(this._user()) {
      if(!this._user().emails[0].verified) {
        return (
          <MsgVerify />
        )
      }
    }
  }

  render() {
    return (
      <div>
        <nav className="navbar navbar-default">
          <div className="container-fluid">
            <div className="navbar-header">
              <button type="button" className="navbar-toggle collapsed"
                      data-toggle="collapse" data-target="#navbar"
                      aria-expanded="false">
                <i className="fa fa-chevron-down fa-fw"></i>
              </button>
              <a className="navbar-brand" href="#">
                <SiteLogo width={48} height={24} primaryColor={'#34495e'}
                          secondaryColor={'#e74c3c'} />
              </a>
            </div>

            <div className="collapse navbar-collapse" id="navbar">
              <ul className="nav navbar-nav">
                <NavLink link="#" text="getting started" className="nav-border-bottom" />
                <NavLink link="#" text="guide" className="nav-border-bottom" />
                <NavLink link="#" text="pricing" className="nav-border-bottom" />
                <NavLink link="#" text="community" className="nav-border-bottom" />
                <NavLink link="#" text="marketplace" className="nav-border-bottom" />
              </ul>

              {this._renderSignInUpOrOut()}
                
              {this._renderDashboard()}
                
            </div>
          </div>
        </nav>
        <div>
          {this._renderMsgVerify()}
        </div>
      </div>
    )
  }
}

Edit: Found an error message from server, don’t know what it means.

I20160518-18:43:24.162(-7)? Exception from sub userData id vtd6998DzNvJEkHEd Error: Meteor.userId can only be invoked in method calls. Use this.userId in publish functions.
I20160518-18:43:24.162(-7)? at AccountsServer.userId (packages/accounts-base/accounts_server.js:82:13)
I20160518-18:43:24.162(-7)? at AccountsServer.user (packages/accounts-base/accounts_common.js:53:23)
I20160518-18:43:24.163(-7)? at Object.Meteor.user (packages/accounts-base/accounts_common.js:232:19)
I20160518-18:43:24.163(-7)? at Subscription.handler (imports/api/users/server/publications.js:3:19)
I20160518-18:43:24.163(-7)? at packages/check/match.js:107:1
I20160518-18:43:24.163(-7)? at [object Object].
.extend.withValue (packages/meteor/dynamics_nodejs.js:56:1)
I20160518-18:43:24.163(-7)? at Object.exports.Match._failIfArgumentsAreNotAllChecked (packages/check/match.js:106:1)
I20160518-18:43:24.163(-7)? at maybeAuditArgumentChecks (packages/ddp-server/livedata_server.js:1701:18)
I20160518-18:43:24.163(-7)? at Subscription._runHandler (packages/ddp-server/livedata_server.js:1026:17)
I20160518-18:43:24.163(-7)? at Session._startSubscription (packages/ddp-server/livedata_server.js:845:9)
I20160518-18:43:24.164(-7)? at Session.sub (packages/ddp-server/livedata_server.js:617:12)
I20160518-18:43:24.164(-7)? at packages/ddp-server/livedata_server.js:551:43

Edit: Fixed the error log by changing publish code back to what it was (posted above). I used Meteor.user() when I was trying to fix but apparently that is no good. Don’t even know why.