Meteor 1.3 + react


#1

Hi all,

since my previous topic (Meteor 1.3 + react + reactRouter best practices) didn’t get any answer I figured out that I tried to ask too many things at once so I’ll try to change approach and ask very specific questions.

Use case: I need 2 react components that work on the same collection, let’s call it ‘houses’, and one component needs all the houses in the collection and the second component only need one house with a specific ID.

At the moment my solution is this (ALL CODE IS PSEUDOCODE)

Solution 1
Component1.jsx

const Component1 = (props) => ({props.loading ? “loading” : "doSomethingWithProps.houses};

export default createContainer((props) => {
const houses = Houses.find({}).fetch();
const loading = !houses;
return {houses, loading};
}, Component1);

Component2.jsx = same code as Component1.jsx, but with Houses.findOne(houseId); instead.

This means that for each component I need to create a container even if the data it needs has already been fetched somewhere else. And each component needs to handle its own loading separetely , potentially duplicating the code multiple times.

Ideally I would like to do something like this:

Solution 2
ParentContainer.jsx

export default createContainer((props) => {
const houses = Houses.find({}).fetch();
return {houses};
}, children); //ideally the children should be rendered only when houses is ready. And they should get all the objects returned above as props

Component1.jsx
const Component1 = (props) => ({doSomething with props.houses});

Component2.jsx
const Component2 = (props) => ({doSomething with props.houses.find(houseId)});

where ParentContainer is a container higher in the router hierarchy, and Component1&2 are two different children.

I can actually do something close to that like this:

Solution 3
ParentContainer.jsx

const ParentContainer = (props) => {
const clonedChildren = props.children && React.cloneElement(props.children, {key: props.location.pathname, houses: props.houses});
return ({props.loading ? “loading” : clonedChildren});

export default createContainer((props) => {
const houses = Houses.find({}).fetch();
const loading = !houses;
return {houses, loading};
}, ParentContainer);

Component1 and 2 as before.

But this solution has disadvanteges:

  • I need to create a component only to clone the children with the houses props
  • I need to clone the children in the first place, and to me it feels a bit ‘dirty’
  • Component 2 needs to filter manually the houses to find the one with the correct houseId

I also tried something similar without cloning and without passing the collection through props, but importing it in the children:

Solution 4
ParentContainer.jsx

const ParentContainer = (props) => ({props.loading ? “loading” : props.children});

export default createContainer((props) => {
const houses = Houses.find({}).fetch();
const loading = !houses;
return {houses, loading};
}, ParentContainer);

Component1.jsx
Import {Houses} from 'path-to-houses.js’
const Component1 = () => {
const houses = Houses.find({}).fetch();
return ({doSomething with houses});
}

Component2.jsx
Import {Houses} from 'path-to-houses.js’
const Component2 = () => {
const houses = Houses.findOne({houseId});
return ({doSomething with house});
}

But this solution has also disadvantages:

  • Component1 and 2 rely on the fact that ParentContainer is loaded before in order to always have the right data in the Houses collection. If they are loaded separetely from ParentContainer they won’t work properly
  • The data in component 1 and 2 will be present, but it won’t be reactive, so if somebody creates a new house they won’t be rerendered. This is a bit weird for me, because I expected that if a new house is added, ParentContainer should be rerendered and therefore the children as well, but after trying it out I noticed that’s not the case, component1&2 don’t see the change.

What is the best way to achieve something close to solution 2 without the disadvantages of solution 3&4? Is there any way to manage this whole usecase in a better way?