Mantra with Redux doesn't rerender the component

So I’m using Mantra with Redux and in my create_deal container a subscription to a store for instance.

Store.subscribe(() => { const error = Store.getState().deal.DEAL_STATE; onData(null, {error}); })
So this all works great but onData never rerenders my component with the error prop. The store and reducers all work fine. If I were to console.log(error) it would return the desired value. But no matter what I can’t get the component to rerender. Isn’t onData suppose to do this for me? Anyways, if anyone is using Mantra with Redux please let me know how you handle this because at the moment I’m pulling my hair out and am down 5+ hours on a project.

The snippet on it’s own looks fine. Could you paste your entire container and component files?

I’m not sure if this is related to your problem but I’ve noticed that when using onData() the way you have, it never runs initially because the callback in subscribe only runs once state changes. I tended to do something like this in my container before I just dropped react-komposer and in favour of connect() from react-redux:

const initialError = Store.getState().deal.DEAL_STATE;
onData(null, {error: initialError});

Store.subscribe(() => {
  const error = Store.getState().deal.DEAL_STATE;
  onData(null, {error});
});

This ensures the initial data is always provided BEFORE a change of state occurs. I really don’t like having to do this, it looks messy. While react-komposer is great for tracker based containers I feel like it’s redux features are just an inferior reproduction of react-redux connect(), which can be used in addition to react-komposers tracker features.

`

import {useDeps, composeAll, composeWithTracker, compose} from ‘mantra-core’;

import AddDeals from ‘…/components/add_deals.jsx’;

export const composer = ({context}, onData) => {
const {Meteor, Collections, Store} = context();

function handleProps() {
	if(Meteor.subscribe('users').ready()) {
		const {types} = Meteor.user().profile;
		const error = Store.getState().deal.DEAL_STATE;
		onData(null, {types, error});
	} else {
		onData(null, {});
	}
}

// subscribe to state updates
// and keep handle to unsubscribe
const unsubscribe = Store.subscribe(() => {
	handleProps();
});

// get initial state
handleProps();

// function to unsubscribe from Store
// and clearing error
const cleanup = () => {
	unsubscribe();
};

// running cleanup when unmounting the component
return cleanup;

};

export const depsMapper = (context, actions) => ({
createDeal: actions.addDeals.createDeal,
context: () => context
});

export default composeAll(
composeWithTracker(composer),
useDeps(depsMapper)
)(AddDeals);

`

Hmm yeah that looks like it works fine. The only problem I have come across is when you add Meteor subscriptions. it will rerun the container initial state or Store.subscribe and causes problems. Because of the subscription problem the container will never unsubscribe from the Store. This pretty much breaks everything and causes the Store.subscribe to rerun anytime there is a state change in another container or action. Pretty frustrating and I can’t find a way around it.

Yeah, I think what’s happening here is any time there’s a reactive / tracker change, you’re starting a new subscription, so your onData is actually being called multiple times, and I guess the last time is actually from that first time, when Meteor.subscribe('users').ready() was false and so it returns {}, overwriting the value from the other instance of onData.

In short, you can’t mix store subscriptions and tracker reactivity in the same composer, it’s asking for trouble.

It’s fine to use both in separate composers, e.g. supplying separate props. But if you need both for a single prop, you’ll have to pick one or the other. This means either using a ReactiveVar for the error state (easiest) or perhaps better, but a bit more work, store the user profile in redux (by having a Tracker.autorun() that calls dispatch() and a reducer to put it in state) and then your reducer for your code above can make use of that. It’s a lot more work but it is a lot clearer what’s going on :> Using tracker only though in this case is definitely less work.

Agreed, only just seen the code and realised you’re trying to do tracker and redux based stuff in the same container. You need one tracker container that’s composed in with composeWithTracker, and then another that’s composed in with compose() for redux. So it’d be something like:

export default composeAll(
  composeWithTracker(trackerContainer),
  compose(reduxContainer),
)(AddDeals);

Also the order of arguments to composeAll matters.

Now if I use something like ReactiveVar ReactiveDict, or Tracker.autorun(), does that rerender my container anytime it changes or does it only update what’s changed. I don’t have a problem using any of the other options of redux but I just want to make sure it doesn’t hinder performance.

Yes, you’re right… with composeWithTracker, any reactive invalidation will cause the composer func to rerun and call onData which might change the props. Of course, that’s no different from a redux subscription triggering it’s handler to rerun and calling onData which might change the props. Either method is fine.

I’m not 100% sure I understood your question though, so if I missed it, maybe try clarify with an example.