I'm trying to write a HOC in typescript to render a spinner while the user have to wait.
I started from this article.
Here is the WithFullScreenSpinnerHOC.ts
typescript code I have:
import RX = require('reactxp');
const styles = {
semiTransparentBlackBbackground: RX.Styles.createViewStyle(
{ backgroundColor: 'rgba(0, 0, 0, 0.7)', justifyContent: 'center' }
),
};
export interface withFullScreenSpinnerProps {
};
export interface withFullScreenSpinnerState {
showSpinner: boolean
};
// Higher-Order component that will allow any component to display a fullscreen activity indicator
const withFullScreenSpinner = <P extends withFullScreenSpinnerProps, S extends withFullScreenSpinnerState>(
WrappedComponent: new (props: P) => RX.Component<P, S>
) =>
class WithFullScreenSpinner extends RX.Component<P & withFullScreenSpinnerProps, S & withFullScreenSpinnerState> {
constructor(props) {
super(props);
this.state = {
showSpinner: false
} as S & withFullScreenSpinnerState;
}
render() {
return (
<RX.View style={{ flex: 1 }}>
<WrappedComponent {...this.props}>
</WrappedComponent>
{this.state.showSpinner &&
<RX.View
style={styles.semiTransparentBlackBbackground}
>
<RX.ActivityIndicator
size="large"
color="black"
/>
</RX.View>}
</RX.View>
);
}
};
export default withFullScreenSpinner;
The component that will use this HOC must have a showSpinner state variable.
So I have a SignIn page for instance that is wrapped with this HOC.
import WithFullScreenSpinner from './WithFullScreenSpinnerHOC'
// The state of the sign-in component (data entered by the user)
interface signInState {
username: string,
password: string,
errorMessage?: string
ready: boolean,
session: any,
showSpinner: boolean
};
class SignIn extends RX.Component<signInProps, signInState> {
constructor(props) {
super(props);
this.state = {
username: '',
password: '',
errorMessage: null,
ready: false,
session: null,
showSpinner: false
};
}
private showSpinner() {
this.setState((prevState, props) => {
let newState = { ...prevState };
newState.showSpinner = true;
return newState;
})
}
private hidepinner() {
this.setState((prevState, props) => {
let newState = { ...prevState };
newState.showSpinner = false;
return newState;
})
}
// Do the Sign In using the auth API that comes through the props of the WithAuth HOC
doSignIn = async (username: string, password: string) => {
const { authLayer } = this.props;
try {
this.showSpinner();
const user = await authLayer.signIn(username, password);
const requireMFA = (user.Session !== null);
this.setState((prevState, props) => {
let newState = { ...prevState };
newState.showMFAPrompt = requireMFA;
newState.session = user.session;
return newState;
});
// Should be null if MFA enabled
return user.signInUserSession;
}
catch (err) {
console.log(err);
this.setState((prevState, props) => {
let newState = { ...prevState };
newState.errorMessage = err.message;
return newState;
});
}
finally {
this.hidepinner();
}
}
render() {
return (
);
}
};
export default WithFullScreenSpinner(SignIn);
But I'm missing something here because changing the state in the wrapped component does not trigger the HOC render method.
I've set a breakpoint in the HOC's render method to confirm that it is called only once.
Any help appreciated.
EDIT 1: This is by design, the components are not related, thus I need a way to communicate between each others. I'll try Resub and answer my own question if this works.
EDIT 2: I have posted my solution below. I have not accepted it as the correct answer yet because I would like to have suggestions/comments.