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.
Related
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.
I have at least two approaches to solve a problem.
Repreform calculations inside views on each update (map, filter, find)
Keep an "CurrentState" object inside my redux state.
I choose to create a CurrentState object that stores my calculated result (It calculates the results inside the reducer function).
Approach 2 saves processor calculations, but it feels dirty.
is it considered an antipatern to have a CurrentState object (updated from the reducer) inside the state?
Thank you in advance!
Not totally clear from your question - but I assume that you are talking about situation, that you can compute "result" from other data in your store and asking if storing it (redundantly) in store is good pattern.
Well, generally I would not recommend it as keeping redundant data in store can lead to inconsistent data bugs - if you forgot to keep your data in sync (e.g. when you or someone else working on the project would change one data and forget to change the order place).
The standard patter for this is usually to use selectors and memonization (see https://github.com/reactjs/reselect). The idea is to keep only normalized data in your store but to use selectors in your views. Selectors are responsible to "cache" (memonize) extensive computation. Another advantage of selectors is that it provides level of indirection for your application and you can easily evolve your state (as the project grows) without affecting your whole application. On the other hand for small and one time projects selectors can be just "another" boilerplate - but in general I recommend to use them.
This was a hard anti-pattern.
If you are reading this chances are you don't fully understand redux and I would like to recommend
https://github.com/leoasis/redux-immutable-state-invariant
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.
What's the best way to handle a case where multiple components are using one store (which is populated by API calls in an action creator), but each component may need to access different sets of data without effecting the other. I.e.: two table components display data from the WidgetStore; one table wants all widgets, the other only displays widgets whose name contains "foo" (this would be based on user input). The table being queried via the API has tens of thousands of widgets, so loading them all into the store and filtering from the store isn't practical. Is there a Flux architecture (like Redux) that already has a way of handling this type of thing?
The simplest way is to just create a parent component and selectively hand off data, using a pluck or selector function, to each of the children.
For the more general answer for yourself going forward... if you follow something along the lines like redux there is already proven patterns which will help you understand passing complex data down.
According to Flux Architecture View uses Action to call Dispatcher that updates the Store, while View listening to Store change events.
My question is: Why do we need Store?
In order to list all users, my Component will call ListAllUsersAction that will in turn call my API and will update the Store with the result of API call. Store then emits change event that the View is listening to. But the store also stores the result. Why? Why this middle layer is needed? I wont call the store directly anyway, so this cache layer makes no sense to me, and as I generate more events that loads more data, eventually all my stores will have all the state of my application because flux architecture says nothing about cleaning the Stores.
Am I missing something?
The goal of Flux is to keep data flow easy to understand even as apps becomes large and complex, such that a new person can be brought up to speed quickly, figure out what's going on by inspecting the source code, and be confident that they can make changes without breaking things. Modularity and separation of concerns are a big part of that. The Stores are a way to keep the data models independent of the details of the view layer and establish a single source of truth for the application state. You can look at any Store's code and see what data it holds, what actions it responds to, which dependencies it has for data in other Stores. It's a matter of organization for the sake of the developers, at the cost of code being slightly less compact.
In order to list all users, my Component will call ListAllUsersAction
that will in turn call my API and will update the Store with the
result of API call.
Since the function of Actions is mainly to provide updated data to stores, you could also just call the API first and then just create one Action to handle the result.
as I generate more events that loads more data, eventually all my
stores will have all the state of my application because flux
architecture says nothing about cleaning the Stores.
Holding the current state of the application is the intended function of the Stores. If user actions or API calls cause the data to change, the Actions notify the Stores and the Stores responsible for keeping that data update accordingly (maybe even being reset to null). There's no need for any other sort of cleaning, because the Stores "having all the state" is exactly what they're supposed to be doing.
Stores are in charge of application state and logic, so for example, let's say you fetch all users through your ListAllUsersAction, you get an array from your API
var users = [{firstName: 'LIMELIGHTS'}, {firstName: 'SKWEE357'}];
Now, the users name are apparently capitalised as your API decides that this is the way to deliver the data.
This just won't do so you want to fix it.
Using just React or just the Action where would you put this code, where would it make sense?
In your view, your dispatcher or your action? No, you definitely don't want to clutter your React component with this type of logic.
Nor does it make sense to do this data manipulation in the Dispatcher or Action, they are after all just notifiers that something should happen.