gpt4 book ai didi

reactjs - 如何在移动到下一个屏幕之前等待 React useEffect Hook 完成

转载 作者:行者123 更新时间:2023-12-03 20:52:45 31 4
gpt4 key购买 nike

我遇到了在多个文件中使用钩子(Hook)的问题,并且在第一个异步方法完成之前它被调用两次用于 useEffect (这应该阻止第二个钩子(Hook)调用,但事实并非如此)。请参见以下 2 个场景:

堆栈导航器

const { context, state } = useLobby(); // Hook is called here 1st, which will do the initial render and checks

return (
<LobbyContext.Provider value={context}>
<LobbyStack.Navigator>
{state.roomId
? <LobbyStack.Screen name="Lobby" component={LobbyScreen} />
: <LobbyStack.Screen name="Queue" component={QueueScreen} />
}
</LobbyStack.Navigator>
</LobbyContext.Provider>
)

大堂 Hook
export const useLobby = () => {
const [state, dispatch] = React.useReducer(...)

//
// Scenario 1
// This get called twice (adds user to room twice)
//
React.useEffect(() => {
if (!state.isActive) assignRoom();
}, [state.isActive])

const assignRoom = async () => {
// dispatch room id
}

const context = React.useMemo(() => ({
join: () => { assignRoom(); }
})
}

队列屏幕
const { context, state } = useLobby(); // Hook is called here 2nd right after checking state from stack navigator

//
// Scenario 2
// Only does it once, however after state is changed to active
// the stack navigator didn't get re-render like it did in Scenario 1
//
React.useEffect(() => {
roomLobby.join();
}, []);

return (
...
{state.isActive
? "Show the room Id"
: "Check again"
...
)

在场景 1 中,我猜当第一个钩子(Hook)被调用并且 useEffect 正在执行异步以将用户添加到房间并将 active 设置为 true 时。同时,条件渲染部分直接移动到队列屏幕,该屏幕再次调用钩子(Hook)并执行 useEffect(因为第一个尚未完成, isActive 仍然为假)。

如何正确设置 useReducer 和 useMemo 以便它根据状态呈现屏幕。

根据答案编辑代码
/* LobbyProvider */

const LobbyContext = React.createContext();

const lobbyReducer = (state, action) => {
switch (action.type) {
case 'SET_LOBBY':
return {
...state,
isActive: action.active,
lobby: action.lobby
};
case 'SET_ROOM':
return {
...state,
isQueued: action.queue,
roomId: action.roomId,
};
default:
return state;
}
}

const LobbyProvider = ({ children }) => {
const [state, dispatch] = React.useReducer(lobbyReducer, initialState);

React.useEffect(() => {
console.log("Provider:", state)
if (!state.isActive) joinRoom();
}, [])

// Using Firebase functions
const joinRoom = async () => {
try {
const response = await functions().httpsCallable('getActiveLobby')();
if (response) {
dispatch({ type: 'SET_LOBBY', active: true, lobby: response.data })
const room = await functions().httpsCallable('assignRoom')({ id: response.data.id });
dispatch({ type: 'SET_ROOM', queue: false, roomId: room.data.id })
}
} catch (e) {
console.error(e);
}
}

return (
<LobbyContext.Provider value={{state, dispatch}}>
{ children }
</LobbyContext.Provider>
)
}


/* StackNavigator */

const {state} = React.useContext(LobbyContext);

return (
<LobbyProvider>
// same as above <LobbyStack.Navigator>
// state doesn't seem to be updated here or to re-render
</LobbyProvider>
);


/* Queue Screen */

const {state} = React.useContext(LobbyContext);

// accessing state.isActive to do some conditional rendering
// which roomId does get rendered after dispatch

最佳答案

您必须注意,自定义 Hook 将在每次调用时创建一个新的状态实例。

例如,您在 StackNavigator 组件中调用钩子(Hook),然后在 QueueScreen 中再次调用,因此将调用 2 个不同的 useReducer 而不是它们共享状态。

您应该改为在 StackNavigator 的父级中使用 useReducer,然后将其用作 useLobby Hook 中的上下文

const LobbyStateContext = React.createContext();

const Component = ({children}) => {
const [state, dispatch] = React.useReducer(...)
return (
<LobbyStateContext.Provider value={[state, dispatch]]>
{children}
</LobbyStateContext>
)
}

并像使用它一样
<Component>
<StackNavigator />
</Component>

useLobby 然后看起来像
export const useLobby = () => {
const [state, dispatch] = React.useContext(LobbyStateContext)

const assignRoom = async () => {
// dispatch room id
}

const context = React.useMemo(() => ({
join: () => { assignRoom(); }
})

return { context, assignRoom, state};
}

StackNavigator 将利用 useLobby 并具有 useEFfect 逻辑
const { context, state, assignRoom } = useLobby(); 

React.useEffect(() => {
if (!state.isActive) assignRoom();
}, [state.isActive])

return (
<LobbyContext.Provider value={context}>
<LobbyStack.Navigator>
{state.roomId
? <LobbyStack.Screen name="Lobby" component={LobbyScreen} />
: <LobbyStack.Screen name="Queue" component={QueueScreen} />
}
</LobbyStack.Navigator>
</LobbyContext.Provider>
)

关于reactjs - 如何在移动到下一个屏幕之前等待 React useEffect Hook 完成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62125737/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com