I have three components, each using the same two queries (custom hooks):
const {
data: survey,
isLoading: isSurveyLoading,
isError: isSurveyError,
} = useSurveyInfo(id);
const {
data: responses,
isLoading: isResponsesLoading,
isError: isResponsesError,
} = useResponses(id);
In each of these 3 components, I'm showing a loading spinner in the case of isLoading
How do I show one loading spinner for all three components?
I know I could use the queries in the parent component and pass props to children. But to eliminate prop drilling, I am calling each query in the child component (which seems to be best practice).
Is there a way to show one loading spinner when any of the 3 child components is loading?
Suspense is made for this, but it's still experimental
https://tanstack.com/query/v4/docs/guides/suspense
One of beauties of react-query is that it allows you to render multiple components that relay on the same data and updates all the components at the same time since they all rely on the same provider.
But now the job to make it look good on the website is on us.
I suggest not rendering the loader from the 2 lower components in the page.
Or if it makes no sense to have a content less component, you can always return null from the components.
const SecondAndTheirComponents = () => {
const { isLoading: isResponsesLoading } = useResponses(id);
if (isResponsesLoading) {
return null;
}
return (
<Content
...
/>
);
};
Or better yet: I would recommend looking into adding skeletons when you are loading data. This way you could save the space for all component and avoid jumpiness when data is returned to the browser.
You can checkout the skeletons by Material-ui (both v3 and v4 work perfectly fro me) v4 skeletons-material
According to your first question loading one spinner for all three components, you can use if else condition of reactjs, for example-
{
isResponseLoading?
isSurveyLoading?
<RunSpinnerCode/>
:
null
}
As shown in the above we can check two and even more condition. If first one is loaded, then we check for second one and so on..
Hope you like the answer if you still face any issue, just lemme know.
Thanks
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I am displaying contact list array.
If contacts array is empty it will show the loader else will display contact list.
I render loader using higher order component Using this tutorial. For this I got performance summary as shown in the below image-
when i render loader using simple if else condition then i had performance graph as below -
comparing this i got to know that while using higher order component it requires more time than simple loop.
Can anyone please explain me which is better to use ? and when we should use the Higher order component ? what are advantages and disadvantages of using Higher order component ?
A higher order component allows you to apply functionality or data that is common amongst a number of components to a component and will return a new component with the functionality or data of the HoC included.
As for advantages or disadvantages. Would entirely depend on the circumstance and problem you are trying to address. You would also need to perform a long list of tests as the times are too close to really say one is quicker than the other. Also this looks like it is on your local so on production server could be an entirely different story.
But in your circumstance are you apply any extra functionality to your <Loader /> component? If you aren't or that functionality is not going to be used anywhere else then it might be unnecessary to use a HoC in your case.
ReactJS - Higher Order Components
pros for HOC:
extremly reusable around your app/projects:
// withShowSupport.js
export default Component => props =>
!props.show
? null
: <Component { ...props } />
easy testing
res:
https://www.robinwieruch.de/gentle-introduction-higher-order-components/
New technology can lead us to be more efficient and effective in productivity. But, of course, it will also increase resource usage.
I don't have enough knowledge to explain more, but I can give you simple study case.
I have a react app which consists of several pages (how about > 10 pages?). I want to do something when each page has been rendered (In other words, page changes). If-else condition rendering? No way!
Actually, you can make an HOC named withBase that will wrap each page (Use it when you export your page component e.g. export default withBase(Home)). withBase will execute a function that indicates page has been changed.
DEMO
As per definition :
HOC is a function that accepts your component as an argument and
returns a new function that adds some feature to the component.
A simple use case is - when you call an API to fetch some data and use them to render some content in your application, we need to show some kind of progress bar, loading indicator, etc to tell users that data is being fetched. We can use HOC for that purpose.A sample code goes here
import React from 'react';
import List from './List.js';
import WithLoading from './WithLoading.js';
const ListWithLoading = WithLoading(List);
class App extends React.Component {
state = {
loading: false,
repos: null
}
componentDidMount() {
this.setState({ loading: true });
fetch(`https://api.github.com/users/farskid/repos`)
.then(json => json.json())
.then(repos => {
this.setState({ loading: false, repos: repos });
});
}
render() {
return (
<ListWithLoading isLoading={this.state.loading} repos={this.state.repos} />
)
}
}
the above is App.js file and below is the HOC component WithLoading
import React from 'react';
function WithLoading(Component) {
return function WihLoadingComponent({ isLoading, ...props }) {
if (!isLoading) return (<Component {...props} />);
return (<p>Be Hold, fetching data may take some time :)</p>);
}
}
export default WithLoading;
so in App.js, we are calling HOC like const ListWithLoading = WithLoading(List); and <ListWithLoading isLoading={this.state.loading} repos={this.state.repos} />, Where List is the component which is passing into the HOC. based on the sLoading prop, loding symbol is shown. Another one interesting thing is HOC dont have a render menthod! beacuse its a simple function !
By my understanding, Higher-Order-Component adds an additional layer between components or libraries for better code-reuse.
For example, you have a library file "User.class.js" that is responsible to retrive/store user data, and it's used by different components. When "User.class.js" is modified, method names changed, or parameters changed. What you gonna do? Apply changes to all those components? What if it's used by 100 different components, or even more? With Higher-Order-Component, you just need to apply the changes to one single file.
I am currently using react-native-navigation (Wix) and RN 0.48.3, without flux or redux.
Currently I am focusing on making the backend handle pretty much all the logic, so I am sending "actions" that the mobile components execute. One of them is to open an internal link, which I can add some passProps from the backend.
Now, the new screen could have multiple components and I am just sending a bunch of properties to a screen.
My question is, what is the best way to send specific props to specific components? I am currently thinking on sending a JSON structure with an ID which I can match to a ref in the final screen.
I am open to ideas. I am not using redux or flux and I´d like to keep it that way but I am willing to add it to the project if that makes things simpler.
If anyone has a better alternative please let me know, but I have solved it like this.
I have a ScreenComponent which actually enriches and add features to all my child components like analytics and some other helpers.
Basically, I am adding a ref to each component that I am adding on each screen and then I use passProps like this:
screenParams: {
screen: 'AudioScreen',
passProps: {
componentProps: {
audioCard: {
title:'Audio Title',
subtitle:'Audio Subtitle',
image:{imageSource},
audioSource:{audioSource}
}
}
}
}
componentProps is the object that I use and in my screen I do this in my componentWillMount:
this._children = React.Children.map(this.props.children, child => {
let extraProps = {
eventEmitter: this._eventEmitter
};
if (child.ref != null && self.props.componentProps && self.props.componentProps[child.ref]){
extraProps = self.getHelper().concat(extraProps, self.props.componentProps[child.ref])
}
return React.cloneElement(child, extraProps);
});
As you can see, I am adding an eventEmitter to each child, so I can communicate to my parent screen to show modals, error alerts, etc. The helper is a module I have that just merges two objects.
That way, I can send props to the screen, props to each component and also I can override everything from the backend.
So far it has been working great.
Hope it helps
Let's say at the top of the app, we retrieve some basic information about the app or user before rendering the rest of the application:
const getUser = gql`
query getUser(id: Int!) {
user(id: $id) {
id
name
}
}
`)
function App({ data }) {
return (
<div>
{!data.loading && !data.error && (
// the application
)}
</div>
)
}
export default graphql(getUser, {
options: (props) => ({ variables: { id: props.id }})
})(App)
Now anywhere in the application, it is safe to assume that the user has been loaded and is stored. What is the proper way for another deeply nested component to the retrieve the user data without having to redo the querying and loading logic?
This is the very basic use of a store-based library like Redux. This is not the purpose to guide every step of the way here but you are looking for a single source of truth as described here: http://redux.js.org/docs/introduction/ThreePrinciples.html
In short:
Receiving getUser response should trigger a 'LOGGED_IN' action dispatching user Data, this would be catched by a reducer updating the user object in your store (as much nested as you want), a container would then connect to this user in the store and have all its data using connect()
As of now, I'm not certain there is a proper way, but these are the options I think are reasonable
Manually pass down data via props
Wrap your deeply nested component with the same query
Manual pass down ensures your components rerender correctly, but it can be a pain to refactor. Wrapping your nested component would just hit the cache. Yes, you probably need to redo the loading logic, but that's not a show stopper.
My advice is to manually pass down props for shallow nested components and rewrap deeply nested components. Unfortunately, react-apollo doesn't provide a convenient way to access the apollo-store for nested components the same way that redux's connect container does.
I am struggling a bit with the concept of global state and reusable components in redux.
Let's say I have a component that's a file-selector that I want to use in multiple places inside my applications state. Creating action/reducers leads to a lot of bloat as I have to handle states with dynamic suffixes and other weird things that just don't really strike me as a smart way to go about things.
What's the general consensus on these things? I can only see two solutions:
Make the file-selector component have local state (this.setState/this.getState)
Make the file-selector be part of the global state but in it's own unique reducer that I can read from once the operation of the component is done?
Any ideas / best practices? Thanks.
Update: To clarify the file selector I am describing is not a simple component that works purely on the client side but has to fetch data from the server, provide pagination as well as filtering etc.. That's why I'd also like to reuse most of the client/server interaction. The views that display this component are of course dumb and only display values from the state - but how do I reuse the actions/reducers in multiple places around the application?
Have your reducer handle multiple instances of your component state. Simply define some "unique" ID for each instance of your FileBrowser component when it appears in the app, and wrap your current state in an object with this uniqueIds as keys, and your old complex state as value.
This is a technique I've used multiple times. If all your FileBrowser are known at compile time, you can even setup the initial state before running your app. If you need to support "dynamic" instances, simply create an Action that initializes the state for a given id.
You didn't provide any code, but here's a contrived example for a reusable Todo reducer:
function todos(state={}, action){
switch(action.type){
case 'ADD_TODO':
const id = action.todoListId
return {
...state,
[id]: {
...state[id],
todos: [ ...state[id].todos, action.payload ]
}
}
// ...
}
}
Usually, the rule of thumb is that you use a redux store to manage data in your application aka storing items fetched from the server and local react state for ui behaviors, like file uploads in your case. I'd make a pure react component to manage file uploads and then use redux-form to manage specific form.
Here is the example of the component I use in my project
import React, {Component, PropTypes} from 'react';
import Button from 'components/Button';
class FileButton extends Component {
static propTypes = {
accept: PropTypes.string,
children: PropTypes.any,
onChange: PropTypes.func.isRequired
};
render() {
const {accept, children, onChange} = this.props;
return <Button {...this.props} onClick={() => this.file.click()}>
<input
ref={el => this.file = $(el)}
type="file"
accept={accept}
style={{display: 'none'}}
onChange={onChange}
/>
{children}
</Button>;
}
}
export default FileButton;
We came to the conclusion that reusable components must be of two kinds:
dumb components, i.e. components that only receive props and trigger "actions" via props callbacks only. These components have minimal internal state or at all. These are the most frequent of reusable components, and your file selector will probably fall in that case. A styled Text Input or custom List would be good examples too.
connected components that provide their own actions and reducer. These components have their own life within the application and are rather independent from the rest. A typical example would be a "top error message box" that displays on top of everything else when the application fails critically. In such a case the application triggers an "error action" with the appropriate message as payload and on the following re-render, the message box displays on top of the rest.
I have the following code in my render method:
render() {
return (
<div>
{this.props.spatulaReady.ready() ? <p>{this.props.spatula.name}</p> : <p>loading spatula</p>}
</div>
)
}
Which according to my understanding, checks if the subscriptionhandle is ready (data is there) and displays it. If no data is available, it should display a simple loading message. However, when I first load the page this snippet is on, it get's stuck on the loading part. On a page reload the data (usually) displays fine.
If I check the spatulaReady.ready() when the page first loads and while the display is stuck on 'loading spatula', and the data that should be there, the handle reports as ready and the data is there like it is supposed to be. If I refresh the page it all displays fine as well. The problem is, this way of checking for data and rendering if it has arrived has worked fine for me in the past. Is it because the render method is not reactive? Because handle.ready() should be reactive.
What makes it even weirder is that it sometimes DOES correctly display the data on page load, seemingly at random.
CreateContainer code:
export default createContainer(props => {
return {
user: Meteor.user(),
spatulaReady: Meteor.subscribe('spatula.byId', props.deviceId),
spatula: SpatulaCollection.findOne()
}
}, SpatulaConfig)
Publication code:
Meteor.publish('spatula.byId', function(deviceId) {
const ERROR_MESSAGE = 'Error while obtaining spatula by id'
if (!this.userId) //Check for login
throw new Meteor.Error('Subscription denied!')
const spatula = SpatulaCollection.findOne({_id: deviceId})
if(!spatula) //No spatula by this id
throw new Meteor.Error(403, ERROR_MESSAGE)
if(spatula.ownedBy != this.userId) //Spatula does not belong to this user
throw new Meteor.Error(403, ERROR_MESSAGE)
return SpatulaCollection.find({_id: deviceId})
})
I know I'm missing a piece of the puzzle here, but I've been unsuccessful at finding it. If you don't know the solution to my specific problem, pointing me in the right direction with another way of waiting for the data to arrive before displaying it is also greatly appreciated.
EDIT: After doing some trial-and-error and reading various other posts somewhat related to my project, I figured out the solution:
export default createContainer(props => {
const sHandle= Meteor.subscribe('spatula.byId', props.deviceId)
return {
user: Meteor.user(),
spatulaReady: sHandle.ready(),
spatula: SpatulaCollection.findOne()
}
}, SpatulaConfig)
It still makes no sense to me that moving the ready() call to create container fixed all my problems though.
As you figured out, moving the .ready() call to createContainer fixes the problem. This is because Meteor reactivity only works when you call a reactive data source (a reactive function), such as collection.find() or subscriptionHandle.ready() within a reactive context, such as Tracker.autorun or createContainer. Functions within the React component, including render, are not reactive contexts from Meteor's perspective.
Note that React and Meteor reactivity are two different things. React's reactivity works simply so that whenever a component's props or state change, it's render function is re-run. It does not understand anything about Meteor's reactive data sources. Since createContainer (that is re-run by Meteor reactivity when reactive data sources in it change) simply passes props to the underlying component, the component is re-rendered by React when the props given from createContainer change.