I have a React-Redux app where I have several tabs, and I keep my code in a structure of folder-per-tab. Each folder contains an actions file, service file, constants file and a reducer file.
When I fetch the data from the server, I fetch it as one big nested object, whose top level keys are sectionA, sectionB, sectionC and so on.
Each tab may use data from multiple sections, for example, tab 1 may use sectionA and sectionB, tab 2 may use sectionB and sectionC and so on.
This creates a problem in the way I split the data into reducers. If the top level keys in the redux store will be "tab1" and "tab2", and I would want to update data in sectionB, then I will have to do it in two different reducers. On the other hand, if the top level keys would be "sectionA", "sectionB" etc, then my folder structure is wrong. Any way to solve this?
Thanks.
It sounds like you are thinking very much like a front-end developer, and categorising your state according to how it relates to the user interface.
You might want to think about how you are normalizing your state shape:
https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape
Redux is really a tiny backend for your front-end. I'm sure the purists will debate this on a million levels, but it actually functions like a little, local document store.
Try thinking about your redux structure more in terms of what the data is, than where you want to put it on the screen.
the normalizr library is some next level-ness for that
https://github.com/paularmstrong/normalizr
I'm still debating whether I think it's too far. My app is starting to turn from an MVVC into an MVCMVCCVMMV... (you get it, some kind of epic roman numeral).
How much data do I want to keep in a pubsub model locally, vs always hitting my API server for that?
How long does a user leave a page open, filling the redux store up with new data until there's a memory problem?
Garbage collection in redux is a whole extra conversation, and this is worth a read: https://github.com/reduxjs/redux/issues/1824
Old mate Dan Abramov jumps in with some useful thoughts on that thread.
I realise none of this is an answer per-se, but it seems like redux has more 'use case scenarios' than answers generally anyway.
Related
I was always wondering if I should use redux store all the time even when it's not really necessary.
For example:
I have form with select field that has some options I fetch from API. Let's imagine that form is for adding new car listing and select options are car models. Should I directly call API from component and display options or should I create store CarModels or something like that and store results from API there and then map those values to state in component?
I have "Favorites" feature, when you click heart next to some item (let's say a car), do I need to go through dispatching all events FAVORITE_CAR_REQUEST, FAVORITE_CAR_SUCCESS etc... or is it good enough to just call API directly from component (using some kind of api-service of course).
It's related to question above. If I have screen where I show favorites, I should then probably have store and connect everything with actual favorite action so I update the list. On new favorite, API will return favorited item, should I push that one in list of favorites already in store or should I just load again latest list when user opens up the favorites screen?
Thanks in advance.
I think this is a perfectly valid question. What I feel like you're trying to ask is if you could/should mix react state and the redux store. The answer is sure! Just think about where you need to use that part of state before deciding where to store it. If you need a part of the state in multiple components, it probably makes sense to use Redux. If you only need state locally, perhaps to set form validation errors, maybe use react's state management if you feel like it. React and redux are both meant to be flexible, so as long as you're consistent in when you use the redux store and react state you should be good.
I found this article that also explains this pretty well: https://blog.jakoblind.no/is-using-a-mix-of-redux-state-and-react-local-component-state-ok/
I tend to use redux when the state has to be accessed globally / complex logic that i want to be logged properly
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.
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;
As a Redux beginner, given (the idea of) a somewhat larger application I imagine a root reducer similar to:
const rootReducer = combineReducers({ accounting, crm, sales })
Application state in this case would contain accounting, crm, and sales even if the user is only using one part of the application. This may be advantageous, for example as a cache when switching back and forth between accounting and CRM, but you probably do not want to retain all data of all views that have ever been opened, that is the complete possible state tree without any pruning, within the application forever or even initializing the whole tree to its initial state on load.
Are there idioms, patterns or libraries which solve this or am I missing something?
As a partial solution which solves retaining all the data I imagine something like resetting parts of the state to their initial state when navigating away from certain parts of the application given some declarative rules such as:
set accounting = {} (indirectly, through an action such as ACCOUNTING_LEAVING) when <Accounting/> receives componentWillUnmount
delete/set crm.mail = {} when <MailEditor/> receives componentWillUnmount
I have not seen examples which clean up the state in any way. Many "list + detail view" examples store state like { list: [...], detail: {...} }, but when switching to the detail view the list is neither emptied, nor nulled, nor deleted. This is nice when I might return to the list view a couple of moments later, but not when using an application 9 to 5 without ever releasing data.
A few related thoughts:
Unless you're expecting dozens or hundreds of megabytes of data to be cached in your store, you're probably worrying about this too soon. Write your app, benchmark, and then optimize.
Dispatching actions to clear out portions of the store is entirely valid and reasonable.
My Redux addons ecosystem catalog may have some libraries listed that would be useful. In particular, the Component State and Reducer pages point to some libraries that do things like dynamically adding and removing reducers from your store, and resetting state.
You may also be interested in my collection of high-quality React and Redux tutorials.
Overall, how you organize your state, when you update it, and what you update it with is up to you. Redux simply provides a pattern and rules for the process of doing the updates.