In some parts of my app,
I have a component using a fragment.
but up the tree somewhere, i sometimes need to create temporary objects waiting to be synced with the server (once they will they are transformed as plain relay objects).
But until then, they are only a few objects passed down the tree of component.
The createFragmentContainer + some #relay(mask: false) make all this possible.
But I would like to make it a bit cleaner using useFragment.
Usefragment doesn't like at all the "fake fragment" data.
Is there a way to somewhat legibly hydrate a fragment from data not directly coming from a query?
or maybe another way?
The best idea seems to go with commitLocalUpdate
you can even create new types for your need
https://relay.dev/docs/v10.1.3/mutations/#using-updater-and-optimisticupdater
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.
For my client I'm creating something like quiz web app in react with redux based on websockets (socket.io) with a huge, very unique data. No user interaction, just presentation layer. It works like this: I get websocket event with url to my layout and payload data, and then I render given url and fire redux action with data as argument, which becomes app's state. Simple as that. BUT I noticed that on first render initial state is loading, not given from websocket as argument to action. As I said data I get is huge and unique so I didn't want declare in reducer something like this:
pageData: {
assets: [],
questions: [],
details: []
And so on. It's much more complicated btw it's just an example. Instead of this I made something like this:
pageData: {}
And I was hoping that on view (using connect) I can get this data like this:
this.props.view.pageData.questions
But then it turned out that I can not get this because it's undefined on first render. So my questions are:
Is there a way to access to this data on first render without
declaring whole structure?
If not, should I reconstruct given data in reducer?
Should I then create reducers for each page (there are like over 20 views
with unique data)
Of course I can declare everything in reducers but I feel it's very hard to maintain so much data.
But you know, maybe I'm just too lazy and I should declare initial state for each page and this question does not have sense ;).
I think you may have a few options here:
Define fallback data in your components if undefined
Don't render your page (or components) until you have received the data
Define your initialState explicitly as you already suggested
All or most your components expect or should expect data of a certain kind and in a certain format. For this reason, laying out the structure beforehand (#3) seems to be most appropriate. Ask yourself this: would my app still display correctly if the format of the web socket event data changes?
To answer your questions specifically:
Is there a way to access to this data on first render without
declaring whole structure?
Yes, you could use the || operator in your bindings to fall back (#1) to an empty array or object or value. Example <MyComponent listOfItems={this.props.items || []}. This effectively creates an empty state, however, IMO this should be standardized in the reducer/store with initialState.
Should I then create reducers for each page[?]
Not necessarily a reducer for each page, but a store with all pertinent data to your application. It is hard to say for sure without knowing more about the architecture of your app, but keeping small, well defined chunks of information is generally easier than one big blob.
I strongly advocate defining your data beforehand. It might sound cumbersome at first, but it will pay off greatly and helps others understand what the app might look like with live data.
that's because you haven't added default case in reducer
default:
return state;
One pattern I've seen recommended is to use selectors to where possible to hide the shape of the store. That way if you need to update the shape of the store, you should be able to get away with only updating your selectors, and not other parts of the application.
However the same problem arises with the use of models within the state.
As one of many examples, let's assume I'm building a file system in Redux. I have a list of files which can either be a directory or a file.
My store might have a fileList property which contains an array of file ids as well as a files object which maps fileId to a file object.
Let's say I have a list of files and I want to, depending on whether it's a file or directory, have a different Item component (i.e. DirectoryItem and FileItem).
One way to achieve this is to do something like:
{
files.map(file => {
file.type = 'directory' ?
<DirectoryItem key={file.id} ...file /> :
<FileItem key={file.id} ...file />
)}
}
(or I could create a higher-order FileListItem component, for example, that does the check and renders either the DirectoryItem or FileItem)
However this might not be ideal because now my component needs to know the structure of the file object. I might want to add a different type of object (i.e. a shortcut file or shared file) and might decide that a type property isn't how I want to represent my data anymore. As such, I'd need to go and update all my components, etc.
If I were doing this in Backbone, for example, I would've probably chosen to define an isDirectory() function on my model, however that doesn't seem to be the Redux way of doing things.
One possible solution I can think of is creating a FileUtils helper class which exports an isDirectory method and takes a file object as a parameter.
Another option will be creating an isDirectory selector which takes a file id as a prop, doing something like:
(files, props) => state.files[props.fileId].type == 'directory'
If I were to create the selector, I suppose I would need to create a higher-order component to call the selector from.
Just wondering if either approach is recommended in Redux? Am I missing another approach that could help solve this issue?
The functional way of doing things simply prescribes tearing the method off of the object and calling it a function.
The recommended way to call it is to instead of having a this, simply pass a regular parameter. This is not a requirement. You can just use call or apply. That may seem real strange in js, but this may change soon with a new :: operator.
Now, you can give this function anything you like to help it get its data.
In your example
(files, props) => state.files[props.fileId].type == 'directory'
You pass it state (naming mistake there) and props and then use this info to come up with an answer. But you could instead choose to pass it a directory entry object. No need to go fetch it from state.
Note that this makes it very close to a method.
isDirectory = entry => entry.type === 'dir';
Now, because it's not getting state, it isn't selecting anything from state and is therefore not a selector.
However, it's plenty functional in nature. There really is no need or use to make life more complicated than that. Adding a higher order component or trying to shoehorn our problems into a more Redux-y way of doing things is needlessly complicating matters.
Selectors are recommended for selecting state so state usage is not tied to state shape. It's an abstraction layer, separating your mapStateToProps from your reducers.
Selectors are now considered part of the Redux Way, but that wasn't always true. And so, at your discretion, being informed of why something is done the way it is, you can then choose to not use it.
And, at your discretion, you can choose to substitute the current trend with your own version. It is highly recommended to do this, of course after consideration of alternatives.
Often the best solution is the one you come up with yourself. Being the most informed about your problem domain, you are uniquely qualified to formulate a matching solution.
Those who have developed great ideas that all of us feed off of and get inspiration from will probably move on from their viewpoint when something better comes along.
There isn't (and probably shouldn't be) a sacred paradigm. Everything is eligible for reconsideration. Occam's razor dictates that the simplest answer is most likely the right one.
And Redux is very much about simplicity. So to do things the Redux Way is mostly about doing things the straightforward way.
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.
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.