Can this.state be accessed in createContainer function?

Also your code doesn’t look quite right in general. You can look here for a good example of using createContainer:

@ciwolsey What do you think about passing ReactiveVar or ReactiveDict through createContainers {params} and on to return to the page along with other data fetched? Using the ReactiveVar or ReactiveDict variable fields as this.state was previously used in getMeteorData( ).

I have the same problem with using state of component in createContainer().
I think that we can use ReactivVar/Dic, if its in one file. But in application structure its is break (components, pages, container).

Yeah that works fine if you want to use Meteors reactivity as opposed to something like Redux. You can use whatever tracker based reactive data sources you like. It does go against the reason most people are using functional component in the first place though as you’re accessing state outside of the function.

1 Like

Thanks for replying. Very helpful. However I was not using functional stateless component, they are just regular react components. I have edited the question.

The best practices in React seem to be to not use state in this way anymore. If you want to use state to filter your collections, you either need to create an extra wrapper component or use the mixin approach. Another option is creating a global reactive var or a redux store to keep your client-only state.

Oh, it might not be obvious but what you’re passing in as the first argument to createContainer is actually a stateless component.

Ah, I got your point.
Thanks again

In what file would you create the global reactive var? Would the global var be better practice than passing it through container params like:

let myvar = new ReactiveVar("false");
<MyPageContainer params={{myvar:myvar}} />

The main point of a container is that it’s OK to access global data sources, like Meteor collections, in there, so that you can avoid accessing them in your pure components.

Using ReactiveVar or ReactiveDict in this way becomes pretty close to what Redux does, but saves you from having to set up a new container component. This should all go in a future Guide article about managing client-side data…

5 Likes

I’m in a similar situation; I’ve got a page where a user selects two related documents, for example let’s say a “Photo Category” and then a “Photo”.

Obviously, in my container for the route/page I could load the entire categories collection and the entire photos collection. But, loading all the photos is incredibly slow, and wasteful, as in reality the user will only want to select the photos from the category that they have selected.

Ideally, I would like to have been able to access the React component’s state in the container, to check the state.selectedCategory and use it to subscribe like Meteor.subscribe('photos-in-category', state.selectedCategory);.

Now I can see a few options for me here, I think there’s nothing stopping me in my React component using Session.set('selectedCategory', categoryId) and then in the container using Meteor.subscribe('photos-in-category', Session.get('selectedCategory'));, but I’m not entirely sure that this is a “good” way to do it…

Otherwise, I could use Redux/Flux but I’m not really ready to implement it application-wide, so I think it’s a step too far for now.

How would you recommend doing something like this @sashko?

1 Like

@Siyfion the option you suggested is perfect, and pretty similar to how you would do it with Flux. Alternatively, put a wrapper component with state, and pass that in as a prop to the container component. Perhaps someone should PR this to the guide article!

3 Likes

Awesome, glad to head that I was on the right track!

I agree - this is an EXCELLENT use case example @Siyfion and I think we should have examples of how to do it.

I’d lean toward trying to figure out the react+redux way of doing this, vs. the Meteor + Session way… (because we are learning React after all)

Any volunteers to do a quick demo?

2 Likes

In the case presented at the beginning of the thread, the subscription depends on the component’s state.
But what if the component’s state (including the initial state) depends on a collection’s contents (for example, user settings)? In this case - will the ‘wrapper component’ approach still be a good fit? It requires the container component to pass up data just for state initialization, which feels a bit weird, doesn’t it?

Here is the greate solution for that.
http://stackoverflow.com/questions/36434899/how-to-handle-meteor-data-that-requires-state-from-child-component?answertab=active#tab-top

1 Like

This is a really great solution, but I have problems with returning myValue from this. I haven’t got permissions to ask a question on Stackoverflow so wondered if anyone here could offer some help?

1 Like

What’s the problem? Can you show some examples?

Thanks @nazarposhta. I can get the myCount flowing through the props and into the pure component, but not myValue. I have tried all permutations

import React, {Component} from 'react';
import {createContainer} from 'meteor/react-meteor-data';
import Scenes from '../imports/collections/scene.js';


export default class StateHolder extends Component {
  constructor() {
    super();
    this.state = {myCount: 1};

  }

  incrementCount() {
    this.setState({myCount: this.state.myCount + 1});
  }

  decrementCount() {
    this.setState({myCount: this.state.myCount - 1});
  }

  render() {
    return <Container myCount={this.state.myCount} incClick={this.incrementCount.bind(this)} decClick={this.decrementCount.bind(this)}/>;

  }
}

// Child component, renders only
class PureComponent extends Component {
//console.log(myValue);

  render() {
    return (
    <div>
      Number {this.props.myCount}
      <button onClick={this.props.incClick}> Inc </button> <button onClick={this.props.decClick}> Dec </button>


    </div>
  );
  }
}

// Decorated child container.
// Make sure to use this one in parent component's render() function!
let Container = createContainer((props) => {
  console.log(props.myCount)
  let doc = Scenes.find({scene_no: props.myCount}).fetch;
  return {
    myValue: doc ? doc.someValue : null
  }
}, PureComponent);

let doc = Scenes.find({scene_no: props.myCount}).fetch;
let doc = Scenes.find({scene_no: props.myCount}).fetch();

2 Likes