I am writing a little "fun" Scala/Scala.js project.
On my server I have Entities which are referenced by uuid-s
(inside Ref-s).
For the sake of "fun", I don't want to use flux/redux architecture but still use React on the client (with ScalaJS-React).
What I am trying to do instead is to have a simple cache, for example:
when a React UserDisplayComponent wants the display the Entity User with uuid=0003
then the render() method calls to the Cache (which is passed in as a prop)
let's assume that this is the first time that the UserDisplayComponent asks for this particular User (with uuid=0003) and the Cache does not have it yet
then the Cache makes an AjaxCall to fetch the User from the server
when the AjaxCall returns the Cache triggers re-render
BUT ! now when the component is asking for the User from the Cache, it gets the User Entity from the Cache immediately and does not trigger an AjaxCall
The way I would like to implement this is the following :
I start a render()
"stuff" inside render() asks the Cache for all sorts of Entities
Cache returns either Loading or the Entity itself.
at the end of render the Cache sends all the AjaxRequest-s to the server and waits for all of them to return
once all AjaxRequests have returned (let's assume that they do - for the sake of simplicity) the Cache triggers a "re-render()" and now all entities that have been requested before are provided by the Cache right away.
of course it can happen that the newly arrived Entity-s will trigger the render() to fetch more Entity-s if for example I load an Entity that is for example case class UserList(ul: List[Ref[User]]) type. But let's not worry about this now.
QUESTIONS:
1) Am I doing something really wrong if I am doing the state handling this way ?
2) Is there an already existing solution for this ?
I looked around but everything was FLUX/REDUX etc... along these lines... - which I want to AVOID for the sake of :
"fun"
curiosity
exploration
playing around
I think this simple cache will be simpler for my use-case because I want to take the "REF" based "domain model" over to the client in a simple way: as if the client was on the server and the network would be infinitely fast and zero latency (this is what the cache would simulate).
Consider what issues you need to address to build a rich dynamic web UI, and what libraries / layers typically handle those issues for you.
1. DOM Events (clicks etc.) need to trigger changes in State
This is relatively easy. DOM nodes expose callback-based listener API that is straightforward to adapt to any architecture.
2. Changes in State need to trigger updates to DOM nodes
This is trickier because it needs to be done efficiently and in a maintainable manner. You don't want to re-render your whole component from scratch whenever its state changes, and you don't want to write tons of jquery-style spaghetti code to manually update the DOM as that would be too error prone even if efficient at runtime.
This problem is mainly why libraries like React exist, they abstract this away behind virtual DOM. But you can also abstract this away without virtual DOM, like my own Laminar library does.
Forgoing a library solution to this problem is only workable for simpler apps.
3. Components should be able to read / write Global State
This is the part that flux / redux solve. Specifically, these are issues #1 and #2 all over again, except as applied to global state as opposed to component state.
4. Caching
Caching is hard because cache needs to be invalidated at some point, on top of everything else above.
Flux / redux do not help with this at all. One of the libraries that does help is Relay, which works much like your proposed solution, except way more elaborate, and on top of React and GraphQL. Reading its documentation will help you with your problem. You can definitely implement a small subset of relay's functionality in plain Scala.js if you don't need the whole React / GraphQL baggage, but you need to know the prior art.
5. Serialization and type safety
This is the only issue on this list that relates to Scala.js as opposed to Javascript and SPAs in general.
Scala objects need to be serialized to travel over the network. Into JSON, protobufs, or whatever else, but you need a system for this that will not involve error-prone manual work. There are many Scala.js libraries that address this issue such as upickle, Autowire, endpoints, sloth, etc. Key words: "Scala JSON library", or "Scala type-safe RPC", depending on what kind of solution you want.
I hope these principles suffice as an answer. When you understand these issues, it should be obvious whether your solution will work for a given use case or not. As it is, you didn't describe how your solution addresses issues 2, 4, and 5. You can use some of the libraries I mentioned or implement your own solutions with similar ideas / algorithms.
On a minor technical note, consider implementing an async, Future-based API for your cache layer, so that it returns Future[Entity] instead of Loading | Entity.
Related
I'm currently working on a single page dapp for lucky bunny farm, it all works and will be released soon. But i'm wondering if maybe i've engineered it entirely wrong because i have put most of the business logic within a collection of useEffects (6). Because i dont have any other pages on the site i've not created hooks as i would in other situations because there's no benefit from a writing code point of view.
It seems to me that having 6 useEffects working off the state variables i've set might be a little much, is there a performance gain from reducing them or doing something else? I have created components for the various on page assets and those internally have their own state and useEffect hooks too. What's the danger in a "large" number of these hooks and at what point should i consider optimising for performance.
Or am i over thinking this and actually this is quite normal and i should only worry when i have say 20 hooks all doing their own thing.
Additionally, i tend to use state for single variables instead of objects, is there a performance gain from using a single state with an object.
So to use an example i have the following states:
dappActionStarted
dappActionCompleted
dappActionError
Would it be more efficient to have something like:
{
started
completed
error
}
And then do a single useEffect for that object as opposed to the three i would need to manage 3 seperate state vars.
I'm developing a software which is drawing some elements on the screen which is using by mechanical engineers.
I'm string my project data in reducer store. This project data has tons of objects, arrays etc. I mean for each element on the screen, there is a data stored in project.
When user makes an action, I must recalculate project and set it to redux store again for example;
...
case SET_ACTIVE_UNIT:
let unit = action.unit;
project = state.get('project').toJS(); //I'm using immutable
project = ProjectLogic.addActiveUnit(project, unit, action.shiftKey);
return state.set('project', fromJS(project));
...
Ok, you will say that this kind of usage is not right. Because I'm reading all data and reseting it to reducer whole data. You will advice me to use state.setIn but it is really imposible. Beacuse in addActiveUnit function will recalculate project, %20 of project data will be changed. So, I can't handle this change state.setIn
My problem starts here; if there is 60-80 elements drawing on the screen after return state.set('project', fromJS(project)); rendering performance slows down. Every new items gets it worse.
How can I handle this problem?
Thanks all
As a general observation, toJS() is considered to be the most expensive API in Immutable.js, and should be avoided as much as possible.
My initial advice would be to not use Immutable.js.
Instead, you might want to look at using immer to handle the immutable update logic.
Also, our new redux-starter-kit package uses Immer internally.
Beyond that, I'd suggest doing some profiling to see where exactly the perf bottlenecks actually are.
This is not a React specific question. I'm thinking of implementing Flux in Aurelia/Angularjs.
While reading up on flux, I'm not convinced of the need of the dispatcher step. Why can't a component call the store directly to update and retrieve data? Is there anything wrong with that approach?
For example: If I have a CarStore that can create new cars, update cars and get a list of cars(just a thin layer on the CRUD api), I should be able to retrieve/update the list by directly calling the store from the car-grid component. Since the store is a singleton, whenever the list updates, car-grid should automatically get the new items. What is the benefit of using a dispatcher in this scenario?
I've created several large apps using React-native with Redux as the store / view state updater.
The dispatch action is synchronous regardless. There's a big disadvantage to using dispatchers, you lose the function signature. (Debugging, auto-catching type-errors, refactoring lost, multiple declarations of the same function, list goes on)
Never had to use a dispatcher and its caused no issues. Within the actions we simply call getState().dispatch. The store is a singleton anyhow, it's heavily recommended you don't have multiple stores. (Why would you do that...)
You can see here why are dispatchers important (check out the section Why We Need a Dispatcher). The way I see it, the idea is basically being able to access to various stores in a synchronous way (one callback finishes before another one is called). You can make this thanks to the waitFor method, which allows you to wait for a store to finish processing an action (or more tan one). There is a good example in the docs. For example, your application may grow and instead of having just that CarStore you have another Store whose updates depend on the CarStore updates.
If you will only ever have one store, then a dispatcher is redundant in my opinion. If you have multiple stores however, then a dispatcher is important so that actions don't need to know about each of these stores.
Please note that I am not saying that you should ditch the dispatcher if you only have one store. It's still a good pattern as it gives you the option of supporting multiple stores if you ever need to in the future.
I'm working on a project which has only been going a very short time. There are few flux stores in place already which manage different aspects of the application state and are relatively independent.
I have 2 questions :
Some of the stores that exist are emitting more than one type of change event. Is this indicative of the stores handling too much unrelated data that should be in separate stores or is this a usual situation?
We need to write a React component that is dependent on more than one of the stores that already exist, and also needs to query the server to get some specific information to render on the page which it will then allow the user to modify. So, before this component can render, it needs to ensure all the stores contain what they need to and issue actions to populate anything that is missing. My question is about how to handle this. Would it be better to create a new store that fetches the specific data required by the component and is dependent on the other stores (using the usual flux store dependency rules), or to have the component knowing which specific stores it is dependent on directly.
For first part of your question: it depends. Flux doesn't force you to follow strict set of rules. Redux for example, uses only one store for everything. I worked on project where almost all components had there own store and another one where we had single store per view that handled data model and all additional states. Without knowing specifics of your project (size, complexity etc.) I can't recommend one over the other. I would probably go with minimum number of stores that make sense to you and your team and refactor as needed (ie. when you feel it handles too much or single file contains too much of unrelated code). Whatever works best for your situation and makes you most comfortable.
For other part: since you want component to render only after data for all stores is populated I would introduce new store to handle server data and use Dispatcher’s waitFor method to define dependencies. If you opt to use stores directly you could then render component using some kind of intial state with loading spinner over parts that are missing or disable user editing and, once data is fetched, update state to display rest of the data/enable editing. This requires more code but can result with better UX. Again, it all depends on your needs.
An application that I develop was initially built with Flux.
However, over time the application became harder to maintain. There was a very large number of actions. And usually one action is only listened to in one place (store).
Actions make it possible to not write all event handler code in one place. So instead of this:
store.handleMyAction('ha')
another.handleMyAction('ha')
yetAnotherStore.handleMyAction('ha')
I can write:
actions.myAction('ha')
But I never use actions that way. I am almost sure, that this isn't an issue of my application.
Every time I call an action, I could have just called store.onSmthHappen instead of action.smthHappen.
Of course there are exceptions, when one action is processed in several places. But when that happens it feels like something went wrong.
How about if instead of calling actions I call methods directly from the store? Will my application not be so flexible? No! Occurs just rename (with rare exceptions). But at what cost! It becomes much harder to understand what is happening in the application with all these actions. Each time, when tracking the processing of complex action, I have to find in stores where they are processed. Then in these Stores I should find the logic that calls another action. Etcetera.
Now I come to my solution:
There are controllers that calls methods from stores directly. All logic to how handle action is in the Store. Also Stores calls to WebAPI (as usually one store relating to one WebAPI). If the event should be processed in several Stores (usually sequentially), then the controller handles this by orchestrating promises returned from stores. Some of sequentials (common used) in private methods of itself. And method of controllers can use them as simple part of handling. So I will never be duplicating code.
Controller methods do not return anything (one-way flow).
In fact the controller does not contain the logic of how to process the data. It's only points where, and in what sequence.
You can see almost the complete picture of the data processing in the Store. There is no logic in stores about how to interact with another stores (with flux it's like a many-to-many relation but just through actions). Now the store is a highly cohesive module that is responsible only for the logic of domain model (collection).
The main (in my opinion) advantages of flux are still here.
As a result, there are Stores, which are the only true source of the data. Components can subscribe to the Stores. And the components calls the same methods as before, but instead of actions uses controller. Interaction with React did not change at all.
Also, event processing becomes much obvious. Now I can just look at the handler in the controller and all becomes clear, and it's much easier to debug.
The question is:
Why were actions created in flux? And what are their advantages that I have missed?
Actions where implemented to capture a certain interaction on the view or from the server which can then be dispatched to as many different stores as you like. The developers explained this with the example of the facebookchat.
There is a messageStore and a threadstore. When the action eg. messagePost was emitted it got dispatched into both stores doing different work to update their attributes. Threadstore increased the number of unread messages and messageStore added the new message to its messagearray.
So its basicly channeling one action to perform datachanges in more than one store.
I had the same questions and thought process as you, and now I started using Flummox which makes it cleaner to have the Flux architecture.
I define my Actions in the same file where I define my Store, and that's close enough. I can still subscribe to the dispatcher to log events so I can see all actions being called, and I have the option to create multi-store Actions if needed.
It comes with a nice FluxComponent that lets you wrap all store-related code in a single place so its children are stateless components that get updated on store changes, like
<FluxComponent connectToStores={['storeA', 'storeB']}>
<InnerComponent />
</FluxComponent>
Keeping the Flux architecture (it uses Facebook's Flux behind the scenes) will hopefully make it easy to use other technologies like GraphQL.