I'm trying to work out - unsuccessfully - what the recommendations are for dispatching an action based on the current state in the Redux store.
I have a setup where, upon logging in, the ID of the current user is stored under auth.currentUser.
I also have separately the details of users stored under users[id].
Firstly, I'd like my loadUser(id) action creator to not bother making API calls if the user details are already available. Secondly, I'd like to have a loadCurrentUser() action creator that makes the call to get the user based on that stored Current User ID.
What is the best way of doing this? I know I can do it relatively easily using redux thunk, but is that best or are there better ways? So far I've avoided using that - using redux promise middleware for my asynchronous actions instead.
Edit: To clarify what I want to do.
When my component loads, it will need to ensure that the current user details are loaded. It would do this by wiring in loadCurrentUser() to be dispatched.
Firstly, this action needs to load the user based on the ID stored elsewhere in the store. Secondly, it needs to only do that if this user with that ID isn't already in the store.
Dedicate a portion of your store to holding user data and connect that to whatever component is relevant. From there, you can make dispatch calls based on the status of your user. Lifecycle methods are a common place to put logic like this.
componentWillMount() {
if (this.props.user.id){ //assuming id is undefined if no user
this.props.dispatchAction()
}
}
Related
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
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.
If I use react with redux - does it always necessary to fetch data from the redux action? Or it depends if I need the data to be stateful?
For example: I have a container which displays a user profile page. Can I fetch its data from the componentDidMount?
Thanks.
Below a list of rules of thumb which could help you to determinate what kind of data should be put into Redux or in your component, I tried to write down few assumptions, but please in mind you know only the right answers :):
Is the data used by other part of your application? Probably in your case yes as a User Profile property like name could be used in different components, like the head of your site, profile details, basket.
Do you need to be able to create further derived data based on this original data?
Is the same data being used by multiple components? Probably yes.
Do you need to cache the data? Probably no.
Yes - this is how you would fetch it if there wasn't redux being used.
If you want to use redux (to make profiles available in general etc.), you should call the action from componentDidMount to properly handle lifecycle events etc.
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.