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.
Related
Note: I am not using Redux (only Context + Hooks)
Introduction
I have a screen "Profile", that renders a TabView.
This TabView, renders two components as scenes:
UserPosts
UserInformation
My Profile screen, fetches the user data from my database, as it has to render some stuff that depends on this data.
Also, it is passed down via props to my "UserInformation" component, as it just renders the user data. So... there is no data fetching in my "UserInformation" component.
In the other hand, my "UserPosts" component is responsible of fetching the user posts, as it renders a component "UserPostsGrid" which renders those posts with pagination (endless FlatList).
Note: Data fetching is performed in custom hooks, not directly inside
the component (also, those hooks manages the respective stateful
data).
Problem
The main problem comes when I need to refresh the user data and the user posts when the user pull-to-refresh inside the "Profile" screen.
There is no problem for this screen to fetch the user data, as I have said before the "Profile" screen is responsible of that action.
But the posts are the stateful data of my child...
Multiple solutions?
I have thought to solve this problem using the current solution:
React.forwardRef() + React.useImperativeHandle()
With this, I will be able to pass a ref to my "UserPosts" and use imperative programming for accessing the data fetching method: userPostsRef.current.refreshPosts();
I have never seen other scenarios for solving this problem like this, but it should work. Instead, other coders implement a ContextProvider to handle all the data and be able to access it wherever they want. The main problem I notice here is: extra memoization + extra re-renders.
Another solution might be to implement all the data fetching inside the parent, and pass the respective callbacks via props to the child. The main problem I notice here is that we are creating some kind of "God Component" and we might have multiple lines of code...
My question
Is it an anti-pattern or a bad practice to implement the first solution ?
To answer your question:
Yes, it is an antipattern in React, because React is built around the idea that you specify the state of your app, and then let React render the components depending on this given state.
If something should change, you are supposed to specify a new state that represents the difference to the old state.
Refs should only be used if there is no other option. Basically, if you use Refs you say "I don't want to use React for this specific part of my code".
As always, imperative code using refs should be avoided in most cases
To use state instead of commands:
I would approach this kind of problem by trying to translate the imperative action into some state of information.
The user says "get me the latest data", that is a command, i.e. imperative.
Now I would ask "what is the user telling me about the state of the data right now ?"
An answer would be "this data is not up to date anymore", and this can be expressed as a state, and passed around via props.
A solution for your problem:
E.g. you might use a prop needsUpdate, which is set to true when the data should be updated, and to false when fetched.
To avoid unnecessary renders (setting needsUpdate = false would cause another call of e.g. useEffect, even if nothing happens there), I probably would use a child component prop like latestUpdateRequest and a child state like latestUpdate which holds a counter or timestamp, and then compare if(latestUpdateRequest > latestUpdate).
To inform the parent about the change just pass a callback function like dataUpdated() as a prop.
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'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()
}
}
In one of my React components, I'm using componentWillMount() to call a function I passed in as a prop via react-redux. This function dispatches an action that redux-saga listens on to load search filters for a form.
Now that I'm using redux-persist and the state rehydrates automatically, I would like to check if these search filters exist and were hydrated before calling my function. There's not much point in dispatching an action to retrieve data that's already in state and ready to use.
What is an idiomatic way to do this? Should I just check the array length on each prop in an if statement and only call the function if they exist already? Or should I be leveraging redux-saga anyway?
Edit: Writing business logic inside the Action Creator seems like an anti-pattern, but it's an option. Also, writing custom Redux Middleware is also an option. Will investigate both and leave an answer if necessary.
Edit 2: I'm not going to answer my own question, because the solution I found doesn't strictly relate to this question. I discovered redux-logic and it's been perfect for my use case.
I am using redux with react to manage the state of a complex form, which will be a single object. My form will have nested data structures which the user may edit, so for example : Applicant.Address.Country would be updated by a textbox on my form. I had the idea of making an action called UPDATE_MODEL where updates could be made by passing in objects like :
{Applicant: {Address: {Country: 'France'} } }
Which would get merged with the state by the reducer. The dispatch to update the model would need to be passed down to every single form field, as well as a prop for telling the component where it fits in the entire tree of the form. Do I have any alternatives to not passing down these two props throughout the form? It just sounds messy.
Yes, you do. You can use React context.
React Context is specifically meant for this.
This link (to ReactJS docs) has a nice example. But this is an experimental feature, so use it with caution.
This link (to a blog) has a nice summary of when to use it and when not to use it.