[Meteor/React] createContainer() gets one value, but when it gets to component as prop it changes?


#1

Hm, title basically sums it up.
I have a Navbar component and a NavbarContainer:

NavbarContainer

export default NavbarContainer = createContainer(() => {
  const signedIn = Meteor.user() !== null;
  console.log("signedIn: " + signedIn);
  console.log("createContainer() got called!");

  return {
    signedIn
  }
}, Navbar);

componentWillReceiveProps method

componentWillReceiveProps() {
    console.log("componentWillReceiveProps()");
    console.log("componentWillReceiveProps() update: " + this.props.signedIn);

    if (this.state.loggedIn != this.props.signedIn) {
      console.log("State changed, updating!");
      this.setState({
        loggedIn: this.props.signedIn
      });
    } else {
      console.log("State didn't change, no need to update!");
    }
}

In Navbar I have the classical “Login, Register”, and once user is logged in, it shows “Hi there”. Also once logged in, the dropdown menu changes and this time includes the Logout btn that is linked with this code:

Logout method

logout(event) {
    Meteor.logout();
    console.log("Logging out");
}

In all the other cases, this works. Ie, not signed in -> login -> it shows “Hi there”. I can close the site and open it again, it’ll still show “Hi there” etc, but the weird thing is once I’m logged in and I click logout, this is what happens in my output (for debugging purposes).

Console output

signedIn: false (this is the console.log() statement from the container above)
Navbar.jsx:234 createContainer() got called!
Navbar.jsx:60 componentWillReceiveProps() (this is a console.log() I simply put in componentWillReceiveProps())
Navbar.jsx:61 componentWillReceiveProps() update: true (this is the weird part! should be FALSE because the first line in output is “false”)

Conclusion?!

So what I’m seeing is:
Upon logging out, in the createContainer, the code is noticing that Meteor.user() is null (user signed out), but while it’s sending that info as props to Navbar component it suddenly changes into opposite value (user is still here)?

I’m really banging my head against the wall, because this thing -> value suddenly changing between the container and it getting received in the component - is something I haven’t run into yet. :confused:

EDIT: Once I manually refresh the page afterwards (after clicking logout), it correctly shows that I’m not logged in. If it helps.


#2

@eXtreme,

Not sure this will help, but I had a very similar problem with containers myself. So I had two components, a nav and the page component. I was using containers on both components - thinking really good encapsulation and everything.

It turns out that the containers are shared in the global space. So, in my case, a publication of a set into the nav was clashing with the publication of the same set into the page component. Each publish (in nav and page components) had its own filtering logic, but they merged in the global container space leading to both components being able to see a merged list.

If you have this problem, it might mean that you are publishing the list of logged in users to a different container than the nav bar, and in this code sure you do filtering, but the publication is still all users.

This may be the case if the code in the nav bar does only authentication and the other component does authorisation only or the opposite.

The solution was to filter both publications consistently for me. Just my 2c.

Thanks so much.

Tat


#3

@eXtreme

componentWillReceiveProps` is called before your this.props is changed (it will receive props after all). The new props are passed as the first parmeter:

componentWillReceiveProps(newProps) {
    console.log("componentWillReceiveProps()");
    console.log("componentWillReceiveProps() update: " + newProps.signedIn);

    if (this.state.loggedIn != newProps.signedIn) {
      console.log("State changed, updating!");
      this.setState({
        loggedIn: newProps.signedIn
      });
    } else {
      console.log("State didn't change, no need to update!");
    }
}

Frankly speaking, i never use componentWillReceiveProps or other life-cycle-methods. Just stateless-components whenever possible, because they are a lot lot easier to understand.

in your example you would keep the NavbarContainer as is, but the Navbar.jsx as statless:

// Navbar.jsx (don't know how it looks like, but it surly has somewhere an if (signedIn) ...:

export default ({signedIn}) => (
   <div>
      (...)
      { signedIn ? <LogoutButton /> : <LoginRegisterButton /> }
      (...)
  </div>

)

No need for state, highly expressive, easy to understand :wink:

Edit:

even if you want to go with statefull-components (class-components), you don’t need componentWillReceiveProps. At least in your example, because this.state.loggedIn is always the same as this.props.signedIn. In your render, you could use this.props.signedIn directly, similar to the stateless example above.

Remember that props and state behave the same, the only difference is that state can’t be set from outside and should reflect inner state that you want to keep during the lifetime of a component.


#4

Apologies for late reply, I’ve been buys with studying for university and the notification about latest reply that just arrived in my mail totally reminded me about this.

First off, @tathagatbanerjee thanks for the heads up. The detail you described wasn’t the problem in this case, but it’s good thing to be aware of going forward with the Meteor, React and using heavily createContainer(), so I’m really thankful for that!

Secondly, @macrozone, that indeed was a case. I figured it out shortly after I wrote this here and felt really stupid about forgetting to pass the parameter. But your reply makes me think on the whole stateless vs statefull components idea (which I’m sure I’ll find a lot of info on the web).

For example what I was doing is -> I’ve been keeping state in Navbar, things like “what is the current active page (for CSS, making navbar item active), is user logged in (for sign in/sign up), etc” and to achieve some of them I basically had to use container to send the props to Navbar, and then grab it inside the Navbar and basically update state with new value that just arrived, so to speak. Thinking more about it, I’m not sure whether I’m just adding more code to the Navbar component that way and polluting it more and more or if I’m doing something “smart” that way.

On the one hand, thinking about it in a way -> Well Navbar is keeping things of things relevant for its functionality seems definitely good, but like you described it could totally be possibly to just keep almost everything stateless and send a bunch of props to everything, again, not sure what’s better!

But I gotta admit that I secretly like the whole state thing that ReactJS bring into the web game. Sure, it’s more typing upfront but it makes me feel more like I’m developing GUI application in say C#/Java where every form/panel keeps track of their class variables (essentially ‘states’) and makes me think I’m separating the concerns of the entire application, although amount of code I’m adding in process would imply I’m just making everything harder to look at. :smile:


#5

i usually resolve the current route in the container and pass it to the component just as property.

With flowRouter, you can use FlowRouter.getRouteName() which returns the current route name.

Having the state out of our components also enables you to create your components outside of your application, e.g. in the excellent react-storybook https://github.com/kadirahq/react-storybook