The short answer is you don’t need to use it but it’s helpful for larger apps or working with more people.
So I think it’s easiest to understand if we think about it without the database and without Meteor and then add those in as the example goes on.
For example say you have a todo list app and your customer wants the header to turn red if there are no todo items and to turn white if there are todo items.
If we’re using vanilla React this becomes an issue because the Todos
component is storing the data in it’s this.state
variable (in memory). There is no way for the header to reach in and check how many todos there are and the Todos
can’t talk to the Header
component (by design).
So the solution to this is to move the todos data (list of objs) from the Todos
comp up higher to the App
component. In this example App is root component. We’ll stick this in App’s state and call the key todosData
.
Now Todos
and Header
are both a child of App
so we can pass the todos data down as props to both of them using
<Header todosData={this.state.todosData} />
and the same for Todos
.
Great so now we’re passing data down to each component and they can declaritively do their thing according to their rules (making the header red if props.todosData.length
is 0.
However now we have an issue as our app grows. Once you have many of these things to push up to the App
level it starts to get confusing. If you’re working on a component that is 5 layers deep, you’ll have to walk up the tree to see where this trail of ‘props’ stops. Also if you move it you’ll need to re-do the ‘pushing it down as props’ dance.
Also if you have 10 layers deep of components, perhaps you go up 3 layers and add your data there. This makes it easier to ‘thread data through props’ but now your data is in two places… App
and this other component sandwiched in between. Clearly this isn’t going to scale (as in code size). I’ve ran into this in a small-ish React Native app.
Ok so now we drop this into Meteor (with no db yet) and instead of keeping that local data in App
we’ll use the Reactive-Dict to store the data in lib/my_state.js
and we’ll expose it globally as dstate
to keep things more clear.
We can now buildup a todos array and set
it back into the dict so that any other UI can update itself when the data changes via the mixin. When the click or form handler fires we can just set
the data and call it a day.
However if we want to make maintenance and testing easier we can put our insert logic into a separate function… lets call it addTodo
and we’ll put it in an actions.js
file so that it’s out of the way. Now in the function it’s taking a para of data
and it’s just fetching the current postsData
, pushing the new post (from the param) on the array and then saving it back using dstate.set('postsData', newPosts);
.
Now the click/submit handler gets the form data and calls postsData(formStuff)
and calls it a day. It’s just responsible for listening to the submit event and forwarding it on. Keeping the views dumb makes it easy to test.
Cool now it’s also super easy to test the logic that inserts a post because it’s just a function!
Ok so how cool would it be if we could log all the things that change state (in dev) so that when we’re debugging we can watch the trail of changing data and see why our app isn’t acting the way we want. We could add a console.log into every one of those functions in the actions.js
file but with tens or hundreds that seems tedious… unless we do it automatically!
Right, so to do that all we need to do is wrap our reactive-dict with a function that will log any of the params that we pass in, then it passes it to the dict… kind of like tapping into the middle of this ‘flow’:
var _dstate = new ReactiveDict('appState');
dstate = {
set: function(key, val) {
console.log("State", key, val);
_dstate.set(key, val)
}
get: ...
}
Right. now it has the same API as before but we get logging. (this is the same concept as Redux middleware)
So now we’ve pretty much re-implemented a basic version of Redux. It doesn’t have reducers but we’re basically handling that in the action file. In a larger Meteor app you could make your own reducer layer and just make the action functions return an object with a type like ‘ADD_TODO’ and that second layer would then handle the data changing.
Using a pattern like this could be much simpler than using Redux for smaller apps. For larger apps the learning curve and boilerplate would likely be worth it (as well as for faster onboarding with teams). However since we have Reacive data and the mixin, we don’t need the event driven system flux/redux has… we can use the mixin to detect changes.
Hope this helps!