I'm creating a website which does a lot of api calls, it is a really big project, when it grew up, i started having some problems with backend's data, some components was depending on another components to receive data, and i had to create many if to check if the data already was loaded to show them. With this situation, i've had some doubts about good practices
Should i load every data from backend in the very beggining? for
example in app.js file and then pass the data already loaded
through props?
Is it a good practice calling api in useEffect? cause it may delay
some seconds, it won't be nice to user
If you have loading states there shouldn't be an issue with fetching data for multiple components progressively throughout your app. Typically I would use a container component where the API call would be made. Then that renders conditionally the loading state or the "dumber" child component that holds and renders the data. Getting data when you need it is probably better than getting all of it and having it when you don't.
This first question feels very opinionated and kinda feels like a case by case basis decision. I want to clarify as well that this is indeed my opinion on this question.
Calling API inside of a useEffect is an entirely legitimate use case. For example say you have an infinite scroller that renders on your page. You could have a useEffect make the initial call to the API to get the first set of results. Regardless, yes this is fine.
useEffect(() => {
someAPICallForInitialData();
}, []);
...some code here
<InfiniteScoller onScroll={() => getMoreData()}>
{chidren}
<InfiniteScoller />
Related
How can I access the data of the query called in the parent component, inside a child component without prop drilling.
For example, if in parent.js I call the getPost query
const {data} = useGetPostQuery();
then how can I access this data further down the hierarchy?
There are two options I see:
The "right" answer way - is to use a selector from API slice to access the cached data which is already in rtk-q's Redux store. Let's say we have posts API slice definition. So we'll be able to access some particular cached post directly with a selector, provided by rtk-q:
.
const selectPost = posts.endpoints.getPost.select(postId);
selectPost - is a memorized selector, gererater by rtk-q for a particular postId. To fetch the data - that you'll need to pass the app state in, and it will return the expected post from the cache, that your parent component fetched before.
Inside the component it can be used with useSelector like:
const { data: post } = useSelector(posts.endpoints.getPost.select(postId));
Some more details here:
https://redux.js.org/tutorials/essentials/part-8-rtk-query-advanced#selecting-users-data
And a pretty straightforward way - is just to use the same query code in your child :).
Even it sounds weird\smelly - there is nothing wrong with it, due to you making your component standalone\decoupled, and it will get this data anyway - even will be used outside the parent component. But if it will be used in a case you described, as a child - it will just fetch data from the cache, with no real API call, so with no harm to performance. And it looks even more clear than selectors.
I prefer exactly this approach, and I feel it keeps the code more consistent - I'm just declaring that "I need that data" in a component, and don't care if it was fetched before or not, keeping all the components decoupled from knowing about any others component, making it cohesive.
It's somehow touched here:
https://redux.js.org/tutorials/essentials/part-8-rtk-query-advanced#cache-data-subscription-lifetimes
Offtopic bonus: In a case when you don't need the post data on a parent's component and want to fetch it only to improve responsiveness - you may use the usePrefetch hook. For example - on the "Edit" button hover, it will make an API call, put the data in the cache, so post will be available for children instantaneous.
More here:
https://redux-toolkit.js.org/rtk-query/usage/prefetching
If you want data to be available to all the rendered components then use Context. But, frequent changes in the context will cause permanence issues as the entire tree will get rerendered if the component is not designed properly.
Another option is to use Redux in the app to manage the states.
https://redux.js.org/introduction/core-concepts
Local storage is another option but will not recommend this.
The easiest way to send data from parent to child through props but if you want to do it without props you should use other stratigies like: localStorage,cockies or redux.
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
My app is structured as follows:
<App>
<SelectItems/>
<Tabs>
<Tab> <Window1> </Tab>
<Tab> <Window2> </Tab>
</Tabs>
</App>
The App has a state element called allItems, and SelectItems is used to narrow this down to App.state.selectedItems. The tabs work in a way that only one of the tabs shows at a given time, it's a component from React-Bootstrap.
The Window1 and Window2 display data that depend on the selectedItems, the data (let's call it data1 and data2) is obtained via a computationally intensive api call. Here's the problem. The cleanest way to do this while thinking in React is to "pull the state up", so that data1 and data2 are updated from App whenever the selectedItems change, and the data is then stored in the state of App and passed down to the tabs. This however is inefficient, because only one Window is showing at a given time, so I'm wasting time updating data that I'm never showing. In my actual app I've got many tabs, so it's a real problem.
What's a way of fixing this? I want the Windows to update whenever the App.state.selection changes, and I looked at static getDerivedStateFromProps(props, state) but it looks like (but I'm not sure) that this function won't work because the state updating would be asynchronous, as it requires an API call.
Are there general strategies one can use in this situation?
Thanks!
You have a couple of options, the first one is the one most people would recommend (because it is a popular option) and that is to use Redux to manage your application state.
Redux will allow you to keep all your data in a separate "store". Then from your components you can connect to the store and access the data which is relevant to them. Those components should only update when the data they are interested in is changed, any other changes to the store will be ignored.
There are a lot of good tutorials on using Redux and React together which you can find online - apparently the ones on egghead are pretty good, you can maybe try this one to get started.
Your other option might be to use a PureComponent so that you can limit when your component will re-render to only when it's props or state change rather than if the parent re-renders. You can read about this here. It's actually similar to the previous solution are the connect function provided by the react-redux library to connect to the Redux store wraps your component in a PureComponent.
I have a react.js app which loads data from an API displays a bunch of questions (textboxes, radiolist, checkboxes, etc). The user fills them in and submits all answers back to the API, which then send a new set of questions.
All these questions are in a single object, so I've created parent react.js component which holds the current set of questions in state. When the state changes it re-renders each question below. This works pretty much fine.
The problem is that sometimes the API displays the exact same question for twice in a row, but as this is held in state and react.js is clever enough to know it doesn't need to render a completely new component, because the old one will do (with a few small updates).
The problem is that if I select a radio button on the first one, based on the initial data stored in state of the child component, which was initially set within componentDidMount). But when the second question comes along, because its essentially the same component, the state remains. The constructor is not called again.
I think I should be using one of the other events, perhaps:
componentWillReceiveProps
componentWillMount
componentWillUpdate
but I can't figure out which one is the most consistent one.
I basically want to reset the selectedAnswer everytime the parent has received new data from the API and essentially re-render all child components (but react won't do that).
Edit
I wonder instead of trying to reset this via the internal lifecycle events, whether I can pass in a different set of props into the component, so it can decide whether to re-create or re-render in the usual way.
okay so to optimally do this lets suppose you api which returns the set of questions, it might contain some id associated with it. Using that id create a uniq key for every child component while rendering something like below
<Child key={`${data_id}_${index}`} />
This will ensure that for the same set they do not keep mounting again and again and will only mount if a new data set is fetched in which case data_id will change which would cause remounting of each and every child component
I'd encourage you to check out Redux. It makes managing state much easier. I'd contribute some code on here but I am not sure I actually understand the question. If you linked us to your Github, then I could probably answer this specific question.
Also, it seems like you don't really need to touch state. It sounds more life attaching an event and controlling state that way. For example, using onSubmit, you can make an API call (and whatever else) and then have another function to reset the form state afterwards. It would be pretty straight forward, especially if you are using then/catch Promises.
Application data lives in stores
Application (ui) state lives in stores (there are different opinions tho)
When the user now submits a form and the server returns a validation error, hot do I get this error back to the view component (the form)? Since the (controller) view components only gets updated by change events from the store(s), the error would need to be added to a store. Other people say that in those cases the view components should be able to listen to events from the action creators.
Whats your opinion on that?
Possibly a duplicate of How to handle async errors in Flux?
In the case where the error doesn't really matter to the rest of the app and you don't need to log it, I'd just return the value right to the component. For example, say you're submitting a form and it comes back 422 or something...unless you want to do something with the error like store it, and none of the other components really care that it errors, don't worry about it...just update the state of the View Component.
Although generally speaking it's best to have state at the top most component, it makes sense for some components (like forms) to have a "transient" state that only matters to them...for example when you're typing in text to a box there's no reason to bubble that up to the top-level component usually.