当我从@react-keycloak/ssr npm包添加SSRKeycloakProvider组件时,我在NextJS项目中遇到了浏览器无限循环问题。此无限循环仅发生在特定应用程序的“报告”页面上。
我的调查使我相信这与keycloak集成处理cookie的方式有关,在报告页面中,我也使用了windows.replaceState()javascript函数。
每次循环开始时,我从应用程序得到的消息如下:
[Function: setReportq]
Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer's output format. This will lead to a mismatch between the initial, non-hydrated UI and the intended UI. To avoid this, useLayoutEffect should only be used in components that render exclusively on the client. See https://reactjs.org/link/uselayouteffect-ssr for common fixes.
at Overflow (/home/ubuntu/growth-admin/node_modules/rc-overflow/lib/Overflow.js:42:32)
at SelectSelector (/home/ubuntu/growth-admin/node_modules/rc-select/lib/Selector/MultipleSelector.js:36:18)
at div
at Selector (/home/ubuntu/growth-admin/node_modules/rc-select/lib/Selector/index.js:38:35)
at Trigger (/home/ubuntu/growth-admin/node_modules/rc-trigger/lib/index.js:79:36)
at SelectTrigger (/home/ubuntu/growth-admin/node_modules/rc-select/lib/SelectTrigger.js:74:25)
at div
at BaseSelect (/home/ubuntu/growth-admin/node_modules/rc-select/lib/BaseSelect.js:67:18)
at Select (/home/ubuntu/growth-admin/node_modules/rc-select/lib/Select.js:66:18)
at InternalSelect (/home/ubuntu/growth-admin/node_modules/antd/lib/select/index.js:55:31)
at div
at div
at div
at Col (/home/ubuntu/growth-admin/node_modules/antd/lib/grid/col.js:59:33)
at FormItemInput (/home/ubuntu/growth-admin/node_modules/antd/lib/form/FormItemInput.js:44:25)
at div
at Row (/home/ubuntu/growth-admin/node_modules/antd/lib/grid/row.js:56:34)
at FormItem (/home/ubuntu/growth-admin/node_modules/antd/lib/form/FormItem.js:101:20)
at form
at Form (/home/ubuntu/growth-admin/node_modules/rc-field-form/lib/Form.js:33:19)
at SizeContextProvider (/home/ubuntu/growth-admin/node_modules/antd/lib/config-provider/SizeContext.js:19:23)
at InternalForm (/home/ubuntu/growth-admin/node_modules/antd/lib/form/Form.js:66:27)
at div
at Report (webpack-internal:///./common/components/DominoReport/index.js:152:3)
at div
at appClassicstyle__ContentWrapper (/home/ubuntu/growth-admin/node_modules/styled-components/dist/styled-components.cjs.js:1:19220)
at div
at appClassicstyle__AppWrapper (/home/ubuntu/growth-admin/node_modules/styled-components/dist/styled-components.cjs.js:1:19220)
at AppClassic (webpack-internal:///./common/components/Admin/report.js:27:3)
at div
at main
at Basic (/home/ubuntu/growth-admin/node_modules/antd/lib/layout/layout.js:78:25)
at Content (/home/ubuntu/growth-admin/node_modules/antd/lib/layout/layout.js:61:37)
at section
at BasicLayout (/home/ubuntu/growth-admin/node_modules/antd/lib/layout/layout.js:93:34)
at Layout (/home/ubuntu/growth-admin/node_modules/antd/lib/layout/layout.js:61:37)
at section
at BasicLayout (/home/ubuntu/growth-admin/node_modules/antd/lib/layout/layout.js:93:34)
at Layout (/home/ubuntu/growth-admin/node_modules/antd/lib/layout/layout.js:61:37)
at exports.ThemeProvider (/home/ubuntu/growth-admin/node_modules/styled-components/dist/styled-components.cjs.js:1:24917)
at Growth (webpack-internal:///./containers/Admin/growth.js:81:3)
at AppClassic (webpack-internal:///./pages/report.js:19:3)
at ThemeProvider (/home/ubuntu/growth-admin/node_modules/@material-ui/styles/ThemeProvider/ThemeProvider.js:48:24)
at KeycloakProvider (/home/ubuntu/growth-admin/node_modules/@react-keycloak/core/lib-commonjs/provider.js:72:51)
at SSRKeycloakProvider (/home/ubuntu/growth-admin/node_modules/@react-keycloak/ssr/lib-commonjs/SSRKeycloakProvider.js:64:28)
at CustomApp (webpack-internal:///./pages/_app.tsx:63:3)
at StylesProvider (/home/ubuntu/growth-admin/node_modules/@material-ui/styles/StylesProvider/StylesProvider.js:57:24)
at ae (/home/ubuntu/growth-admin/node_modules/styled-components/dist/styled-components.cjs.js:1:13296)
at AppContainer (/home/ubuntu/growth-admin/node_modules/next/dist/server/render.js:293:29)
上面的消息提到了parseCookies函数(in _app.tsx:63:3),pages/index.tsx第20行基本上是这一行:
const parsedToken: ParsedToken | undefined = keycloak?.tokenParsed
、在Growth组件中设置URL参数的windows.replaceState()函数,以及同样位于Growth组件中的特定reportq()函数。
下面是my _app.tsx,我相信parseCookies函数在这里很有用:
import React, { useEffect } from "react"
import App from 'next/app'
import { SSRKeycloakProvider, SSRCookies } from '@react-keycloak/ssr'
import cookie from 'cookie'
import type { IncomingMessage } from 'http'
import { ThemeProvider } from '@material-ui/core/styles';
import theme from '../theme';
const KC_URL = process.env.NEXT_PUBLIC_KC_URL;
const KC_REALM = process.env.NEXT_PUBLIC_KC_REALM
const KC_CLIENT_ID = process.env.NEXT_PUBLIC_KC_CLIENT_ID
const keycloakCfg = {
realm: KC_REALM,
url: KC_URL,
clientId: KC_CLIENT_ID
}
interface InitialProps {
cookies: unknown
}
export default function CustomApp({ Component, pageProps, cookies }) {
const initOptions = {
onLoad: 'login-required',
checkLoginIframe: false
}
return (
<SSRKeycloakProvider
keycloakConfig={keycloakCfg}
persistor={SSRCookies(cookies)}
initOptions={initOptions}
>
<ThemeProvider theme={theme}>
<Component {...pageProps} />
</ThemeProvider>
</SSRKeycloakProvider>
);
}
// I think the cookies and this function have to do with the issue
function parseCookies(req?: IncomingMessage) {
if (!req || !req.headers) {
return {}
}
return cookie.parse(req.headers.cookie || '')
}
CustomApp.getInitialProps = async (appContext) => {
// Your static props paths
const staticPropsPaths = [
"/paper/[paperId]/[paperName]",
"/hubs"
]
if (process.browser || !staticPropsPaths.includes(appContext.router.route)) {
const appProps = await App.getInitialProps(appContext)
return { ...appProps, cookies: parseCookies(appContext?.ctx?.req) }
}
}
这是我的索引.tsx:
import type { NextPage } from 'next'
import { useKeycloak } from '@react-keycloak/ssr'
import type { KeycloakInstance, KeycloakTokenParsed } from 'keycloak-js'
import Growth from '../containers/Admin/growth'
type ParsedToken = KeycloakTokenParsed & {
email?: string
preferred_username?: string
given_name?: string
family_name?: string
}
const Home = ({ query }) => {
const { keycloak } = useKeycloak<KeycloakInstance>()
const parsedToken: ParsedToken | undefined = keycloak?.tokenParsed
const loggedinState = keycloak?.authenticated ? (
<span className="text-success">logged in</span>
) : (
<span className="text-danger">NOT logged in</span>
)
const welcomeMessage =
keycloak?.authenticated || (keycloak && parsedToken)
? `Welcome back ${parsedToken?.preferred_username ?? ''}!`
: 'Welcome ! Please login to continue.'
return <Growth query={query} page={'home'} />
}
Home.getInitialProps = async ({ query, res }) => {
return { query }
}
export default Home
增长代码如下:我认为增长部分的利益线:
window.history.replaceState('state', 'Growth ', `${BASE_URL}${page}${reportq}`)
增长部分:
const Growth = ({ query, page }) => {
const { keycloak, initialized } = useKeycloak()
const router = useRouter()
let [p, setP] = useState(page)
let reportq;
let prospectq;
// I believe this function is involved
const setReportq = (params) => {
window.sessionStorage.setItem("reportq", params)
}
const setProspectq = (params) => {
window.sessionStorage.setItem("prospectq", params)
}
useEffect(() => {
if (typeof window !== 'undefined') {
reportq = window.sessionStorage.getItem('reportq');
if (!reportq)
reportq = '';
prospectq = window.sessionStorage.getItem('prospectq');
if (!prospectq)
prospectq = '';
}
if (typeof window !== 'undefined' && page == 'report') {
const keys = Object.keys(query);
let params = "?";
for (let i = 0; i < keys.length; i++) {
params += `${keys[i]}=${encodeURIComponent(query[keys[i]])}&`
}
// console.log("eval reportq vs query", { reportq, params })
if (!reportq && keys && keys.length > 0) {
console.log("growth line 78 setting reportq")
setReportq(params)
}
else {
if (params != reportq) {
console.log("growth line 81 updating url with ", reportq)
window.history.replaceState('state', 'Growth ', `${BASE_URL}${page}${reportq}`)
}
}
}
})
const key = query.key;
const setPage = (page) => {
setP(page)
setTimeout(() => {
const newUrl = `${BASE_URL}${page == 'home' ? '' : page}${page == 'report' ? reportq : page == 'prospect' ? prospectq ? prospectq : `?key=${key}` : `?key=${key}`}`
router.push(newUrl);
}, 1);
}
return (
<ThemeProvider theme={theme}>
<>
<Head>
<title>Growth</title>
<meta name="robots" content="noindex" />
<meta name="theme-color" content="#2563FF" />
</Head>
<Layout>
<Sider
style={{
overflow: 'auto',
height: '100vh',
position: 'fixed',
left: 0,
}}
>
<Menu theme="dark" mode="inline" defaultSelectedKeys={[page]}>
<Menu.Item key="report" onClick={() => setPage('report')}>
Reports
</Menu.Item>
</Menu>
</Sider>
<Layout className="site-layout" style={{ marginLeft: 200 }}>
{p == 'report' && <Report query={query} setReportq={setReportq} />}
</Layout>
</Layout>
</>
</ThemeProvider >);
}
Growth.getInitialProps = async ({ query, res }) => {
return { query }
}
export default Growth;
1条答案
按热度按时间8xiog9wr1#
负载变化:“需要登录”才能加载:“勾选”,