Differences to Redux
Trux is quite different to Redux, so much so that it literally breaks all of its three core principles. Before you spit your ristretto out in disgust, let me say that I really, really like Redux. I think it is an elegant, awesome solution to application state management and if you are already invested in it, Trux might not be the right choice for you.
I do believe however that Redux and immutable data forces developers to rethink solutions to things in a way that may, at times, seem slightly daunting at first. In order to work with it you may need extra libraries, such as react-router-redux
if you wish to use React-Router and redux-form
to manage the state of your forms with Redux. Asynchronous functionality added on top brings extra complexity with libraries such as redux-thunk
and redux-saga
to consider. There is also a nontrivial amount of boilerplate you need to write. You have to truly invest in Redux.
Trux offers an alternative approach that is geared towards simplicity and speed. You won't need any extra libraries or tools to get it working and conceptually, I feel it is quite easy to grasp. I also feel the investment carries a few less things to manage, learn and keep up to date with.
Multiple stores (sort of)
In Trux, you will typically have multiple stores for data, albeit kept in a single module. Usually these stores are a representation of remote data in the client side of your app. For example, for a blog, you may have a User
model, a Post
model and a Comment
model. Likewise you may also have a Users
collection, Posts
collection and Comments
collection. These stores could each have various components connected to them.
Stores are still the single source of truth for the data driven parts of your app. However, Trux is fine with self managed state for certain components, such as forms.
Protected mutability
Trux stores are mutable, but there's a catch - any time you mutate a store, it is expected that a validation occurs in your system to let you know if this change is allowed or not.
console.log(User.name) // logs Frodo
Component.truxid = 'PROFILE'; // set the truxid for the component
Component.connect(User); // connect a component to the User store
User.name = 'Sam';
User.update(); // attempt to update the user's name in the remote store
It is expected here that the update request would hit some sort of validator on the server. If this validation fails, you will receive an error and Trux will immediately restore
your model to its previous state. Connected components will re render back to their state before the mutation.
If you are not working with an external API, you can simply override the default update
method for your own uses and call a model's restore
method when something invalid happens.
Internal store changes
Changes to a model or collection's data should only ever happen through interactions with the store itself. Let's look at a simple User
model
class User extends Model {
constructor(data) {
super(data);
}
get name() {
return this.data.name;
}
set name(name) {
if (!name || !name.length) {
throw new Error('You must supply a valid name');
}
this.data.name = name;
}
}
In this example, you may change the name
property of a User
anywhere in your app by calling User.name = 'new name'
and this will call the internal set name
method of the model. Notice that you have customisable, context aware ways of ensuring that bad data does get injected into your store. Again, its recommended that your API always perform validation on any mutations as well.