我正在开发一个React Redux应用程序,该应用程序允许Google oauth2登录,并且我创建了一个单独的GoogleAuth组件,用于处理所有登录/注销过程。
GoogleAuth组件通常位于header中,作为header组件的子级。但是,在某些页面(当用户未登录时显示(中,我将GoogleAuth组件放置在其他位置,并将其从标题中删除。
例如,在"登录"页面上,GoogleAuth组件将从标头中卸载,另一个实例将安装在登录页面中。
我在下面包含了GoogleAuth组件的代码。
class GoogleAuth extends React.Component {
componentDidMount() {
window.gapi.load('client:auth2', () => {
window.gapi.client.init({
clientId: 'xxxx',
scope: 'email'
}).then(() => {
this.auth = window.gapi.auth2.getAuthInstance();
const isGoogleSignedIn = this.auth.isSignedIn.get();
if(this.props.isSignedIn !== isGoogleSignedIn) {
this.onAuthChange(isGoogleSignedIn);
}
this.auth.isSignedIn.listen(this.onAuthChange);
})
});
};
componentWillUnmount() {
delete this.auth;
}
onAuthChange = (isGoogleSignedIn) => {
console.log("onAuthChange called:", this.props.place);
//place is a label assigned to the parent component
if (!this.props.isSignedIn && isGoogleSignedIn && this.auth){
this.props.signIn(this.auth.currentUser.get().getId());
} else if (this.props.isSignedIn && !isGoogleSignedIn && this.auth) {
this.props.signOut();
}
}
handleSignIn = () => {
this.auth.signIn();
}
handleSignOut = () => {
this.auth.signOut();
}
renderAuthButton() {
if (this.props.isSignedIn === null) {
return null;
} else if (this.props.isSignedIn) {
return (
<button className="google-auth-button" onClick={this.handleSignOut}>
Sign Out
</button>
)
} else {
return (
<button className="google-auth-button" onClick={this.handleSignIn}>
Sign In With Your Google Account
</button>
)
}
}
render() {
return this.renderAuthButton()
}
}
const mapStateToProps = (state) => {
return {
isSignedIn: state.auth.isSignedIn,
userId: state.auth.userId
}
}
export default connect(
mapStateToProps,
{signIn, signOut}
)(GoogleAuth);
我面临的问题是,每次安装GoogleAuth组件时,都会调用onAuthChange
方法(当用户的身份验证状态更改时触发(一次,即使从那以后已经卸载了。因此,如果我打开应用程序,然后转到登录页面(这导致GoogleAuth从Header中卸载并插入登录组件(,然后登录,我会看到以下控制台日志。
onAuthChange called: header
onAuthChange called: login
我有一些事情需要一些建议:
- 这是我需要解决的问题吗?如果我不采取任何措施来解决它,会发生什么
- 当组件卸载时,我试图通过显式删除
auth
对象来进行一些清理。但是,对于GoogleAuth
组件的每个实例(无论是否已安装(,仍然调用onAuthChange
方法 - 我能够通过检查
this.auth
是否存在来防止对操作创建者的重复调用。这种方法有什么缺点吗 - 是否有一段时间后,卸载的组件将自动清除
console.log
的函数没有被GCed
this.auth
被设置为window.gapi.auth2.getAuthInstance()
。delete this.auth
仅从您的this
中删除auth
密钥。只要window.gapi.auth2.getAuthInstance()
返回的对象有其他引用(例如,在库本身中,看起来很像单例(,它就不会被GCed->你的delete
没有多大区别要解决此问题,您必须从componentWillUnmount
中的this.auth.isSignedIn
中删除侦听器,可能有一个名为this.auth.isSignedIn.removeListener(...)
的函数。如果没有,您可以查看有关如何删除侦听器的文档。
我还建议不要在组件的每个实例中加载API并初始化客户端,而是只进行一次。
编辑:要注销侦听器,您可以看看这个问题:如何删除Google OAuth2 gap事件侦听器?但是,我建议不要为每个组件实例添加/删除侦听器。
编辑2:示例
class GoogleAuth extends React.Component {
componentDidMount() {
googleAuthPromise.then(
googleAuth => {
const isGoogleSignedIn = googleAuth.isSignedIn.get();
if(this.props.isSignedIn !== isGoogleSignedIn) {
this.onAuthChange(isGoogleSignedIn);
}
this.unregisterListener = listenSignIn(this.onAuthChange);
}
)
};
componentWillUnmount() {
this.unregisterListener();
}
onAuthChange = async (isGoogleSignedIn) => {
console.log("onAuthChange called:", this.props.place);
const googleAuth = await googleAuthPromise;
if (!this.props.isSignedIn && isGoogleSignedIn && googleAuth){
this.props.signIn(googleAuth.currentUser.get().getId());
} else if (this.props.isSignedIn && !isGoogleSignedIn && googleAuth) {
this.props.signOut();
}
}
handleSignIn = async () => {
const googleAuth = await googleAuthPromise;
return googleAuth.signIn();
}
handleSignOut = async () => {
const googleAuth = await googleAuthPromise;
return googleAuth.signOut();
}
renderAuthButton() {
if (this.props.isSignedIn === null) {
return null;
} else if (this.props.isSignedIn) {
return (
<button className="google-auth-button" onClick={this.handleSignOut}>
Sign Out
</button>
)
} else {
return (
<button className="google-auth-button" onClick={this.handleSignIn}>
Sign In With Your Google Account
</button>
)
}
}
render() {
return this.renderAuthButton()
}
}
const mapStateToProps = (state) => {
return {
isSignedIn: state.auth.isSignedIn,
userId: state.auth.userId
}
}
export default connect(
mapStateToProps,
{signIn, signOut}
)(GoogleAuth);
// create a customer listener manager in order to be able to unregister listeners
let listeners = []
const invokeListeners = (...args) => {
for (const listener of listeners) {
listener(...args)
}
}
const listenSignIn = (listener) => {
listeners = listeners.concat(listener)
return () => {
listeners = listeners.filter(l => l !== listener)
}
}
// make the client a promise to be able to wait for it
const googleAuthPromise = new Promise((resolve, reject) => {
window.gapi.load('client:auth2', () => {
window.gapi.client.init({
clientId: 'xxxx',
scope: 'email'
}).then(resolve, reject)
})
})
// register our custom listener handler
googleAuthPromise.then(googleAuth => {
googleAuth.isSignedIn.listen(invokeListeners)
})
免责声明:这是未经测试的代码,您可能需要进行小的更正。