我试图在打字稿中写一个HOC来渲染一个微调器,而用户必须等待。
我是从this article开始的。
以下是WithFullScreenSpinnerHOC.ts
打字稿代码:
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;
将使用此HOC的组件必须具有showSpinner状态变量。
所以我有一个SignIn页面,例如用这个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);
但是我在这里遗漏了一些东西,因为更改包装组件中的状态不会触发HOC渲染方法。
我在HOC的渲染方法中设置了一个断点,以确认它只被调用一次。
任何帮助表示感谢。
编辑1 :这是设计上的,组件不相关,因此我需要一种相互通信的方式。如果有效的话,我会尝试Resub并回答我自己的问题。
编辑2 :我已在下面发布了我的解决方案。我还没有接受它作为正确答案,因为我想提出建议/意见。
答案 0 :(得分:0)
我找到的解决方案是使用Resub
我已经定义了一个商店,它将保存微调器的可见性值:
import { autoSubscribe, AutoSubscribeStore, StoreBase } from "resub";
// Resub Store that will allow any component to communicate with the FullScreenSpinner HOC
// https://github.com/Microsoft/ReSub
@AutoSubscribeStore
class SpinnerStateStore extends StoreBase {
private visible: boolean = false;
// Call this from the Wrapped Component to ask for the spinner to be shown/hidden
public toggleVisibility(visible: boolean) {
this.visible = visible;
// Do not forget this call otherwise autosubscriptions won't be called
this.trigger();
}
// Reserved for the FullScreenSpinnerHOC
@autoSubscribe
public isVisible() {
return this.visible;
}
}
export = new SpinnerStateStore();
HOC必须扩展ComponentBase而不是RX.Component。问题中的代码变为:
import RX = require("reactxp");
import ComponentBase from "resub/dist/ComponentBase";
import WithFullScreenSpinnerStateStore = require("./WithFullScreenSpinnerStore");
const styles = {
semiTransparentBlackBbackground: RX.Styles.createViewStyle(
{ backgroundColor: "rgba(0, 0, 0, 0.7)", justifyContent: "center" },
),
};
// tslint:disable-next-line:no-empty-interface
export interface IWithFullScreenSpinnerProps {
}
export interface IWithFullScreenSpinnerState {
pendingIO: boolean;
}
// Higher-Order component that will allow any component to display a fullscreen activity indicator
const withFullScreenSpinner = <P extends IWithFullScreenSpinnerProps, S extends IWithFullScreenSpinnerState>(
WrappedComponent: new (props: P) => RX.Component<P, S>,
) =>
// tslint:disable-next-line:max-line-length
class WithFullScreenSpinner extends ComponentBase<P & IWithFullScreenSpinnerProps, S & IWithFullScreenSpinnerState> {
constructor(props) {
super(props);
this.state = {
pendingIO: false,
} as S & IWithFullScreenSpinnerState;
}
// tslint:disable-next-line:max-line-length
protected _buildState(props: P & IWithFullScreenSpinnerProps, initialBuild: boolean): S & IWithFullScreenSpinnerState {
return {
pendingIO: WithFullScreenSpinnerStateStore.isVisible(),
} as S & IWithFullScreenSpinnerState;
}
// tslint:disable-next-line:member-ordering
public render() {
return (
<RX.View style={{ flex: 1 }}>
<WrappedComponent {...this.props}>
</WrappedComponent>
{this.state.pendingIO &&
<RX.View
style={styles.semiTransparentBlackBbackground}
>
<RX.ActivityIndicator
size="large"
color="black"
/>
</RX.View>}
</RX.View>
);
}
};
export default withFullScreenSpinner;
这是有效的,因为每次拨打SpinnerStateStore.toggleVisibility()
时,WithFullScreenSpinner._buildState()
都会调用this.trigger()
。