There’s no winners on this one, only losers (Unless you want to go a different route and hit up ClojureScript or Elm).
Immutable.js is definately the well known solution, and we have various groups at work using it with good results. However, the API is clunky (records make this better, and the getters are natural), and serializing from immutable to vanilla object when need be is incredibly slow.
I had a chat a while back with some of the people developing Discord (the game oriented chat software that also happen to be used by Reactiflux), and they had tremendous performance gains going with just simple object spreads. Deeply nested state is a pain this way, but you really should not do that 95% of the time. I like the react util immutable helpers or just deep cloning when that’s not good enough.
You’d think Immutablejs structural sharing would be a huge win, but browser garbage collectors are becoming quite good, and in my personal benchmarks (copying/modifying arrays and lists of hundreds of millions of complex objects, hundreds of millions of times), there was almost no gains from immutablejs, and sometimes even losses. If you reuse the data a lot, you main gain, but that is just not a common scenario in normalized data.
Then the other reason to use Immutablejs is as a safety against mutating by accident. I’ve grown fond of https://github.com/leoasis/redux-immutable-state-invariant as a development-only tool. Won’t check things in production, but it catches almost everything during development or during test runs.
Now, if you enjoy Immutablejs and it works for you and you don’t mind the API, go for it. It’s a great tool. But make sure you understand the gains and tradeoffs. It’s not as obvious as you’d think.