I’m using the Mantra architecture, and I’m relatively new to it. I’m trying to develop a simple filtering application that modifies the page content (let’s call them Posts for now) as you change the filters. For example, one of my filters is a checkbox group for Categories. Clicking on one of the checkboxes will result in a view that only shows Posts tagged with that category. I have the logic worked out; my problem is reactively updating the Posts shown in a sane (and scalable) way.
I have a dumb wrapper component around the filters and the Post list, as follows:
<Wrapper>
<Filter1 />
<Filter2 />
<Filter3 />
[...]
<PostList />
</Wrapper>
Each filter has its own container and component, for example, this is a “cost” slider that waits for the Posts subscription to complete (in the PostList composer, which sets the LocalState variable) before it sets the slider’s high and low values in the component:
export const composer = ({context}, onData) => {
const {Meteor, Collections, LocalState} = context();
if (LocalState.get('POSTS_READY')) {
const max = Math.ceil(Collections.Posts.findOne({},{sort: {cost: -1}}).cost);
const min = Math.floor(Collections.Posts.findOne({},{sort: {cost: 1}}).cost);
onData(null, {max, min});
}
};
export const depsMapper = (context, actions) => ({
set_filters: actions.posts.set_filters,
get_filters: actions.posts.get_filters,
context: () => context
});
export default composeAll(
composeWithTracker(composer),
useDeps(depsMapper)
)(CostSlider);
Here’s the CostSlider component
<div className="filter-block">
<br/>
<h2 className="text-white">Cost: <span className="cost-amt text-white inline"></span></h2>
<div className="filter-content">
<Slider range allowCross={false} value={this.state.cost_range} min={this.state.min} max={this.state.max} onChange={_.throttle(this.costFilter, 1000)} />
</div>
</div>
the costFilter function sets the local state (this.setState) and the global state (the LocalState Reactive-Dict through the set_filter action), i.e.
costFilter = (cost_range) => {
// local state management
this.setState({cost_range})
this.setSpan(cost_range); // sets the number in the UI, i.e. Cost:$120 - $150
const {set_cost_range, get_filters} = this.props;
// global state management
set_cost_range({cost_range});
}
Calling set_cost_range above calls the following function contents in my actions file:
LocalState.set('COST_FIELD': cost_range);
This works properly, I’ve done my console.log() s
Now, in my PostList container I have the following code:
export const composer = ({context}, onData) => {
const {Meteor, Collections, LocalState} = context();
if (Meteor.subscribe('posts.list', {
cost: LocalState.get('COST_FIELD'),
[...] // other arguments to query the collection on the publication side
}).ready() ) {
LocalState.set('POSTS_READY', true);
posts = Collections.Posts.find().fetch();
onData(null, {posts});
}
}
};
export default composeAll(
composeWithTracker(composer),
useDeps()
)(PostList);
I’ve tried using Tracker.autorun() here, but the subscription does not auto-update as I want it to. I’ve also tried using the Search-Source package in a hacky way(by stringifying and objectifying the filters on the client and server, respectively), but that requires subscribing to all the posts and I don’t think that’s scalable for my application.
Am I approaching this problem correctly? Should I try something different? Should I be passing data through the parallel components differently? Any advice is appreciated
Thanks!