-
Notifications
You must be signed in to change notification settings - Fork 25
Redux-meteor discussion #3
Comments
Yes, I remember this project. I think what it does inside a reducer should go in a middleware. Reducers should not be aware of Meteor collections. |
I totally agree with @djhi , ive just been reading through https://medium.com/@meagle/understanding-87566abcfb7a also as you said before @djhi the middlewares folder could/should definitely be a npm package. im barely getting started with redux, but this stack seems very promising. obviously the fork of @jedwards1211 repo for webpack integration still seems a bit tricky/hacky and hopefully this will change in the future with new versions of meteor (npm integration, webpack, es6 modules, etc ) . btw you should anounce this project on meteor forums to get some visibility and feedback :) great job @djhi ! :) |
Thanks :) I'll make npm packages for the middlewares when I'll be convinced there are no better ways to do them. I'm still trying to figure out what @JEdwards meant by making them hot reload compatibles. I totally agree about it being a "little" hacky but there are no other ways to do this in order to get "standard" npm and webpack integration right now. I'd like meteor to just be a collection of npm packages, maybe with suffixes like I'm not sure I want that projet to be that visible so soon because I'm not really happy about the React part. But I do need feedbacks as it may lead to better ways to do it. Maybe I'll do in one week. |
sure :) btw just fyi , i find your setup and proposed pull-request cleaner than On Sat, Nov 28, 2015 at 2:37 PM Gildas Garcia [email protected]
|
Thanks, I'm just applying some patterns I learned since I work at @marmelab. jedwards did all the hard work ! |
After watching the egghead.io redux videos and browsing through your code, I now understand why it doesn't make sense to have Meteor collections in reducers. I like the approach you took in the middleware. I wonder if there is a way to use constants for the different actions that are dispatched from the middlewares (such as the |
Yes, there is a way. I borrowed this from @RobinBressan. You define an helper object with methods like this to build action types:
We could also define typeBuilder as a function taking the prefix as a parameter of course. You then declare your actions types like this:
And the middleware could use the typebuilder too when dispatching ready and changed:
Same for reducers. Neat, isn't it ? |
I'll introduce this very soon in the project. Just need to find time for it... |
There's another nice Meteor, Webpack, React starter here that even supports SSR but it has many of the same challenges and I had a hard time trying to integrate redux into it because they actually hide the |
Just an update... I've been working with the devs of the reactivestack Meteor, Redux, Webpack starter and it now has full Redux support. I have also fully integrated it with this repos middelware, actiontypebuilder and other cool stuff and it makes for a very solid foundation. The only current drawback is that the reactivestack does not have a standard package.json file, but that will change when Meteor 1.3 comes out. Otherwise it fully supports code splitting, standard Meteor integration (without all the complex scripting of the jedwards stuff - to run it you just type meteor as with any meteor app) and when integrated with the core features of this repo (middleware, action builders, etc), is very cool to work with. |
Hi David, I assume you mean the Meteor subscriptions? If so, subscriptions have a stop() method that I invoke via a seperate action in the components unmount. The middleware already has handles for each subscription so you just need to modify it to stop when the additional action is invoked. You have to change a few things to get it all working, but it's almost the opposite of subscribe (without ready and changed) from workflow perspective. |
@dbismut If it helps... here is what I changed (or added).... @djhi meteorSubscription.js... ...
export default store => next => action => {
if (!action.meteor || !action.meteor.subscribe && !action.meteor.unsubscribe) {
return next(action);
}
console.log('ACTION: ', action)
const { subscribe, get, onChange, unsubscribe } = action.meteor;
// If we already have an handle for this action
if (handles[action.type]) {
const subscriptionId = handles[action.type].subscriptionId;
computations[subscriptionId].stop();
handles[action.type].stop();
//if unsubscribing, dispatch needed actions and exit
if(unsubscribe) {
store.dispatch({
type: actionTypeBuilder.ready(action.type),
ready: false,
});
store.dispatch({
type: actionTypeBuilder.unsubscribe(action.type),
data: [],
});
return;
}
}
const handle = subscribe();
const subscriptionId = handle.subscriptionId;
handles[action.type] = handle;
computations[subscriptionId] = Tracker.autorun(() => {
const data = get();
const ready = handle.ready();
store.dispatch({
type: actionTypeBuilder.ready(action.type),
ready,
});
if (ready) {
if (onChange) {
onChange(data);
}
store.dispatch({
type: actionTypeBuilder.changed(action.type),
data,
});
}
});
}; export function actionTypeBuilder(prefix) {
return {
type: actionType => `${prefix}/${actionType}`,
ready: actionType => `${actionType}/ready`,
changed: actionType => `${actionType}/changed`,
error: actionType => `${actionType}/error`,
unsubscribe: actionType => `${actionType}/unsubscribe`,
};
}
export default actionTypeBuilder('@my-nutrition'); As I mentioned, I then dispatch an unsubscribe in the unmount... componentWillUnmount() {
const { actions } = this.props;
actions.someSubscriptionUnsubscribe();
} That action creator looks something like this and MUST be passed the same export function someSubscriptionUnsubscribe() {
return dispatch => {
dispatch({
type: MY_SUBSCRIPTION, //<- THIS MUST BE THE SAME TYPE PASSED IN ON THE SUBSCRIBE
meteor: {
//passing in a dummy string here so this is not null/empty
unsubscribe: 'unsubscribe'
}
});
};
} You should see everything logged for the actions if you are monitoring them and if you want to clean up the state you can do something like... ...
case actionTypeBuilder.unsubscribe(MY_SUBSCRIPTION):
return {
...state,
items: data //this will set items to an empty array
};
... |
@genyded thanks, it does help a lot. I was hoping there would be some way to hijack the A few things on my side: maybe you should let the export function someSubscriptionUnsubscribe() {
return dispatch => {
dispatch({
type: MY_SUBSCRIPTION,
meteor: {
unsubscribe: 'unsubscribe',
items: initialState.items
}
});
};
} Also, don't you feel it's weird to keep track of the old computation in And one last thing: when I logout from my app, I'm automatically redirected to a signup page. When I signup again (without reloading the page), I'm redirected to the page I was logged to, but the subscriptions don't occur, and my data is not loaded. My way of solving this is to check if there is already a similar subscription and skip the new subscription (instead of stopping the existing one and generating another one). |
@dbismut It's probably possible to do something like composeWithTracker, but right now I do not have time to boil the ocean and was trying to get something working quickly.
... but I supposed you could do something like setting them both to undefined in the unsubscribe portion of the middleware: if(unsubscribe) {
computations[subscriptionId] = undefined;
handles[action.type] = undefined;
store.dispatch({
type: actionTypeBuilder.ready(action.type),
ready: false
});
store.dispatch({
type: actionTypeBuilder.unsubscribe(action.type),
data: [],
});
return;
} That shouldn't have any side effects that I can think of, but there are no delete() or other cleanup methods for either computations or handles - only stop(). That seems like a Meteor issue to solve though. They may be garbage collected at some point, but are both still populated immediately after calling stop() on both (you can see this by logging them somewhere).
|
@genyded Of course, I'm not expecting for you to come up with solutions in what I'm trying to achieve, from what I can see we stumble upon more or less the same threads - I guess we're trying to integrate the same things (i.e. meteor, webpack, redux) so that's why I was eager to have that discussion with you.
And the way I solved the issue is with the following code (not implementing export default store => next => action => {
if (!action.meteor || !action.meteor.subscribe) {
return next(action);
}
const { subscribe, get, onChange } = action.meteor;
// if a subscription already exists for that action type, don't do anything
// there is no stop() for existing subscriptions
if (!handles[action.type]) {
const handle = subscribe();
const subscriptionId = handle.subscriptionId;
handles[action.type] = handle;
computations[subscriptionId] = Tracker.autorun(() => {
const ready = handle.ready();
if (ready) {
const data = get();
if (onChange) {
onChange(data);
}
store.dispatch({
type: actionTypeBuilder.changed(action.type),
data,
loaded: !_.isEmpty(data)
});
}
store.dispatch({
type: actionTypeBuilder.ready(action.type),
ready
});
});
}
}; I think this is a bad idea since it prevents from changing subscription arguments, but for now it solves my logout issue. I'll try to dig further though to understand what's happening, but I was wondering if the same would happen in your situation (again, not knowing what your implementation looks like). Is that any clearer? Cheers. |
@dbismut Cool - always happy to chat about whatever! We also do seem to be moving somewhat in sync. I agree using delete in favor of setting them to unassigned is better for handles and computations. Thanks for clarifying number 3! Not sure what may be going on there yet either, but I get data (ready->change) on subsequent reloads whether or not I unsubscribe first. Based on your last code sample though, it looks to me like |
Yes, It's far from ideal, since that would prevent new subscriptions from the same action type but with different arguments (like But again, the issue only seems to happen when logging out with |
I (and maybe others) would like to know what you find if you have time. May help us if we run into it in the future! |
Hey - so here are my findings. Therefore, these bits of code never actually run in sequence: computations[subscriptionId].stop();
handles[action.type].stop();
// <-- here is the unsubscribe action that stops the code right there
const handle = subscribe();
const subscriptionId = handle.subscriptionId;
handles[action.type] = handle;
computations[subscriptionId] = Tracker.autorun(() => { ...}); But if you dispatch the same subscribe action twice (without unsubscribing), then you should see the same bug I see, in other words that the new subscription will never get ready. EDIT: I think I'm close to a working solution, I'll post this tomorrow. |
Ok so here it is. It's not extensively tested, but it seems to work. Essentially the idea is to make it work similarly to how Meteor handles subscriptions internally, with the use of The actions to be dispatched should be structured as follow: // action.js
export function loadItems(postCollection) {
return (postId) => {
return dispatch => {
dispatch({
type: ITEMS,
meteor: {
subscribe: true, // tells the middleware that this is a subscribe action
name: 'posts', // name of the subscription
params: [postId], // parameters to be passed to the subscribe method
get: () => postCollection(postId).fetch(),
initialData: [] // this is to indicate what the data should be reverted to when the subscription stops
}
});
};
};
} And here is the middleware. // middlewares/meteorSubscription.js
import actionTypeBuilder from '../actions/actionTypeBuilder';
/*
subs will keep track of existing subscriptions and will be structured as follows:
subs[action.type] = {
name, // name of the subscription we subscribe to
params, // parameters of the subscription (like in Meteor.subscribe(name, [params]))
handle, reference to the subscription handle,
computation reference to the autorun computation for the query get
};
*/
const subs = {};
export default store => next => action => {
if (!action.meteor || (!action.meteor.subscribe && !action.meteor.unsubscribe)) {
return next(action);
}
const { name, get, initialData, unsubscribe, onChange } = action.meteor;
const params = action.meteor.params || [];
const sub = subs[action.type];
if (sub && unsubscribe) {
// If there is an existing subscription and the unsubscribe action
// has been dispatched, we stop the subscription.
// The onStop callback will take care of dispatching the unsubscribe action.
sub.handle.stop();
return;
}
// We check if there is an existing subscription with the same subscription name and
// same params that is running with the same action.type
const existing = sub && sub.name === name && _.isEqual(sub.params, params);
if (! existing) {
// Here we know that a similar subscription (same collection, same params)
// hasn't been already dispatched
// If `existing` wasn't undefined, so if there was an existing subscription with
// the same subscription name and same params as the action dispatched
// then there would be no reason to resubscribe
// to the same subscription with same params!
if (sub) {
// However, if there is a subscription that was dispatched
// with the same action.type, we stop it
sub.handle.stop();
}
// Let's take care of the new subscription
const handle = Meteor.subscribe(name, ...params, {
onStop: () => {
// When the subscription stops for whatever reason,
// we stop the autorun computation and we dispatch the unsubscribe action
// This is cool since the unsubscribe action is also dispatched
// if the subscription stops for any reason
subs[action.type].computation.stop();
store.dispatch({
type: actionTypeBuilder.unsubscribe(action.type),
data: initialData || undefined
});
store.dispatch({
type: actionTypeBuilder.ready(action.type),
ready: false
});
delete subs[action.type];
}});
subs[action.type] = {
name,
params: _.clone(params),
handle: handle
};
// the rest is similar
subs[action.type].computation = Tracker.autorun(() => {
const ready = handle.ready();
store.dispatch({
type: actionTypeBuilder.ready(action.type),
ready
});
if (ready) {
const data = get();
if (onChange) {
onChange(data);
}
store.dispatch({
type: actionTypeBuilder.changed(action.type),
data
});
}
});
}
}; |
Cool stuff! I put it through some wringers and everything I tried worked just fine. Nice job! The only thing I noted is that you are not importing lodash in the modified middleware. So, I assume you have a meteor global lodash (or underscore) which is fine. However that will not work 'out of the box' with this repo because it only has an NPM lodash depedency, so it must be imported to use it or or meteor global of one or the other must be added. Otherwise this seems like a good candidate for a PR and/or NPM package of it's own (probably not worth Meteor packaging/wrapping it because 1.3+ will use standard modules)! Thanks for taking the time to do and share this! |
Glad to see you guys thinking about improving this meteor/redux integration! I'll take time to read the full discussion this wk. Have you read the How we redux articles by Abhi Aiyer yet ? I've tried to applied his workflow to a small project but using meteor 1.3 and that was way more simple. I'd just like to get rid of the meteor mixin. More on that later :) |
@genyded You're right, I'm using @djhi well yes, I had read them quickly. Does the simplicity improvement in your small project workflow comes from being able to use Meteor 1.3 or from Aiyer's redux implementation? I guess most of the complexity in your repo comes from removing the meteor mixin and replacing it with a middleware, which is what you did and what I genuinely like and precisely what he doesn't (see this discussion :). Also the fact that you removed all global declarations for collections clearly adds a layer of complexity (but again, I also like it). I think a cool improvement would be to inject subscriptions and unsubscriptions directly in react-redux |
Here is the new implementation I'm trying, which I think makes more sense. I'm not sure it works, I haven't tested it extensively, but I'm removing the I'm using a Promise as I want to try to better handle server side rendering with import actionTypeBuilder from '../actions/actionTypeBuilder';
const subs = {};
export default store => next => action => {
if (!action.meteor || !action.meteor.subscribe) {
return next(action);
}
const { name, get, params, unsubscribe, onChange } = action.meteor.subscribe;
const parameters = params || [];
return new Promise((resolve, reject) => {
if (Meteor.isServer) {
Meteor.subscribe(name, ...parameters);
next( { type: actionTypeBuilder.changed(action.type), data: get() });
return resolve();
}
const sub = subs[action.type];
if (sub && unsubscribe) {
sub.handle.stop();
if (subs[action.type] && subs[action.type].computation) {
subs[action.type].computation.stop();
}
delete subs[action.type];
next({ type: actionTypeBuilder.unsubscribe(action.type) });
return resolve();
}
const existing = sub && sub.name === name && _.isEqual(sub.parameters, parameters);
if (existing && sub.handle.ready()) {
return resolve();
}
if (sub && sub.handle.ready()) {
sub.handle.stop();
}
next({ type: actionTypeBuilder.loading(action.type) });
const handle = Meteor.subscribe(name, ...parameters, {
onReady: () => {
subs[action.type].computation = Tracker.autorun(() => {
const data = get();
if (onChange) {
onChange(data);
}
next({ type: actionTypeBuilder.changed(action.type), data: data });
return resolve();
});
},
onStop: (error) => {
if (error) {
next({ type: actionTypeBuilder.error(action.type), error });
return reject(error);
}
if (subs[action.type] && subs[action.type].computation) {
subs[action.type].computation.stop();
}
}
});
subs[action.type] = {
name,
parameters: _.clone(parameters),
handle: handle
};
});
}; EDIT: some tweaks - contrary to what I've said above, you wouldn't want to get the |
@dbismut - Hey David, I'll try to find some time to play with this over the weekend. I am curious if you'd recommend a particular Promise library/package? Also is there a particular reason you went the Promise route instead of something like Meteor.wrapAsync()? Thanks! |
@genyded unfortunately I'm really genuinely new to all this, so I'm really afraid I'll give you a wrong explanation. But it seems to me that Promises are part of es6 so no need for a package AFAIK. However, I did need to add Not sure about |
Thanks again! Got it. Just FYI - 'wrapAsync' is sort of a Meteor-ish way to do promise like calls (make Async appear as sync). IMHO it's not as 'clean' or straight forwards as Promises (and often requires bringing Fibers into the mix), but it is Meteor's 'way'. Recent occurrences in the JS community as a whole sometime have me wondering (and probably MDG too) what parts of an app should be Meteor and what should be 'something else'. We saw evidence of this with React (versus Blaze) and I think this is also something that falls under that umbrella (promises versus wrapAsync). Not saying anything or either is right or wrong, just that this too has a couple of ways of approaching it. I personally tend to favor Promises as well, but others may not because it's yet another non-Meteor API to learn and does add more code. Just don't want others who may read all this to think anyone is saying this is the only (or maybe even the 'right') way to handle things. I am anxious to see what unfolds as the ultimate preferred 'mold' for a Meteor, React, Webpack, Babel, Redux, GraphQL, Relay, Flow, ??? app. LOL. It'll probably be good/valid for about a month ;-) |
True I agree that this is probably a lot of headaches just for something that is going to be outdated in a few weeks ^^ Who knows what MDG will come up with React integration in Meteor 1.3+.
Me too... At the moment, I'm trying to make my app as agnostic from Meteor as it can be, using Meteor functions only in middlewares. At the expense of duplicating the data state from minimongo in redux as in the nutrition app from @djhi Also by the way, some actions in the app use thunks when I'm not sure it's necessary. For example at L11 in meals.js, this can be rewritten: export function deleteMealFactory(collection) {
return id => {
return dispatch => { // <--- this dispatch is not necessary
dispatch({
type: MEALS_REMOVE,
meteor: {
remove: {
id,
collection,
},
},
});
};
};
} I think this could be rewritten: export function deleteMealFactory(collection) {
return id => {
return {
type: MEALS_REMOVE,
meteor: {
remove: {
id,
collection,
},
},
}
}
} |
And here is the new // meteorMethod.js
import actionTypeBuilder from '../actions/actionTypeBuilder';
export default store => next => action => {
if (!action.meteor || !action.meteor.call) {
return next(action);
}
const { method, params } = action.meteor.call;
const parameters = params || [];
const meteorMethod = typeof method === 'string' ? Meteor.call : method;
if (typeof method === 'string') {
parameters.unshift(method);
}
return new Promise((resolve, reject) => {
next({ type: actionTypeBuilder.loading(action.type) });
meteorMethod(...parameters, (error, result) => {
if (error) {
next({ type: actionTypeBuilder.error(action.type), error });
return reject(error);
}
next({ type: actionTypeBuilder.success(action.type) });
return resolve(result);
});
});
}; That allows writing actions such as: // auth.js
export function signIn(email, password) {
return {
type: USER_SIGNIN,
meteor: {
call: {
method: Meteor.loginWithPassword,
params: [email, password]
}
}
}
}
// example chaining actions
export function getIpAddress() {
return {
type: USER_GETIPADDRESS,
meteor: {
call: { method: 'users.getIPAddress' }
}
}
}
export function signUp(email, username, password, captcha) {
return {
type: USER_SIGNUP,
meteor: {
call: {
method: Accounts.createUser,
params: [{ email, username, password, captcha}]
}
}
}
}
export function signUpWithCapcha(email, username, password, captchaValue) {
return dispatch => {
return dispatch(getIpAddress()).then((remoteip) =>
dispatch(signUp(email, username, password, { value: captchaValue, remoteip }))
);
}
} So far so good, and everything related to redux now looks similar to what redux guys are doing :) |
@dbismut You're right about the unnecessary dispatch. You did a great job reviewing the middlewares ! Can you submit a PR ? Thx ! |
Hey @djhi, unfortunately I'm not familiar with jedwards1211/meteor-webpack-react, I'm using kickstart-hugeapp. Your project has been immensely useful in helping me understanding redux, but I think it's too complex for me to be able to submit a PR without breaking it. I'm thinking of publishing a repo with my own implementation of meteor / react / redux (largely based on what you did), but I'm really scared I would mess your app with the new middlewares and associated actions and reducers. |
Ok. My latest attempt, for reference if it helps anyone :) Again, not tested extensively, but should show the general direction!! Main changes: there is now a subscriptions state attribute that handles the state of subscriptions via a middleware. That middleware then delegates the autorun query to the datasource middleware. |
thx for sharing :) |
@dbismut - Hey David, Finally got a chance to try out your latest and overall looks very cool! I am curious (and unless I missed something did not see an example) how you are handling 'unsubscribe'? |
Hey @genyded, cool that you like it! To be honest, I haven't really tested export function unsubscribe(type, name, ...params) {
return {
type: type,
meteor: {
unsubscribe: { name: name }
}
};
} ...where What I really really like about the way everything works now is the fact that the actions (whether they are related to subscriptions or |
Cool - I figured out the unsubscribe thing - it's mapped to the 'stopped' action type and I was still looking for 'unsubscribe'. I too love the use of promises for the reasons you stated and because they can be chained if needed. One other thing I haven't quite yet figured out though is what these should look like in the store as far as the middleware portion. The old one(s) had something like I'm sure I'll figure it out if I play a bit more, but if you have a sec to clarify this, that would be cool. |
Clearly I'm not using notifications in my implementation just yet. To be honest, I'm not sure that's really relevant the way it's done in the original implementation. Back to the original implementation code, I would rather do: export default store => next => action => {
if (!action.meteor || !action.meteor.call) {
return next(action);
}
const { method, parameters, onSuccess, onError } = action.meteor.call;
const params = parameters || [];
Meteor.call(method, ...params, (error, result) => {
if (error) {
if (onError) {
return onError(error);
}
return next({type: NEW_NOTIFICATION, level: 'danger', message: 'error message'});
}
if (onSuccess) {
return onSuccess(result);
}
return next({type: NEW_NOTIFICATION, level: 'success', message: 'success message'});
});
}; ...of course, that would mean you would combine the middlewares as in: const middlewares = [
thunkMiddleware,
meteorSubscription,
meteorDatasource,
meteorMethod,
meteorInsert,
meteorUpdate,
meteorRemove,
loggerMiddleware,
]; Hope this makes sense? |
OK - i took out the notification stuff altogether, but am still getting export function signIn(email, password) {
return {
type: USER_SIGNIN,
meteor: {
call: {
method: Meteor.loginWithPassword,
params: [email, password]
}
}
}
} ...as opposed to... export function signIn(email, password) {
return dispatch => {
dispatch({
type: USER_SIGNIN,
meteor: {
call: {
method: Meteor.loginWithPassword,
params: [email, password]
}
}
})
}
} How can they work with dispatch? |
Well, the first action without the dispatch is what an action is supposed to look like: you still need to dispatch it as in The structure with Can you please paste your meteorMethod middleware? Does it start with: export default store => next => action => { ... } Can you also paste how you combine middlewares? Make sure you don't add the meteorMethod middleware by calling the function |
Here is the middleware and it behaves the same with or without dispatch...
import actionTypeBuilder from '../ducks/actionTypeBuilder';
export default store => next => action => {
if (!action.meteor || !action.meteor.call) {
return next(action);
}
const { method, params } = action.meteor.call;
const parameters = params || [];
const meteorMethod = typeof method === 'string' ? Meteor.call : method;
if (typeof method === 'string') {
parameters.unshift(method);
}
return new Promise((resolve, reject) => {
next({ type: actionTypeBuilder.loading(action.type) });
meteorMethod(...parameters, (error, result) => {
if (error) {
next({ type: actionTypeBuilder.error(action.type), error });
return reject(error);
}
next({ type: actionTypeBuilder.success(action.type) });
return resolve(result);
});
});
}; Here is the combine... ...
const middleware = [
thunkMiddleware,
routingMiddleware,
meteorSubscription,
meteorDatasource,
meteorMethod,
//meteorMethod(newSuccessNotification, newErrorNotification),
//meteorInsert(newSuccessNotification, newErrorNotification),
//meteorUpdate(newSuccessNotification, newErrorNotification),
//meteorRemove(newSuccessNotification, newErrorNotification),
//loggerMiddleware
];
const finalCreateStore = compose(
applyMiddleware(...middleware),
... |
Hmm... I don't know, that's weird. There is this issue mentioning something similar: reduxjs/redux-thunk#15 ... but I guess that's not it right? |
@genyded not sure if this is related, but there was a bug with the way I handled |
sorry for necroposting, but I'm thinking about making front end of a meteor app easier to live with, and I consider adopting redux (maybe for just localstate management, maybe for data from mongo as well, not sure yet). I find what you post here really nice and promising, and would like to ask, what happened to all this now, after a year passed? were you happy with this solution? has it evolved to maybe some npm package I could use? how ready for prod use is stuff in the gist by @dbismut? would you advise to use it? or maybe you've got totally disappointed with this concept and it's not really worth that? thanks |
Btw, is there a way to have some branch of the store as a reactive data source, so I could dispatch an action that would e.g. flip some flag in it and all the components that have that flag as a prop would automatically re-render? |
Hey @you-fail-me, honestly this was done a long time ago, and even if this still works with the packages at the time, I wouldn't be so sure with the latest updates (Meteor 1.4, React 15, etc.). I'm using it in production but I wouldn't consider it very stable. I don't remember exactly, but I know there are some edge cases where everything doesn't work so well. Also, I'm not a pro developer, so anything I do I wouldn't recommend in production ;) However, I've updated the gist with the latest code I wrote last year if you want to have a look. But considering all the controversy around Meteor, I'm not sure I would advise pursuing this route. |
@dbismut Thank you for reply! What do you guys think about this https://github.com/samybob1/meteor-redux-middlewares ? |
Hi @you-fail-me, I don't use Meteor anymore and won't be working on this projet in this form. I might rebuild it with the stack I use for almost 2 years now which I have revisited recently with Apollo. My current stack uses:
No real time updates for now but at least you're free to do it your way. Too much lock in with Meteor. |
Hey, dunno if you looked at this already. Looks interesting https://github.com/zhongqf/meteoredux
The text was updated successfully, but these errors were encountered: