React - useEffect 运行 即使状态变量没有变化
React - useEffect running even when there was no change in state variable
我的 kotlin 应用程序中有一个端点,如下所示:
either.eager<String, Unit> {
val sessionAndCookieUser = commonAuth.decryptCookieGetUser(getCookie(context), ::userTransform).bind()
val user = sessionAndCookieUser.session.user
val ctx = Ctx(ds, SystemSession, conf)
val dbUser = getUserEither(ctx, user.id).bind()
val signatureAlgorithm = SignatureAlgorithm.HS256
val signingKey = SecretKeySpec(conf.get(ZendeskJWTSecret).toByteArray(), signatureAlgorithm.jcaName)
val iat = Date(System.currentTimeMillis())
val exp = Date(System.currentTimeMillis() + 7 * 24 * 60 * 60 * 1000)
val token = Jwts.builder()
.claim("name", dbUser.name)
.claim("email", dbUser.email)
.setIssuer(conf.get(StreamAppName))
.setIssuedAt(iat)
.setExpiration(exp)
.signWith(signingKey, signatureAlgorithm)
.compact()
context.setResponseCode(StatusCode.OK)
.setResponseType("application/json")
.send(jsonObject("token" to token).toString())
}.mapLeft {
context.setResponseCode(StatusCode.UNAUTHORIZED)
}
我正在设置一个响应,如果用户通过身份验证,我应该发送 jsonObject
,如果用户未通过身份验证,我应该发送 UNAUTHORIZED
。
当我在浏览器中测试此端点时,我只得到该请求的 status unknown
- 当我调试后端时,否则我得到 200
没有响应数据。
如果我在邮递员中测试它,我会得到 json 作为回应。
我看到正在构建令牌,后端方面一切看起来都很好,但是浏览器中没有加载响应。
我是这样从反应中获取它的:
export const fetchGet = (uriPath: string) =>
fetch(fullUrl(uriPath), {
method: 'GET',
credentials: 'include'
})
useEffect(() => {
console.log('got here')
fetchGet('/auth/token')
.then(res => {
console.log('res ', res)
return res.json()
})
.then(res => {
console.log('res.json ', res)
return res.ok ? setJwtToken(res.token) : Promise.reject(res.statusText)
})
.catch(error => {
console.log('err ', error)
setError(error.toString())
})
}, [])
在控制台中,我只能看到 'got here' 正在记录,没有其他内容,并且前端因错误而崩溃:
DevTools failed to load source map: Could not load content for
data:application/json;charset=utf-8;base64, longTokenString...:
Load canceled due to reload of inspected page
我做错了什么?
已更新
我在这里发现了一个问题,我还有 2 个 useEffect
函数,在我得到结果之前它们正在重定向。我不确定为什么 useEffect
函数在我传递错误状态变量 运行 时与初始状态没有变化?
完整代码如下:
const [jwtToken, setJwtToken] = useState(null)
const [error, setError] = useState(null)
useEffect(() => {
fetchGet('/auth/token')
.then(async res => {
const data = await res.json()
if (!res.ok) {
const error = data?.message || res.statusText
return Promise.reject(error)
}
return data
})
.then(({token}) => setJwtToken(token))
.catch(err => {
console.log('err ', err)
setError(err.toString())
})
}, [])
useEffect(() => {
if (jwtToken) {
// window.location.href = `/mypage.com?access/jwt?jwt=${jwtToken}&return_to=`
console.log(jwtToken)
}
}, [jwtToken])
useEffect(() => {
console.log(error)
//window.location.href = '/login'
}, [error])
更新编号。 2:
const [jwtToken, setJwtToken] = useState('')
const { search } = useLocation()
useEffect(() => {
fetchGet('/auth/token')
.then(async res => {
const data = await res.json()
if (!res.ok) {
const error = data?.message || res.statusText
return Promise.reject(error)
}
return data
})
.then(({token}) => setJwtToken(token))
.catch(() => window.location.href = '/login')
}, [])
useEffect(() => {
const params = new URLSearchParams(search)
const returnTo = params.get('return_to') ? `&return_to=${params.get('return_to')}` : ''
jwtToken !== '' ? window.location.href = `${url}/jwt?jwt=${jwtToken}${returnTo}` : null
}, [jwtToken])
return <p>Authenticating ...</p>
我已经删除了不必要的错误 useEffect 函数,但现在我得到:
Warning: Can't perform a React state update on an unmounted component.
This is a no-op, but it indicates a memory leak in your application.
To fix, cancel all subscriptions and asynchronous tasks in a useEffect
cleanup function.
我收到此警告,并且在获取令牌后它也没有重定向。这次我做错了什么?
每个 useEffect 回调都将在第一次挂载时调用。您应该包括一个简单的 if 语句以确保在 运行 您的错误处理逻辑之前设置错误。
useEffect(() => {
if(error) {
console.log(error)
//window.location.href = '/login'
}
}, [error])
您的 API 的 CORS 配置可能存在问题。
Access-Control-Allow-Origin
响应 header 必须设置为你的 React 应用程序的来源(它不能是 *
对于有凭证的请求)并且 Access-Control-Allow-Credentials
必须是 true
.不包括它们将导致不透明的响应。
https://fetch.spec.whatwg.org/#cors-protocol-and-credentials
这是我完成的答案。这里的主要问题是 useEffect
的使用不正确,尤其是依赖数组中的对象。
先说这段代码
useEffect(() => {
// TODO something with error
}, [error]);
因为 error
是一个对象,而 React useEffect
使用浅层比较,正如您在 中看到的那样。它将使 useEffect
中的代码永远 运行。
下一部分,您会收到警告,因为您对重定向的使用不正确。只需删除 useEffect
即可。
原因是,当我们出现错误时,您 catch
中的代码应该 运行。除此之外, jwtToken
也会在那个时候改变。它将使您的应用程序在渲染过程完成之前重定向。
我的 kotlin 应用程序中有一个端点,如下所示:
either.eager<String, Unit> {
val sessionAndCookieUser = commonAuth.decryptCookieGetUser(getCookie(context), ::userTransform).bind()
val user = sessionAndCookieUser.session.user
val ctx = Ctx(ds, SystemSession, conf)
val dbUser = getUserEither(ctx, user.id).bind()
val signatureAlgorithm = SignatureAlgorithm.HS256
val signingKey = SecretKeySpec(conf.get(ZendeskJWTSecret).toByteArray(), signatureAlgorithm.jcaName)
val iat = Date(System.currentTimeMillis())
val exp = Date(System.currentTimeMillis() + 7 * 24 * 60 * 60 * 1000)
val token = Jwts.builder()
.claim("name", dbUser.name)
.claim("email", dbUser.email)
.setIssuer(conf.get(StreamAppName))
.setIssuedAt(iat)
.setExpiration(exp)
.signWith(signingKey, signatureAlgorithm)
.compact()
context.setResponseCode(StatusCode.OK)
.setResponseType("application/json")
.send(jsonObject("token" to token).toString())
}.mapLeft {
context.setResponseCode(StatusCode.UNAUTHORIZED)
}
我正在设置一个响应,如果用户通过身份验证,我应该发送 jsonObject
,如果用户未通过身份验证,我应该发送 UNAUTHORIZED
。
当我在浏览器中测试此端点时,我只得到该请求的 status unknown
- 当我调试后端时,否则我得到 200
没有响应数据。
如果我在邮递员中测试它,我会得到 json 作为回应。
我看到正在构建令牌,后端方面一切看起来都很好,但是浏览器中没有加载响应。
我是这样从反应中获取它的:
export const fetchGet = (uriPath: string) =>
fetch(fullUrl(uriPath), {
method: 'GET',
credentials: 'include'
})
useEffect(() => {
console.log('got here')
fetchGet('/auth/token')
.then(res => {
console.log('res ', res)
return res.json()
})
.then(res => {
console.log('res.json ', res)
return res.ok ? setJwtToken(res.token) : Promise.reject(res.statusText)
})
.catch(error => {
console.log('err ', error)
setError(error.toString())
})
}, [])
在控制台中,我只能看到 'got here' 正在记录,没有其他内容,并且前端因错误而崩溃:
DevTools failed to load source map: Could not load content for data:application/json;charset=utf-8;base64, longTokenString...: Load canceled due to reload of inspected page
我做错了什么?
已更新
我在这里发现了一个问题,我还有 2 个 useEffect
函数,在我得到结果之前它们正在重定向。我不确定为什么 useEffect
函数在我传递错误状态变量 运行 时与初始状态没有变化?
完整代码如下:
const [jwtToken, setJwtToken] = useState(null)
const [error, setError] = useState(null)
useEffect(() => {
fetchGet('/auth/token')
.then(async res => {
const data = await res.json()
if (!res.ok) {
const error = data?.message || res.statusText
return Promise.reject(error)
}
return data
})
.then(({token}) => setJwtToken(token))
.catch(err => {
console.log('err ', err)
setError(err.toString())
})
}, [])
useEffect(() => {
if (jwtToken) {
// window.location.href = `/mypage.com?access/jwt?jwt=${jwtToken}&return_to=`
console.log(jwtToken)
}
}, [jwtToken])
useEffect(() => {
console.log(error)
//window.location.href = '/login'
}, [error])
更新编号。 2:
const [jwtToken, setJwtToken] = useState('')
const { search } = useLocation()
useEffect(() => {
fetchGet('/auth/token')
.then(async res => {
const data = await res.json()
if (!res.ok) {
const error = data?.message || res.statusText
return Promise.reject(error)
}
return data
})
.then(({token}) => setJwtToken(token))
.catch(() => window.location.href = '/login')
}, [])
useEffect(() => {
const params = new URLSearchParams(search)
const returnTo = params.get('return_to') ? `&return_to=${params.get('return_to')}` : ''
jwtToken !== '' ? window.location.href = `${url}/jwt?jwt=${jwtToken}${returnTo}` : null
}, [jwtToken])
return <p>Authenticating ...</p>
我已经删除了不必要的错误 useEffect 函数,但现在我得到:
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
我收到此警告,并且在获取令牌后它也没有重定向。这次我做错了什么?
每个 useEffect 回调都将在第一次挂载时调用。您应该包括一个简单的 if 语句以确保在 运行 您的错误处理逻辑之前设置错误。
useEffect(() => {
if(error) {
console.log(error)
//window.location.href = '/login'
}
}, [error])
您的 API 的 CORS 配置可能存在问题。
Access-Control-Allow-Origin
响应 header 必须设置为你的 React 应用程序的来源(它不能是 *
对于有凭证的请求)并且 Access-Control-Allow-Credentials
必须是 true
.不包括它们将导致不透明的响应。
https://fetch.spec.whatwg.org/#cors-protocol-and-credentials
这是我完成的答案。这里的主要问题是 useEffect
的使用不正确,尤其是依赖数组中的对象。
先说这段代码
useEffect(() => {
// TODO something with error
}, [error]);
因为 error
是一个对象,而 React useEffect
使用浅层比较,正如您在 useEffect
中的代码永远 运行。
下一部分,您会收到警告,因为您对重定向的使用不正确。只需删除 useEffect
即可。
原因是,当我们出现错误时,您 catch
中的代码应该 运行。除此之外, jwtToken
也会在那个时候改变。它将使您的应用程序在渲染过程完成之前重定向。