React - 即使状态变量没有变化,useEffect 也会运行

Lef*_*eff 5 fetch jwt reactjs react-devtools

我的 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)
    }
Run Code Online (Sandbox Code Playgroud)

我正在设置一个响应,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())
      })
  }, [])
Run Code Online (Sandbox Code Playgroud)

在控制台中,我只能看到“到达这里”被记录,没有其他内容,并且前端因错误而崩溃:

DevTools 无法加载源映射:无法加载 data 的内容:application/json;charset=utf-8;base64, longTokenString...:由于重新加载检查的页面而取消加载

我在这里做错了什么?

更新

我在这里发现了一个问题,我还有两个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])
Run Code Online (Sandbox Code Playgroud)

更新编号 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>
Run Code Online (Sandbox Code Playgroud)

我已经删除了不必要的错误 useEffect 函数,但现在我得到:

警告:无法对已卸载的组件执行 React 状态更新。这是一个空操作,但它表明应用程序中存在内存泄漏。要修复此问题,请取消 useEffect 清理函数中的所有订阅和异步任务。

我收到此警告,并且在获取令牌后它也没有重定向。这次我做错了什么?

Shi*_*BR2 0

这是我完整的答案。这里的主要问题是使用useEffect不正确,特别是对于依赖数组中的对象。

我们先来说一下这段代码

useEffect(() => {
  // TODO something with error
}, [error]);
Run Code Online (Sandbox Code Playgroud)

因为error是一个对象,React使用浅层比较,正如您在这个问题useEffect中看到的那样。它将使里面的代码永远运行。useEffect

下一部分,您会收到警告,因为您未以正确的方式使用重定向。只需删除useEffect它就可以了。

原因是,当我们出现错误时,您的代码catch应该运行。除此之外,jwtToken到时候也会改变。它将使您的应用程序在渲染过程完成之前重定向。