Upgrading from createContainer to withTracker

I upgraded meteor and I started receiving deprecation warning for createContainer(). As a result, I’ve tried to implement withTracker however now I’m getting Component(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.. I’m not sure what I’m missing here, can someone point out my error.

Path: App.jsx

import { Meteor } from 'meteor/meteor';
import React from 'react';
import PropTypes from 'prop-types';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { withTracker } from 'meteor/react-meteor-data';

// IsCandidate Spefic Routes
import TestContainer from '../../containers/candidate/TestContainer';

const App = appProps => (
  <Router>
    <ScrollToTop>
      <div className="bgColor">
        <NavBar {...appProps} />
        <Grid className="main-page-container">
          <Switch>
            {/* candidate routes */}
            <IsCandidate exact path="/candidate/testpage/:id" component={withTracker(TestContainer)} {...appProps} />

            {/* IsPublic routes */}
            <Route render={function () {
              return <p>Page not found</p>;
            }}
            />
          </Switch>
        </Grid>
      </div>
    </ScrollToTop>
  </Router>
);

App.propTypes = {
  loggingIn: PropTypes.bool,
  isCandidate: PropTypes.bool
};

export default withTracker(() => {
  const loggingIn = Meteor.loggingIn();
  return {
    loggingIn,
    isCandidate: !loggingIn && !!Meteor.userId() && !!Roles.userIsInRole(Meteor.userId(), 'isCandidate'),
  };
})(App);

Path: IsCandidate.jsx

import React from 'react';
import PropTypes from 'prop-types'; // ES6
import { Route, Redirect } from 'react-router-dom';

const IsCandidate = ({ loggingIn, isCandidate, component: Component, ...rest }) => (
  <Route
    {...rest}
    render={(props) => {
      if (loggingIn) return <div />;
      return isCandidate ?
      (<Component loggingIn={loggingIn} isCandidate={isCandidate} {...rest} {...props} />) :
      (<Redirect to="/login" />);
    }}
  />
);

IsCandidate.propTypes = {
  loggingIn: PropTypes.bool,
  isCandidate: PropTypes.bool,
  component: PropTypes.func
};

export default IsCandidate;

Path: Testcontainer.jsx

import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
import { Test } from '../../../api/test/test';

import TestPage from '../../pages/candidate/TestPage';

export default TestContainer = withTracker(({ match }) => {

  const testHandle = Meteor.subscribe('test', match.params.id);
  const loadingTest = !testHandle.ready();
  const testCollection = Test.findOne(match.params.id);
  const testExist = !loadingTest && !!testCollection;


return {
  loadingTest,
  testExist,
  testCollection: testExist ? testCollection : {}
};
  
}, TestPage);
1 Like

Hi - don’t have time to absorb the details of your example.

But, as an example, here is how I use withTracker and it works; you might want to take a similar approach.

/* PostListContainer.js */

import { withTracker } from ‘meteor/react-meteor-data’ ;
import { Postmssgs } from ‘/lib/Postmssgs.js’ ;
import PostList from ‘./PostList.jsx’ ;
import { Meteor } from ‘meteor/meteor’ ;

/* called by Comcon.jsx */

export default withTracker( props => {

// Postmssgs subscription in startup.jsx

let USERAVATARURL = null ;
USERAVATARURL = Meteor.users.findOne({ _id: Meteor.userId() },
{ “profile.image”: 1, _id: 0 }) ;

// limited to 20 on publish

let POSTS = null ;
POSTS = this.Postmssgs.find( {}, { sort: { daytecreated: -1 } } ).fetch() ;

return {
userAvatarURL: USERAVATARURL ? USERAVATARURL : [ ] ,
posts: POSTS ? POSTS : [ ] ,
}
})( PostList ) ;

1 Like

You’re not using it properly.

Wrong: withTracker(() => {}, Component);

Correct: withTracker(() => {})(Component);

4 Likes

Here you go!!!

export default TestContainer = withTracker(({ match }) => {

const testHandle = Meteor.subscribe(‘test’, match.params.id);
const loadingTest = !testHandle.ready();
const testCollection = Test.findOne(match.params.id);
const testExist = !loadingTest && !!testCollection;

return {
loadingTest,
testExist,
testCollection: testExist ? testCollection : {}
};

})(TestPage);

1 Like

Thanks. I’m still getting Component(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.

check SO https://stackoverflow.com/questions/46923697/upgrading-from-createcontainer-to-withtracker-with-react-router-v4/46926124#46926124

2 Likes

Thank you very much!

I thought I understood self executing functions, but I don’t. In this case which one is context (supposedly “this”) and which one is the parameter, since withTracker takes component as argument which I belief references the actual presentation component in source code, that begs the question how does that tracker data insert?

I guess whats happening is that it’s being curried.https://medium.com/@kbrainwave/currying-in-javascript-ce6da2d324fe

What happens with loadingTest and testExist in return scope, since they are returned without propnames? How are they retrieved afterwards? Are those values later accessible via …rest statement?