为什么第二次渲染后"登录谷歌"按钮会消失



我正在使用Google Identity中的"登录Google"按钮。我已经将这个按钮文档页面中的HTML放入了React组件中。看起来像这样:

export default function GoogleLoginButton() {
return (
<>
<div
id="g_id_onload"
data-client_id="XXXXXX"
data-auto_prompt="false"
></div>
<div
className="g_id_signin"
data-type="standard"
data-size="large"
data-theme="outline"
data-text="sign_in_with"
data-shape="rectangular"
data-logo_alignment="left"
></div>
</>
);
}

第一次加载页面时,谷歌登录按钮正确显示,我可以登录。然后,登录按钮被注销按钮取代。问题是,当我点击注销按钮时,它不会再次出现谷歌登录按钮!为什么?

我可以补充一下,注销后刷新页面会带回谷歌按钮。

正如Stian所说,一旦应用程序渲染,谷歌脚本就会注入谷歌按钮。问题是,如果你重新渲染按钮所在的组件,它将消失,因为谷歌脚本已经执行,并且在未来的重新渲染中不会执行(它不会再次注入谷歌按钮(。

另一种解决方案是在useEffect中调用window.google.accounts.id.renderButton,该useEffect应放置在重新渲染的组件中。这将在每次调用useEffect时重新注入谷歌按钮(重新渲染组件(。

renderButton方法接收的第一个参数应该是div的ref,google脚本将在该div中注入登录按钮。

注意:记住首先初始化调用google.accounts.id.initialize 的谷歌脚本

下面是一个例子:

const divRef = useRef(null);
useEffect(() => {
if (divRef.current) {
window.google.accounts.id.initialize({
client_id: <YOUR_CLIENT_ID_GOES_HERE>,
callback: (res, error) => {
// This is the function that will be executed once the authentication with google is finished
},
});
window.google.accounts.id.renderButton(divRef.current, {
theme: 'filled_blue',
size: 'medium',
type: 'standard',
text: 'continue_with',
});
}
}, [divRef.current]);
return (
{/* Other stuff in your component... */}
{/* Google sign in button -> */} <div ref={divRef} />
);

它之所以不起作用,是因为附带的客户端库在以后的渲染中不会再次运行。

在页面加载时,客户端库运行并在HTML所在的位置注入iframe。在稍后的渲染中,可以在DOM中看到这种情况不会发生;HTML存在,但没有iframe。

一种解决方案是永远不要从DOM中删除HTML。相反,当需要隐藏登录按钮时,将样式display: none应用于该按钮。

我发现了这个问题的另一个解决方案,创建了一个useEffect钩子,每次组件渲染时都会将Google按钮<script>标记插入DOM。这样可以防止在离开页面并再次返回时按钮消失。完整的React组件如下所示:

const GoogleButton = () => {
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://accounts.google.com/gsi/client';
script.async = true;
script.defer = true;
document.body.appendChild(script);
return () => {
document.body.removeChild(script);
};
}, []);
return (
<>
<div
id='g_id_onload'
data-client_id='your-client-id'
/>
<div
className='g_id_signin'
data-type='standard'
data-size='large'
data-theme='outline'
data-text='sign_in_with'
data-shape='rectangular'
data-logo_alignment='center'
/>
</>
);
};

对于TypeScript,我对Alex的回答做了一些修改。

首先,不要忘记将<script src="https://accounts.google.com/gsi/client" async defer></script>添加到reactpublic文件夹中的index.html页面。

创建一个google_sso.ts文件并使用以下代码:

import { useRef, useEffect } from 'react';
declare const google: any;
const GoogleSSO = () => {
const g_sso = useRef(null);
useEffect(() => {
if (g_sso.current) {
google.accounts.id.initialize({
client_id: "xxxxxxxx-koik0niqorls18sc92nburjfgbe2p056.apps.googleusercontent.com",
callback: (res: any, error: any) => {
// This is the function that will be executed once the authentication with google is finished
},
});
google.accounts.id.renderButton(g_sso.current, {
theme: 'outline',
size: 'large',
type: 'standard',
text: 'signin_with',
shape: 'rectangular',
logo_alignment: 'left',
width: '220',
});
}
}, [g_sso.current]);

return (<div ref={g_sso} />);
}

export default GoogleSSO 

然后,无论你在哪里需要按钮,都可以使用这个:

import  GoogleSSO from "../common/google_sso";
<GoogleSSO />

最新更新