I have a big component, I store all the information about the selected items and all the information in the store(ngrx).
There are different types in this component and I can switch between them and change information about them(via inputs). And then, at the click of a save button, send all changes to the server.
What is the best way to put changed data in the store?
Send to the store during an onchange event (minus: a lot of dispatch)
Send when switching between types (minus: you need to somehow check the status of the type that has been changed right now before pressing the save button)
Or, in general, is it a bad practice to put elements that will change, and better store them to service?
And another question, is it correct to carry out calculations in a reducer. For example, I dispatch a list of elements in the store and I need to add a new element for the selected type. I can do this through the service, but then I need to pull out all the elements, the selected type and a few more parameters, and then perform actions and put the changed array back into the store. Or do all these actions in a reducer with known data.
Or in general, is this the wrong architectural approach to keep this list in the store?
Without knowing the use case it's hard to give a correct answer to this question, because it all depends on the needs.
As a rule of thumb, if the state only affects the current component, the ngrx store is not the place to store its data. An example of this is a form, it's usually an overkill to sync the form with the state in the store. That being said, if you need rehydration on the form, it's a good use case to keep the form and the store in sync.
The minus of dispatching a lot of actions, isn't a "real" downside of it imho - the ngrx store (redux in general) is designed to handle a lot of incoming actions.
To answer the second question, yes that's were reducers are for imho - it's here where I expect some logic. See the redux docs for more info.
You can also put some "view logic" inside the selectors, like filtering, sorting, paginination, ...
Mike and Brandon gave a talk at ng-conf and they explain what should belong in state and what does not. The talk gives useful insights, Reducing the Boilerplate with NgRx
Related
I have a collection of articles. A user sometimes edits or deletes an article. My app keeps track in the state which article is currently being worked on (call this the currentArticleId).
Now when a user edits or deletes an article, would I then give my action-creator the currentArticleId as an argument (e.g.: deleteArticle(this.props.currentArticleId)), or would I NOT do that and just fire the action creator without arguments (e.g.: deleteArticle()), and retrieve the currentArticleId from state in my action creator?
What's best practice here? Why would I choose one solution over the other?
If by state you mean Redux application state, then there is no right or wrong answer. You can even have two actions - deleteCurrentArticle() or deleteArticle(articleId) and use whatever suits you more.
However if you're asking about the component state, I think action creators should be decoupled from components. So I'd prefer passing the currentArticleId as an argument.
I would make your action creator more flexible by passing in an article ID, then your delete article action could delete the current article, or any other article it wanted to.
edit: I would like to add that there are usually better alternatives to keeping information like this in the global state. If you're only editing/deleting a single article on a page, then you could have a URL like article/{id}/edit and then grab the ID from there and pass it into what you need, or if you have a list of articles that each have editors, consider passing the article ID as a prop into the editor component.
Following the flux concepts we can get the next assertions for which I couldn't find explanations.
Every store will receive every action.
Why? My suggestion: since a store contents some business-logic we have to provide it with all possible changes and data so the store can decide what to do with them on its own.
The data in a store must only be mutated by responding to an action.
Why? My suggestion: the reason is violation of unidirectional data-flow in case of not responding to an action.
Every time a store's data changes it must emit a "change" event.
Why? I can't get this point.
Flux is just a way of managing the data flow of your application, so it is up to the developer to make sure this actually happens. But I'll try to paint a picture of why these concepts are a part of Flux.
Every store will receive every action.
If you have only one dispatcher in your application, every store will listen to actions dispatched through that dispatcher. It is up to you whether or not the store should act on the action dispatched, but to be able to react on it the store has to know of it.
Not all actions should lead to changes in a store, though. But the dispatcher simply doesn't care, because it won't know anything about the store implementation. It's just telling all stores that this action happened, do what you want with it or go on with your life without caring.
The data in a store must only be mutated by responding to an action.
You're right that doing it with a different approach could be violation of unidirectional data-flow. Doing things this way makes sure all parts of your application has the correct state based on the actions that happens.
By not doing it this way you would let go of one of the flux strengths. Update your store based on dispatched actions, and other stores will also be aware that the action happened, and thereby react to it if they want to. If you update the store directly you will end up having no clear picture of what parts of your application that are altering the state of your store.
Every time a store's data changes it must emit a "change" event.
People often describe the stores in a flux application as the source of truth. When a store's data changes, the basis for the visualization of your data changes. You want to be confident that if my store holds a certain value, this is what my application uses as it's data.
It's related to the first quote here. The store doesn't know if a listener depends on it's data. By emitting a change, it will let all listeners know that hey, I changed. Make sure you have all my latest changes. If you don't emit change, the listener could end up displaying something based on old data.
All of these statements are related to the same thing: If an action happens in your application, don't make any assumptions about which part of your application that wants to know the details of it. Make sure everyone can act on it, if they want to.
For example, if I have a login reducer and a user icon reducer. When I login, I want to update the user icon as well as storing the user's info. I am stuck between two choices:
First one is to export the USER_LOGIN action and have both login reducer and user icon reducer handle USER_LOGIN action.
Second approach is to have a 1 to 1 mapping between action and reducer (one type of action belongs to only one reducer). We have login reducer handle USER_LOGIN, then with Saga/Thunk we dispatch a side effect UPDATE_USER_ICON to the user icon reducer.
Which one is a better practice ? I personally favor the second approach.
Quoting the Redux FAQ entry on dispatching multiple actions:
There's no specific rule for how you should structure your actions. Using an async middleware like Redux Thunk certainly enables scenarios such as dispatching multiple distinct but related actions in a row, dispatching actions to represent progression of an AJAX request, dispatching actions conditionally based on state, or even dispatching an action and checking the updated state immediately afterwards.
In general, ask if these actions are related but independent, or should actually be represented as one action. Do what makes sense for your own situation but try to balance the readability of reducers with readability of the action log. For example, an action that includes the whole new state tree would make your reducer a one-liner, but the downside is now you have no history of why the changes are happening, so debugging gets really difficult. On the other hand, if you emit actions in a loop to keep them granular, it's a sign that you might want to introduce a new action type that is handled in a different way.
Try to avoid dispatching several times synchronously in a row in the places where you're concerned about performance. There are a number of addons and approaches that can batch up dispatches as well.
I would say your first approach is better and that is the one I use commonly. Yes it may affect readability of your code when you multiple reducers are acting upon same action type but it helps keep code less verbose (which is my major complain with React ecosystem) and a bit of performance. Actions like login would not have huge impact on performance but when I am making API calls on user actions, I just handle same action type in multiple reducers. For readability, I add comments and documentation.
Do you really need to store the user icon in Redux? A better approach in this case is to have a selector that returns the icon based on the logged in user. That way you can keep the minimal amount of state in the store.
You can use the Reselect library to cache selectors.
I am a bit lost on what to keep in the state tree of Redux.
I saw two conflicting statements on what to store in the state tree(s).
React doc tell us that only user input should be stored in state trees.
The original list of products is passed in as props, so that's not state. The search text and the checkbox seem to be state since they change over time and can't be computed from anything. And finally, the filtered list of products isn't state because it can be computed by combining the original list of products with the search text and value of the checkbox.
Redux doc tells us that we often should store UI state and data in the single state tree:
For our todo app, we want to store two different things:
The currently selected visibility filter;
The actual list of todos.
You’ll often find that you need to store some data, as well as some UI state**, in the state tree. This is fine, but try to keep the data separate from the UI state.
So React tells that we should not store data (I am talking about data of the todos) and, for me, Redux tells the opposite.
In my understand I would tend on the React side because both React and Redux aims to predict a UI state by storing:
all what can't be computed (eg: all human inputs) and are part of the UI:
checkbox value
input value
radio value
...
All minimal data that could be use to build a query and send it to the API/database that will return the complete user profil, friends lists, whatever...:
user Id
creation dates range
items Ids
...
For me that excludes all database/API results because:
that stands on data level
could be computed by sending the right (and computed by pure reducers) query.
So what is your opinion here?
React documentation about the View Component state, but Redux documentation about the Application state. So, there is no conflicts between definitions.
If we talk about Redux - you make all your components without state (and transform stateless root component to stateful with help of react-redux's connect function). If you have large response from the server and you show your data with pagination / filters, you can treat your application state as what you see on screen and not put all data in Redux store, only what you need to render (for example, 100 rows to show page and total number of rows to show pagination). There is no restriction for this. The whole data you can put into another place. For example, in another data container in web-worker (I make a full request in web-worker and fetch from there only needed data to display).
Added after question edited:
The original list of products is passed in as props, so that's not state.
In that example, the reason why list of products isn't state - it's already in props. It means that the one of parent components have this as state.
I feel that the problem is that originally Redux was pushed really hard, and some people were so purists, that they argued for separating everything to Redux and re-rendering the whole application on every change. And then we ended up with this response of the creator, which actually only added a confusion, because redux was and still is a de-facto standard for new react applications, and a lot of tutorials assume it.
So, I feel that people are pressured from each side, and often they do some things without real understanding why they should (especially newcomers creating constants, actions and reducers). So, for those who read it, please start without redux, and keep it just in local state (but try to keep in some component like DataContainer).
For those who need redux, rule thumb is to put all async data (so all requests go through redux), and data which is needed for independent components. If components obviously located nearby, keep it in a local state and pass as props.
Redux is a very helpful library, but it's power is needed only after you start to have at least several routes, different query options and some complex UI. Before that there is a good chance that you are overengineering (but, of course, if you are sure that you will exceed this size, feel free to start with Redux). And again, you'll literally never will want to put your slider or small dropdown position in the store -- react's state serves perfectly for it.
I have an app that uses a sync API to get its data, and requires to store all the data locally.
The data set itself is very large, and I am reluctant to store it in memory, since it can contains thousands of records. Since I don't think the actual data structure is relevant, let's assume I am building an email client that needs to be accessible offline, and that I want my storage mechanism to be IndexedDB (which is async).
I know that a simple solution would be to not have the data structure as part of my state object and only populate the state with the required data (eg - store email content on state when EMAIL_OPEN action is triggered). This is quite simple, especially with redux-thunk.
However, this would mean I need to compromise 2 things:
The user data is no longer part of the "application state", although in truth it is. Since the sync behavior is complex, and removing it from the app state machine will hurt the elegance of the redux concepts (the way I understand them)
I really like the redux architecture and would like all of my logic to go through it, not just the view state.
Are there any best-practices on how to use redux with a not-in-memory state properties? The thing I find hardest to wrap my head around is that redux relies on synchronous APIs, and so I cannot replace my state object with an async state object (unless I remove redux completely and replace it with my own, async implementation and connector).
I couldn't find an answer using Google, but if there are already good resources on the subject I would love to be pointed out as well.
UPDATE:
Question was answered but wanted to give a better explantation into how I implemented it, in case someone runs into it:
The main idea is to maintain change lists of both client and server using simply redux reducers, and use a connector to listen to these change lists to update IDB, and also to update the server with client changes:
When client makes changes, use reducers to update client change list.
When server sends updates, use reducers to update server change list.
A connector listens to store, and on state change updates IDB. Also maintain internal list of items that were modified.
When updating the server, use list of modified items to pull delta from IDB and send to server.
When accessing the data, use normal actions to pull from IDB (eg using redux-thunk)
The only caveat with this approach is that since the real state is stored in IDB, so we do lose some of the value of having one state object (and also harder to rewind/fast-forward state)
I think your first hunch is correct. If(!) you can't store everything in the store, you have to store less in the store. But I believe I can make that solution sound much better:
IndexedDB just becomes another endpoint, much like any server API you consume. When you fetch data from the server, you forward it to IndexedDB, from where your store is then populated. The store gets just what it needs and caches it as long as it doesn't get too big or stale.
It's really not different than, say, Facebook consuming their API. There's never all the data for a user in the store. References are implemented with IDs and these are loaded when required.
You can keep all your logic in redux. Just create actions as usual for user actions and data changes, get the data you need and process it. The interface is still completely defined by the user data because you always have the information in the store that is needed to GET TO the rest of it when needed. It's just somewhat condensed, i. e. you only save the total number of messages or the IDs of a mailbox until the user navigates to it.