-
Notifications
You must be signed in to change notification settings - Fork 781
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
V2 Effects #750
Comments
Thanks for writing this up 🎉 Glad I could ask the painful questions 😂 |
So when do we start making repo(s) for things like I'm on board with making them separate packages, but does each deserve its own repo or would a monorepo be better for the FX packages? |
I like your little mono repo tbh As long as it's tree shake-able, I don't see the harm 😄 But some Effects might get gnarly and need to have their own too. I think most light wrappers can go to monoland and heavy wrappers might need to be their own thing |
@selfup tree-shaking is a separate issue since I'm still on board with individual packages for each, allowing users to only add dependencies on the FX they want to use. On a related note - I'm not sure how I feel about subscriptions, and if they deserve to be separate or not. |
I am open to anything tbh, I just know that setting up each repo with the same rollup config will get tiring. From a user perspective it's great to have individual things, just worried about maintenance 😄 Yea subs are a weird one. Down to open the floor on this one for sure |
@okwolf My current plan is to create a @hyperapp/gizmo repository for every domain, e.g., @hyperapp/time should include delay effects, tick subscriptions, constants, and (maybe) even utility functions. |
@selfup Yeah, I know! Fortunately, the build configuration part of the process can be automated, the hard part is and has always been writing and keeping the documentation up to date. |
@jorgebucaran The main advantage to using a monorepo would be making some of that automation easier - since the FX would always be in folders adjacent to each other so scripts could reliably add new or update existing FX libraries. This includes chores like updating a dependency version across all FX with one operation. With that said, I'm OK with separate repos per package. The main advantages with that way would be dedicated issue tracking, contributing to some repos but not others, and tighter control over creating new FX. |
@okwolf Noted. How do you manage your monorepos by the way? |
I don't have much experience in this area myself. But if you want to make a political statement you're apparently supposed to use Lerna. |
Fascinating. I wouldn't use Lerna (or any other software) anyway. A simple script which I'll write myself should do if I go the monorepo way. |
Wouldn't those examples end up being wrapped in the Beyond that, I think it looks good 👍 |
@SkaterDad since JSX compiles into calls to our |
I'd guess it's going to be confusing to more than React users if JSX is promoted for working with Effects. I see how it works, but I can't imagine doing it in real code. In the view functions, it makes some sense, since it's mimicking HTML. Even there, I've stopped using it in my own projects. |
@SkaterDad - IMO I quite like the JSX style as it seems to better communicate the declarative nature of things. There's not really a logical reason I can attribute to this feeling except maybe I'm thinking back to other declarative languages that used XML. Just my 2 cents worth. |
@SkaterDad We'll have to wait to see how it pans out. Since effects and subscriptions are never directly created inside the view, I find using JSX a breath of fresh air as it makes my application code feel less monotonous. The good news is that using JSX to create effects (or subscriptions) is entirely up to you! |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@jorgebucaran: I agree with @mindplay-dk and I think the comment was worthwhile. Why would you encourage JSX syntax other than preference? Is there any benefit of using JSX over the Vanilla JS syntax? When I saw the JSX syntax my first reaction was "wait, isn't this an effect?". |
I've given up fighting the case against JSX, so I withheld my opinion until now. I respect personal preference. But I do find it confusing in this case and what is more, I fear that the APIs are being defined (or rather valid variations being dismissed - like currying and positional arguments) so that this style can be maintained. Although, I realise there are other valid arguments against these variations it would be nice to know that the decisions were made impartially without bias toward JSX. |
No, named arguments are idiomatic and that's why I prefer them to positional arguments or currying arguments. Even if the proposed effects API used positional arguments it would be easy to write effects in JSX using a facade that translates their signature to what the JSX parser expects. |
@ryanhaney Don't use JSX to write your effects or subscriptions. It's not mandatory. But let others who like it use it if they prefer it. If we are worrying about future examples in the documentation using JSX, then let's not. I'll use plain JavaScript for all the examples. I'm happy just mentioning that JSX can be used too. |
I have nothing against named arguments, they work great. Just wanted to voice my concerns around that issue. That is all I have to say on the matter, thanks for the confirmation.
☝️ This I think, is quite important though. Glad to hear you consider it. |
👍 |
I have a effects related question regarding development against mock data. It is very convenient to e.g. avoid real https requests during development and work against mock, or in case of Random() to have control about what number is going to be generated when debugging the app. |
@mshgh Yeah, I think so! Since effects separate out everything that is a side effect, you could just import your own fake side effects instead of the real ones while in debug mode. At least if you're using CommonJS modules you could: var Random
if (process.env.NODE_ENV === 'development') {
Random = require('./mocks/fakeRandom')
else {
Random = require('@hyperapp/fx').Random
}
... I'm not sure if the same is possible, or how with Es6 modules though 🤔 |
@mshgh One of the big reasons, if not the reason for all these changes in Hyperapp is to simplify testing, so, yes, some of those things, will be possible with effects. With Random() effects, what number will be generated is not important. Instead, we want to test if our action produces an effect that says it will generate a random number. There will be no actual random numbers generated. It's a strict equality check. Similarly, with an Http() effect, we don't care what data the server will send, but whether our action produces an effect that says it will fetch some data from our server with some specific parameters. No actual HTTP request will be created. Those will be only the tests that assess your effects (technically actions that produce effects). Those will not be the only tests in your application. You probably want other tests as well. For example, a test that checks whether some UI behaves appropriately when you try to render some null or invalid data. |
What @jorgebucaran says is true, but if I'm not mistaken I think @mshgh was not so much talking about testing as in unit testing, but rather about live-debugging an app with faked effects (to use a fake backend, with fixed data for example). At least that's the perspective I took in my response. Just to clarify :) Unit-testing is a far more common type of test probably, but anyhow: both types of testing/debugging are equally more easy thanks to separate effects. |
@zaceno is correct. I got the part effect itself is "just" data, so it is easy to test if the effect function generates what is expected. I was curious about the live-debugging with fake backend. As put above. So I should implement my own fakeHttp effect. It would work for me I think. Thank you guys for your quick answers. |
The FX API rocks! 😎 P.s I wanted to make some enquiries. In the case of running multiple effects at once as demonstrated here: const StartItAll = state => [
state,
[
Fringe.someEffect({
action: SomeAction,
prop1: "foo",
prop2: "bar"
}),
Fringe.anotherEffect({
action: AnotherAction
})
]
] I was wondering if the effects are run by the hyperapp runtime one before the other in order of the array indices. This is because sometimes I only want an asynchronous task to run if the previous async task resolves successfully and then use whatever data gotten from there. I'm really appreciative of the FX API and I'm adapting to it, however this issue is trivial to me. |
@ThobyV In your example every effect will run concurrently. To do what you are asking for, It should be possible to come up with a library of effect utilities to spice this up. |
Thanks for the clarification @jorgebucaran. I think I understand that well. I'll really look forward to the FX utility library. The It also happens that most times I need to run a chain of abstracted queries. I'm building an application using HAV2 and might have to write the firebase effects myself. |
Summary
Side effects are the bread and butter of our programs. It is almost impossible to get anything useful done without them. At the same time, we want our programs to be correct and easy to test, refactor, parallelize, optimize and so on. The key is to minimize and isolate side effects, pushing them to the fringes of our code.
V2 introduces a new declarative approach to creating side effects known as managed effects or just effects. Effects tell Hyperapp how to make HTTP requests, update our browser history, send data over WebSockets, etc. Effects save you from dealing with asynchronous control flow and managing side effects on your own.
In this issue, I'll introduce the upcoming Effects API. I'll explain what effects are and how to create, use and implement them.
Background
Just like a virtual node represents a DOM element, but it is not a real DOM element, effects represent the potential to produce one or more side effects but don't run any code themselves.
Think of effects as virtual commands waiting to be fulfilled. Creating them won't change the environment or involve using async functions, promises or callbacks. Effects are plain objects that carry what needs to happen along with whatever data they need.
Most of the time we'll use an effect library to create effects that Hyperapp can make sense of: @hyperapp/http, @hyperapp/random, @hyperapp/time, etc.
We'll hand the effect object over to Hyperapp as part of the return value of an action. The runtime will pick it up, execute it, and notify our application with the result when it's done.
Here are some concrete examples: creating HTTP requests, creating time delays, setting or removing the focus on a DOM element, generating random numbers, changing the browser history, writing and reading to/from local/session storage, opening/closing a database connection as well as writing and reading to it using IndexedDB, requesting/exiting fullscreen, enqueing data over a WebSocket, etc.
Declarative effects
We create effects with an effect constructor. You can implement your own effects, but most of the time you won't need to, as Hyperapp ships with all the effects you probably need.
Let's study the basic anatomy of an effect, and then we'll look at some concrete examples.
Fringe.someEffect
is an effect constructor. It returns a tuple like the one below. The first element is the effect implementation, encapsulating the magic. The second element is your props object.action
is not a special property name, but you'll see it used very often as it's obvious what is for. This is the action you want Hyperapp to dispatch with the result of the effect. The other props are whatever data we need to pass to the effect. An HTTP effect will need at least a URL, while other effects may not even take properties.How do we tell Hyperapp to run this effect? We do that through actions (#749).
This tells Hyperapp to update your state (in this case we're just pasing the same state object we received in the action) and run
Fringe.someEffect
.Need to parallelize multiple effects?
Examples
Time delays
After Hyperapp is initialized, it will dispatch your
init
action to start your app, setting the state and running theTime.delay
effect.JSX
Turns out you can use JSX to describe effects too.
HTTP requests
JSX
Generating random numbers
JSX
Implementing your own effects
If you want to implement effects, one of these things are happening:
PhaserLink
and there is no Hyperapp effect for it.Let's implement an effect adapter for
setTimeout
, there's not much to it. There are essentially two parts to implementing an effect:An effect function. It encapsulates the implementation of the effect. It receives your props and the
dispatch
function so you can send messages back to Hyperapp.An effect constructor (optional). A function that takes your props and returns a tuple with an effect function and props.
Why cache the effect function? If we create a new function from scratch every time we invoke an effect constructor, we wouldn't be able to assert
deepEqual(effect1, effect2)
.Consider an action returning an effect that generates random numbers. We don't need to compare any numbers to verify our effect works with Hyperapp. We'll test if the action returns the effect we want instead.
The text was updated successfully, but these errors were encountered: