I have one file loading ui that I call whenever my app's components fetch data from the backend so the frontend can show loading...
The issue is when one component is fetching data, I dispatch loadingData() which causes the other components to showing loading... as well. I know this is happening because I have one action for loading that I dispatch. My question is, should I have separate loading actions for each component? If no, how can I go about fixing this? Thank you.
//Loading action
export const LOADING_DATA = '[ui] LOADING DATA';
export const LOADING_DATA_COMPLETE = '[ui] LOADING DATA COMPLETE';
export const loadingData = () => ({
type: LOADING_DATA
});
The answer is you shouldn't have a loadingData() Redux action in the first place. Loading or not is, as you correctly pointed out, every component's "local" state, so you should store it appropriately - inside each component's "normal" state.
Redux store is designed for storing the data that is mutual to several components. And whether some component is ready or not is certainly NOT that.
It is perfectly fine to handle a loading state either in local component state, the part of your redux state where you will finally store the data, or a completely different part.
There is no "one size fits all" solution and different applications handle it differently.
If you want to track that state globally, it is a fairly common pattern to have a yourApi/pending action followed either by a yourApi/fulfilled or yourApi/rejected action - this is how createAsyncThunk of the official redux toolkit handles it.
But of course, if you have two components sharing the same data, then they also share the same loading state. Maybe you should check if the data is already present and fetch it only when it is not already present, because why fetch it twice in the first place?
Or, if the loading state is really describing a different endpoint, really split that up into multiple loading state.
There is good practice that you have loading for each subject you're calling a backend api, for example a loading for calling books api, a loading for calling movies api and so on.
I recommend you create a loadings object in your state and fill it with different loadings that you need like this:
loadings: {
books_loading,
movie_loading
}
so in your components, you wouldn't call a general loading state which affects a lot of components, only those who need the specific loading will use it and you will solve the problem you have
Related
I have a react project configured with redux for state management.
For api calls I use a file called axiosInstance. In this file I need to have access to redux store (and I have access importing store and using getState - store.getState()). Now the problem is that I want also to update the redux state from this file (axiosInstance).
How I can update redux store from this file (which is not a react component) in a efficient method?
Okay, I assume that you are using Axios for the network calls.
tl;dr
Use store.dispatch(action) and action to make changes to store state
Yes, the store can be accessed with store.getState(), you can also change state with store.dispatch(action). as per the docs
Dispatches an action. This is the only way to trigger a state change.
The store's reducing function will be called with the current getState() result and the given action synchronously. Its return value will be considered the next state. It will be returned from getState() from now on, and the change listeners will immediately be notified.
This happens because of the functional paradigm that redux follows (google for more).
Now, the action has to be defined with an actionCreator, or simply you could call .dispatch() with an inline object with one string property as {type: 'name-of-action'}. This prompts the store to change the state.
You may require redux-thunk for async actions.
Conclusion:
Let's save some trouble here as you may have many calls to the store, you can create a context that handles the AxiosInstance (as there is already a different file for that).
Make a context in the AxiosInstance file and then you can start dispatching actions as per the network responses. this will save you a lot of trouble as the state is updated by redux without actually looking at it much.
I've recently started using react using functional components and react-query and it has been working fine except that I'm not clear about how to organize components properly.
the way I'm used to designing components is by having a top level component that performs all data access and passes data to it's child components through props. it also passes various callback handlers to child components so that when action is required, the top level component would update the data and passes the new data to child component. so in my case all calls to useQuery(), useMutation() reside in top level component but it's making the code extremely messy. but it's much like a page containing various child components that only display data or help user interact with data.
function Page(){
const [page, setPage] = useState(1)
const [size, setSize] = useState(10)
const persons = useQuery('persons', async ()=> await getPersons(page, size))
const addPerson = useMutation(async (args)=> {
const {id, name, desc} = args
await addPerson(id, name, description)
})
const person = useQuery('persons', async ()=> await getOnePerson(page, size), { enabled : false })
const addPersonCB = (id: number, name: string, desc: string)=> {
addPerson.mutate({id, name, desc})
}
// complex if/else logic to choose child components
the second approach is to disperse react useQuery() and useMutation throughout the components where it's need. and to further simplify things, if rendering logic is complex, each component would have a parent component that would perform the action and passes data as prop.
function PersonCard(props: PersonCardPropsType){
const {data, isLoading, isError, error} = useQuery(`personQuery${props.id}`, getPerson)
if(isLoading)
return <Wait />
if(isError)
return <Error reason={error} />
const record = data as PersonModel
return ( <PersonCardUI person={record} />)
}
and there are may compoenents for grid, form and etc each one in form of pair like
<PersonEditor />, <PersonEditorUI />, <PersonGrid />, <PersonGridUI />
in this case the calls are dispersed everywhere in the code. I want to know
For large projects, which approach is recommended and why?
Is the mix-match of Redux & react-Query okay? like for instance a grid has page size and page number which should go in redux, maybe?
Is it okay to use pure axios/fetch at some places with redux/react-query it's considered a frowned upon way of doing things?
It is generally considered a best practice to use useQuery where you need it. The separation into container / presentational components, while still possible, has been largely deprecated since hooks came around. With redux connect / mapStateToProps, it was best practice. Now, even in redux, you just call useSelector and useDispatch close to where you need it. This is no different in react-query.
There is a great talk on this subject from Mark Erikson: Hooks, HOCs and tradeoffs that I can totally recommend watching.
Using react-query hooks where they are needed not only avoids prop drilling, it also makes it easier for react-query to keep your data up-to-date, because more observers (=components that call useQuery) are mounting. This is also why it's best to just set a staleTime when you want to customize refetching behaviour. I've written about this in detail in React Query as a State Manager.
Is the mix-match of Redux & react-Query okay? like for instance a grid has page size and page number which should go in redux, maybe?
Totally, as long as you don't sync server state to redux. page number and page size are considered "client state" because the client is control over that state. The user selects the page, and the server responds with the data depending on it. I also like to abstract that away together in custom hooks:
const useData = () => {
const pageNumber = useSelector(state => state.pageNumber)
return useQuery(["data", pageNumber], () => fetchData(pageNumber))
}
that way, you have a hook you can use wherever you want (without passing anything to it), and it re-fetches data automatically if the pageNumber changes.
Is it okay to use pure axios/fetch at some places with redux/react-query it's considered a frowned upon way of doing things?
If you don't need caching / loading states managed for you etc then sure. The only thing that comes to my mind where I don't want a query / mutation might be file downloads or so :)
Let’s step back for a minute and see what each of these abstractions help us achieve & then it makes it easier to see how one should architect an application.
Very broadly, you have
useQuery useSwr
Redux or any other global state management tool
concept of lift state up
Context api
Saving state in url (filters say or link to say a product/item page)
useQuery useSwr are responsible for managing remote state and provide a snapshot of your data that resides behind a remote API. They help with fetching data, caching, error handling, showing loading spinners. They give us additional features such as refetch after a certain interval or refetch on focus, refetch a certain # of times on error etc. Whether we then decide to call these individually in each component or a parent component is a matter of design i.e. implementation detail.
Redux and other global state management tool help with managing local state, globally throughout your client application. A great example of that would be your auth’ed user. That information probably is required globally so redux sounds like a great place to have that information. Shopping cart is another example that might make sense in a redux store.
Lift state up when you want to share information with siblings. This stackoverflow question is a perfect example of lifting state up. DataTableComponent now becomes a controlled component or what you might call a presentation component.
If lifting state up becomes too cumbersome then look at context api or perhaps redux.
So, taking shopping cart as an example, you might decide that context api makes better sense or perhaps lifting state up makes more sense rather than having it in a redux store. My point being that there isn't one way of doing this and it will be a judgement call.
Lastly, you might have a page with filters say, and you may want to give your users an ability to send a link/Url & you might want the recipients to see the same information as the sender. So, now you must save state in your url via say query strings.
Going back to my comment above, there is no one way of doing things. So, you may start off by lifting state but then realize it's too cumbersome so you may switch to context api or even redux.
But each of these abstractions usually do have a place in your application & I have used all the above abstractions in conjunction with each other quite successfully.
I am new to React and want to understand the difference from classic MVC.
I want to create a simple components that loads some data initially and renders let say a grid.
On some state or prop change it will reload the data and re-render.
What is the best approach in react from below two options?
using the lifecycle events to load the data, update some state and render while in another event will show some loading opacity.
Work with redux and react-redux? but in all example I cant see API calls.
Is this the role of a middleware (Thunk?)?
Will appropriate an explanation.
Both the approaches are correct. It depends on your use case. If you can avoid using redux in your app, use the lifecycle methods to make API calls (also called subscriptions in react documentation). If you think your app has many components and different components needs to share a state, then use redux.
You should also look at React hooks https://reactjs.org/docs/hooks-reference.html
You can use Effect Hook https://reactjs.org/docs/hooks-effect.html to make API calls and update your component's state.
Update:
Both Thunk and Sage are used to manage side effects in your application (making API calls from here). I've used saga, I don't know much about thunk.
How you would use redux-saga:
Say if you want to get some data for display on a button click, this is how it works:
On button click you dispatch an action, say GET_DATA
Your redux reducer will change some state on this particular action, say isLoading=true
You can use isLoading in your component to show a spinner/overlay
At the same time saga will listen to GET_DATA action and make the API call
If success, from Saga you'll dispatch an action, say GET_DATA_SUCCESS with the data from API call
Reducer will change isLoading=false and set the data in state, say apiData = { ... }
If failure, from Saga you'll dispatch an action, say GET_DATA_FAILED with the error code/message
Reducer will change isLoading=false and set the error in state, say apiError = { ... }
You can now use isLoading=false in you component to remove spinner and display the data (apiData) or error (apiError) in you component.
You can go through this tutorial to learn more https://redux-saga.js.org/docs/introduction/BeginnerTutorial.html
I came across interesting article (Link at the end of post). Author of the article states, that they treat redux store as client-side database and UI logic does not fit there (If it's not needed for unrelated components), even for data fetching purposes. For example we want to show some loading spinner when fetching some data:
async componentDidMount() {
this.setState({isLoading: true});
await this.props.fetchSomeData();
this.setState({isLoading: false});
}
We fire async thunk action that fetches some data that is needed for more than one component or we want to cache that data, even when that component is unmounted.
The only component that is concerned about loading state is the one in which we fire thunk action, other components are not concerned about state of loading. But I always see redux examples with async actions creators which fire REQUEST/SUCCESS/FAILURE actions types and reducers bloated with loading states even if they're are used in one component.
I can see some cons of this code, that some state lives in component and some in redux, but pros are that redux store is not bloated with state that is not needed for other components, also we can avoid the verbosity of redux.
So my question would be, what are cons of this state separation regarding this particular example ?
Article: https://dev.bleacherreport.com/3-things-i-learned-about-working-with-data-in-redux-5fa0d5f89c8b
(Also interesting discussion in article comments)
IMO, some of the cons of your state separation approach is that:
If there is complex API fetching behind the scene ( fetch A then fetch B then fetch C) redux-thunk would be inadequate and you will have to use redux-saga or redux-observable and together with them, the REQUEST/SUCCESS/FAILURE styles.
If, in the future, there are other components need to listen to the 'loading' state or worse need the same data but there is no guarantee that the order of display them will be the same ( A mount and fetch data and then B mount before the data is fetched, and B fetch again ), you will have to refactor all the structures. And that doesn't count the case when you need to CANCEL a fetch.
If your component unmount before the data is fetched, the "this.setState" call will result in a null reference error.
In general, I agree that UI state should be put in the component, but global state must always be stored in the Redux store and your isLoading is not totally a UI state but a state of the request that your component are listening to.
I've seen numerous examples about this, but every example incorporates details about persistent values like todo is "completed".
I understand we should keep this ("completed") value in store for each TODO, and when store gets recomputed using reducers, the component is re-rendered.
Where do we keep information like "isUpdating" etc for each todo, which I would use to show a loader on a particular todo item (not for the list of todos) when user marks a todo complete and a subsequent ajax call is involved to mark todo as complete in database.
I also understand that as this information might not be useful for other components I should better keep it under the presentational component "TODO". But if these flags are kept in the component, how will the component know that the update on todo was successful or failure on store update ?
In redux we have multiple reducers that handle state as whole. In a todo app, todos are a slice of the state, but they are not always the entire state. In your case, your app's state consists of two entities, the todos, and the status of the loading.
With the above in mind, you can have one reducer that handles the todods. It will take care of loading all the items, or updating an item, or deleting one. In other words, this reducer is only concerned with the state of the todos, but it does not care about where you are in your lading process of an ajax call.
For the loading process you can create another reducer. This reducer knows nothing about your todos and it can really work for any sort of data that your app manages. All this reducer is worried about is if we are currently in the middle of an async ajax call. If we are then we can set some bool like isLoadingto true, and when the async call is either done or errored out we just set it to false. Then any component that wants to display some spinner while loading only needs to subscribe to state changes of this loading reducer.
This is the way I have implemented this feature in multiple production projects at my work. This is by no means the only way, or the best way, its just a way that I know works.
EDIT: The above works well for a more general loader across the site, however the OP wants to know how to make each individual item show a loader when some async call is happening that involves only that item.
The approach I would take is as follows. Instead of having only one array in state that keeps track of the todos, we now have 2 arrays in state. One array keeps track of the data, and the other keeps track of the data as it relates to UI. In other words, the array that keeps track of data will know nothing about isLoading since this is a UI related prop, but the other array will keep track of all the todos as they relate to UI. This way when looping through each todo during the render faze, we can look to see what the isLoading status is of this exact todo in the UI related array in state, and if isLoading is true then we display a spinner on this specific todo.
This gives a nice seperation of concerns. The array that is keeping track of data does not need to worry about UI at all. but we still get the benefit of having an individual spinner for item in the list.
If you want to use redux middleware for handling these asynchronous action (such as redux-saga that you have tagged your question with) then it is much easier to store the values alongside the relevant items in the state.
Basically, it boils down to this: If you've externalised the action from the component, then you should externalise the resulting state of the action.