繁体   English   中英

如何防止/锁定 function 在另一个单独的异步调用解决之前返回?

[英]how to prevent/lock a function from returning until another separate async call has resolved?

我正在处理路由身份验证,并将身份验证状态存储在上下文中,以便其他 React 组件可以检查用户是否已登录。代码的相关部分是:

const [loggedInUser, setLoggedInUser] = useState(null)
const authed = () => !!loggedInUser

useEffect(() => {
  async function fetchData() {
    const response = await fetch('/loggedInUser')
      .then(res => res.json())
    setLoggedInUser(response)
  }
  fetchData()
}, [])

对这段代码的快速解释是,我需要部分代码中的用户数据(例如 id),所以我需要存储loggedInUser object。 但是,对于更简单的任务,例如检查用户是否已登录,我使用 function authed来检查loggedInUser变量是否包含 object(用户已登录)或 Z37A6259CC0C1DAE299A78664。

要检查用户是否已登录,我使用的是 passport.js,我'/loggedInUser'路线如下所示:

app.get('/loggedInUser', async (req, res) => {
  if (!req.isAuthenticated())
    return null

  const { id, name } = await req.user
    .then(res => res.dataValues)

  return res.json({ id, name })
})

问题是,在useEffect()fetch()能够命中 GET 路由并使用 API 响应setLoggedInUser(response)之前,使用此上下文并检查authed()的代码正在运行。 所以使用authed()总是返回 false,即使 API 响应稍后将loggedInUser设置为某个 object 值,现在authed()为 true。 显然这是一个竞争条件,但我不知道如何解决它。

是否有一个优雅的解决方案,我可以“锁定” authed()从返回值,直到 useEffect() 完全“设置”了loggedInUser的 state ?

我设想的一个(可怕的)解决方案可能是这样的:

const [loggedInUser, setLoggedInUser] = useState(null)
const isFinishedLoading = false // <--
function authed() {
  while (!isFinishedLoading) {} // a crude lock
  return !!loggedInUser
}

useEffect(() => {
  async function fetchData() {
    const response = await fetch('/loggedInUser')
      .then(res => res.json())
    setLoggedInUser(response)
    
    isFinishedLoading = true // <--
  }
  fetchData()
}, [])

有没有更好的方法来“锁定” function authed()直到加载完成?


编辑:

为了澄清我对authed()评论的用法,这里是我的App.js的精简版

export default function App() {
  return (
    <>
      <AuthProvider>
        <Router className="Router">
          <ProtectedRoute path="/" component={SubmittalTable} />
          <Login path="/login" />
        </Router>
      </AuthProvider>
    </>
  )
}

function ProtectedRoute({ component: Component, ...rest }){
  const { authed } = useContext(AuthContext)

  if (!authed()) // due to race condition, authed() is always false
    return (<Redirect from="" to="login" noThrow />)

  return (<Component {...rest} />)
}

我不明白为什么您需要authed()成为 function,并且不要直接使用isLoggedIn 另外我看不到您在哪里设置Context 值,但无论如何......

一般建议

一般来说:在 React 中,试着去想

  • “在任何给定时刻我的应用程序的 state 是什么”
  • 而不是“应该按什么顺序发生”

在你的情况下:

  • "根据状态,现在应该显示哪个页面",
  • 而不是“一旦发生某事就重定向”。

用户有权或无权任何给定时刻使用您的应用程序。 那是state ,您可以将此 state 存储在某处。 我们称之为 state isAuthorized

state 不同地方存放

  1. 您可以将isAuthorized存储在Context中,只要您知道它在需要时可用。 如果 Context 在您想知道用户是否被授权时不可用(在您的应用程序中似乎就是这种情况),那么您不能使用 Context 来存储isAuthorized (至少不能单独使用)。

  2. 您可以在每次需要时获取isAuthorized 然后isAuthorized在 fetch 响应之前不可用。 现在您的应用程序的 state 是什么? 它还没有notReady (可能)。 您可以将 state notReady存储在某处,例如再次在上下文中。 notReady最初总是为true ,因此只有当您明确表示时,您才知道应用程序已准备就绪。)应用程序可能会显示 Spinner 并且只要它不是notReady就不会执行任何其他操作。

  3. 您可以将isAuthorized存储在例如浏览器存储(例如sessionStorage )中。 它们在页面加载时可用,因此您不必每次都获取 state。 浏览器存储应该是同步的,但实际上我会将它们视为异步的,因为我读过的有关浏览器存储的内容并没有激发信心。

问题与解决方案

您要做的是将isAuthorized存储在 (1) Context AND (2) 每次都获取它,因此您有 2 个需要同步的状态。 无论如何,您确实需要在开始时至少获取一次isAuthorized ,否则,该应用程序还没有准备好使用。 因此,您确实需要同步和 state (3) notReady (或isReady )。

同步 state 是使用 React 中的useEffect (或使用“依赖项”)完成的,例如:

useEffect(() => {
  setIsFinishedLoading( false );    // state (3) "app is ready"
  fetchData().then( response => {
    setLoggedInUser( response );    // state (2) "isAuthorized" from fetch
    setIsFinishedLoading( true );
  }).catch( error => {
    setLoggedInUser( null );
    setIsFinishedLoading( true );
  });
}, []);

useEffect(() => {
  if( isFinishedLoading ){
    setIsAuthorized( !!response );      // state (1) "isAuthorized" in local state or Context
  }
  
}, [ isFinishedLoading, response ]);

“阻止”

无论如何,您可能都不需要它,但是:

在 Javascript 中无法阻止这种意义上的代码执行。 您将改为在其他时间执行代码,例如使用 Promises。 这再次需要以稍微不同的方式思考。

你不能这样做:

function authed(){
  blockExecutionBasedOnCondition
  return !!loggedInUser
}

但你可以这样做:

function authed(){
  return !!loggedInUser;
}

function executeAuthed(){
  someConditionWithPromise.then( result => {
    authed();
  });
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM