I'm thinking on creating a React ponent called LoadingMask, where I can show or not (depending on the state) a loading mask from any ponent. The idea is showing it before an ajax call, and hiding it after I receive the data.
I don't want to display two masks at the same time, so if one ponent is making a request, and another one creates another request, I want to add 1 to my "MaskCounter", and substract one when the Request is finished. If the counter is 0, I need to hide the LoadingMask.
I order to do this, I think I need to create a "Singleton" ponent, that I can share through the whole platform, so there's only exist one LoadingMask. I also don't think it's nice to send the events to hide/show the mask to all ponents.
Any ideas?
I'm thinking on creating a React ponent called LoadingMask, where I can show or not (depending on the state) a loading mask from any ponent. The idea is showing it before an ajax call, and hiding it after I receive the data.
I don't want to display two masks at the same time, so if one ponent is making a request, and another one creates another request, I want to add 1 to my "MaskCounter", and substract one when the Request is finished. If the counter is 0, I need to hide the LoadingMask.
I order to do this, I think I need to create a "Singleton" ponent, that I can share through the whole platform, so there's only exist one LoadingMask. I also don't think it's nice to send the events to hide/show the mask to all ponents.
Any ideas?
Share Improve this question edited Mar 25, 2022 at 19:05 isherwood 61.1k16 gold badges120 silver badges168 bronze badges asked Jun 23, 2017 at 16:42 markaguirmarkaguir 2931 gold badge2 silver badges5 bronze badges 1- Why don't you think it's nice to send a callback event handler to show/hide the mask to all pents? If each ponent has a loading state which requires a LoadingMask, why is adding the LoadingMask to each ponent any tidyer than adding a callback to each ponent? – Andy Vennells Commented Dec 1, 2022 at 20:51
2 Answers
Reset to default 3To share data between ponents, you can :
- Use a lib like Redux, and keep in shared store your mask loader status
- Use the React context api from your root ponent, and share loader status to all childrens. See an example below :
class Application extends React.Component {
constructor() {
super();
this.state = {
nbTasks: 0
};
this.addTask = this.addTask.bind(this);
this.removeTask = this.removeTask.bind(this);
this.isLoading = this.isLoading.bind(this);
}
addTask() {
this.setState(prevState => ({
nbTasks: prevState.nbTasks + 1
}));
}
removeTask() {
this.setState(prevState => ({
nbTasks: prevState.nbTasks - 1
}));
}
isLoading() {
return this.state.nbTasks > 0;
}
getChildContext() {
return {
addTask: this.addTask,
removeTask: this.removeTask,
isLoading: this.isLoading
};
}
render() {
return (
<div>
<ComponentX />
<ComponentY />
<LoadingMask />
</div>
);
}
}
Application.childContextTypes = {
addTask: PropTypes.func,
removeTask: PropTypes.func,
isLoading: PropTypes.func
};
const LoadingMask = (props, context) => (
context.isLoading()
? <div>LOADING ...</div>
: null
);
LoadingMask.contextTypes = {
isLoading: PropTypes.func
};
class ComponentX extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
message: 'Processing ...'
};
}
ponentDidMount() {
this.context.addTask();
setTimeout(() => {
this.setState({
message: 'ComponentX ready !'
});
this.context.removeTask();
}, 3500);
}
render() {
return (
<div>
<button disabled>{this.state.message}</button>
</div>
);
}
}
ComponentX.contextTypes = {
addTask: PropTypes.func,
removeTask: PropTypes.func
};
class ComponentY extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
message: 'Processing ...'
};
}
ponentDidMount() {
this.context.addTask();
setTimeout(() => {
this.setState({
message: 'ComponentY ready !'
});
this.context.removeTask();
}, 6000);
}
render() {
return (
<div>
<button disabled>{this.state.message}</button>
</div>
);
}
}
ComponentY.contextTypes = {
addTask: PropTypes.func,
removeTask: PropTypes.func
};
ReactDOM.render(
<Application />,
document.getElementById('app')
);
<script src="https://unpkg./prop-types/prop-types.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react/15.6.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react/15.6.1/react-dom.min.js"></script>
<div id="app"></app>
I found this library use-between to be simple, powerful and useful. It removes plexity of redux for sharing data between within functional ponents.
import React, { useState, useCallback } from 'react';
import { useBetween } from 'use-between';
Context/Session.ts
export const useShareableState = () => {
const [count, setCount] = useState(0);
const inc = useCallback(() => setCount(c => c + 1), []);
const dec = useCallback(() => setCount(c => c - 1), []);
return {
count,
inc,
dec
};
};
App.tsx
import { useBetween } from 'use-between';
import { useShareableState } from './src/Context/Session'
const useSharedCounter = () => useBetween(useShareableState);
const Count = () => {
const { count } = useSharedCounter();
return <p>{count}</p>;
};
const Buttons = () => {
const { inc, dec } = useSharedCounter();
return (
<>
<button onClick={inc}>+</button>
<button onClick={dec}>-</button>
</>
);
};
const App = () => (
<>
<Count />
<Buttons />
<Count />
<Buttons />
</>
);
export default App;