![](/img/trans.png)
[英]Using RxJS how to buffer function calls until an other async function call has resolved
[英]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 isAuthorized
。
您可以将isAuthorized
存储在Context中,只要您知道它在需要时可用。 如果 Context 在您想知道用户是否被授权时不可用(在您的应用程序中似乎就是这种情况),那么您不能使用 Context 来存储isAuthorized
(至少不能单独使用)。
您可以在每次需要时获取isAuthorized
。 然后isAuthorized
在 fetch 响应之前不可用。 现在您的应用程序的 state 是什么? 它还没有notReady
(可能)。 您可以将 state notReady
存储在某处,例如再次在上下文中。 ( notReady
最初总是为true
,因此只有当您明确表示时,您才知道应用程序已准备就绪。)应用程序可能会显示 Spinner 并且只要它不是notReady
就不会执行任何其他操作。
您可以将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.