-
Notifications
You must be signed in to change notification settings - Fork 581
Make state immutable in reducers/effects #46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
So! The current tests run in node which means you can't call So I made a test file that you can browserify and run in the client. However, setting up automated testing for browser tests is super opinionated so I didn't set up a way to actually, you know, run the tests. We use karma for scripto for client testing (and xvfb for doing it headless on CI). However, karma is very heavy and requires a TON of modules and plugins. but it also is extremely reliable, and gets you coverage reports easily. But, since it's also XVFB for automated testing, I still can't get it set up correctly on travis-ci (but can do it on circle-ci). I've also done testling, which is super minimal, but doesn't get you coverage easily and still requires XVFB. Zuul is great, but you can't run it in an automated fashion without setting up sauce labs support (I have an extremely old branch where I tried to implement local test running but never finished). So like, um, I have no clue how you want to commit to running these tests, but like this will open it and run it... (Or xdg-open if you're on linux. I have no clue how to do this on windows lol) |
|
Ooh, love this patch - minor version I reckon. Just needs tests fixed and we can merge. Browser tests are def tricky - perhaps just add these tests for now, but exclude them from CI? - that way Travis stays green and we can save ourselves some work in the future. I've done a bit of work for tests / coverage here - #24. It uses |
Call Object.freeze on state when passing it into to the reducers or effects so that state cannot be modified except when being returned from an emitter. Add tests to prove state is immutable in these scenarios.
|
Oooooh I hadn't seen tape-run and it's instructions for using electron! That would greatly simplify some other only-chrome stuff in my life :). Interestingly, you can't easily mark a test as "skip" when you run tape tests through the node executable. Adding I renamed it so it comes later in the glob pattern (and since node will exit after the first file it will never execute), but this feels really hacky? Better ideas or solutions? Also added some documentation to explain that |
|
So I tried to use Zuul since then it can run in sauce labs for free using "open sauce" which means you can target a ton of browsers, but Zuul uses an older version of browserify-istanbul, which relies on an older version of esprima which is unable to parse template tag syntax. I opened a PR to update to the latest version of browserify-istanbul, but that's a dead-end until then defunctzombie/zuul#289 I do have this running under karma in my project, but the list of requirements is pretty heavy (although some sub-deps of choo require karma already so we're installing it as it is). Here's the minimal list I've found I need to just support tape and istanbul via browserify: That feels kind of heavy, but it works. Except for in travis-ci. |
|
This patch is looking great by the way - testing, merging and publishing as minor ✨ |
|
oops; so the |
|
Ah yeah, so the problem here is that I think we should clean this up and publish in |
|
@yoshuawuyts @toddself Hey, just a heads up -- I've lost the reference to where I read about this, but I distinctly remember reading that |
@yoshuawuyts interesting; could you say a bit more about this? If I'm understanding you correctly, this would effectively remove "actions" as a concept independent of effects and reducers--is that right? |
Oh yeah, that's a good call. We could def add an argument that can be passed on
Ummm, maybe? The way I view actions is as a single trigger on any of the stores - a Hope this answers your questions! |
Interesting. In my work, I've been thinking of "actions" as a separate abstraction, basically representing any "verb" within the system (some of which map directly to user actions like |
|
I have it set up similarly but rather use effects to trigger multiple reducers to react to input. The user might perform some action which causes several parts of the state to update, so i trigger an effect which in turn triggers all the single modification reducers to fire that are necessary.
|
|
Yeah, I can see how that makes sense, although again, if there's going to be a 1-1 mapping between actions and effects||reducers, then it seems to me like the "actions" concept is unnecessary... |
|
It's more of one-to-many. One effect can trigger several reducers, but On Fri, Jun 24, 2016 at 06:36:12AM -0700, Anand Thakker wrote:
|
|
Here is an example: On Fri, Jun 24, 2016 at 06:36:12AM -0700, Anand Thakker wrote:
|
effects-reducers is 1-to-many, but I thought @yoshuawuyts was proposing above that actions-{reducers or effects} should be 1-1 |
Yup you're both right. One action can trigger either one effect or one reducer; effects can emit multiple actions. Not the current behavior, but think it would make more sense |
@yoshuawuyts I can definitely see your point on the clarity/simplicity of doing this, but it still leaves me with the question about what value is then being added by the "action" concept? Since each action would simply map directly to either a reducer or effect, they really are equivalent to function calls (like you said above) -- so at that point, why is the indirection through |
Because multiplexing. A function call requires knowledge of which function you're calling, and importing it explicitly from the location it's declared. By using a message bus, function calling becomes centralized and thus more loosely coupled. This enables better tracing, logging and upgrading parts of the application without touching other parts. It also clearly signals that it's a different method of calling functions; one where callbacks or returning eventual values doesn't fly - thus making for a better enforcement of the unidirectional data flow (despite there always being ways of cheating around it). |
Ah, yeah: I totally agree on the value of a message bus internally. What I was thinking/wondering about was not plain function calls, but rather an API where instead of getting
... but I hadn't thought of this point, which I can see being a downside of the thing I'm describing above. I guess now that I've written it out more explicitly, the thing I was suggesting adds more magic and more internal machinery for little gain. Thanks for engaging with my speculations and questions! |
|
@yoshuawuyts agreed on the fact that |
|
@toddself Given that this is moved to https://github.com/yoshuawuyts/choo/tree/browser-tests and |
|
💯 |
Call Object.freeze on state when passing it into to the reducers or effects
so that state cannot be modified except when being returned from an emitter.
Add tests to prove state is immutable in these scenarios.