# #reactjs #google-cloud-firestore #react-hooks
Вопрос:
Я не уверен, почему, но по какой-то причине у меня нет значения старого состояния при вызове обновления, кажется, что оно всегда имеет значение состояния по умолчанию.
const defaultState:RootState = {
loadSessions:(groupId:string,lectureId:string)=>{},
loadSession:(sessionId:string)=>()=>{},
userJoined:(user:IUser)=>{}
};
const TSessionContext = React.createContext<RootState>(defaultState);
export const TSessionProvider: FC = ({ children }) => {
const [state, setState] = useState<RootState>(defaultState);
const loadSession = (sessionId:string)=>{
console.info("Calling load session");
const unsubSession = db.collection(COLLECTION_REF).doc(sessionId)
.onSnapshot((snapshot) => {
const session = snapshot.data() as ISession;
console.info("setting session",state);
setState({
...state,
session
});
});
const unsubUsers = db.collection(`${COLLECTION_REF}/${sessionId}/users`)
.onSnapshot((querySnapshot) => {
const users:IUser[] = [];
querySnapshot.forEach((doc) => {
users.push(doc.data() as IUser)
});
console.info("setting users",state);
setState({
...state,
users
});
});
return ()=>{
unsubUsers();
unsubSession();
};
}
return (
<TSessionContext.Provider
value={{
...state,
loadSession,
}}
>
{children}
</TSessionContext.Provider>
);
};
export const useTSession = () => useContext(TSessionContext);
Здесь я всегда получаю вывод «настройка сеанса» со значением состояния по умолчанию и «настройка пользователей» со значением состояния по умолчанию.
Код, в котором я использую этот крючок:
const {loadSession,session,users} = useTSession();
useEffect(()=>{
if(sessionIdamp;amp;!session){
const unsub = loadSession(sessionId);
return () => {
unsub()
}
}
},[]);
Затем в моем основном компоненте я session
сначала получаю с user
неопределенным.
А затем user
с session
неопределенным.
Если я добавлю еще один крючок, который извлекает больше данных, то возникнет та же проблема. Так что я действительно не уверен, в чем здесь проблема.
Если я разделю свои крючки, внутреннее состояние будет таким:
const [iSession, setISession] = useState<any>();
const [iUser, setIUser] = useState<any>();
return (
<TSessionContext.Provider
value={{
...state,
users:iUser,
session:iSession,
loadSession,
}}
>
{children}
</TSessionContext.Provider>
);
Тогда это, кажется, работает правильно, не знаю, почему другой вариант не работает.
Ответ №1:
setState является асинхронным, поэтому, когда вы вызываете их таким образом, вы перезаписываете предыдущее состояние. Я не видел шаблона, подобного тому, который вы используете, но вам не следует дважды пытаться записать состояние в одной и той же функции, так как вы столкнетесь с проблемами, которые, возможно, вы могли бы обновить до этого
const loadSession = async(sessionId:string)=>{
const newState = {}
console.info("Calling load session");
const unsubSession = await db.collection(COLLECTION_REF).doc(sessionId)
.onSnapshot((snapshot) => {
const session = snapshot.data() as ISession;
console.info("setting session",state);
newState.session = session
});
const unsubUsers = await db.collection(`${COLLECTION_REF}/${sessionId}/users`)
.onSnapshot((querySnapshot) => {
const users:IUser[] = [];
querySnapshot.forEach((doc) => {
users.push(doc.data() as IUser)
});
console.info("setting users",state);
newState.users = users
});
setState({
...state,
...newState,
});
return ()=>{
unsubUsers();
unsubSession();
};
}
Комментарии:
1. Хм, но onSnapshot тоже асинхронен. Таким образом, в этом случае может быть вероятность того, что коллекция пользователей будет получена до сеанса. Или я ошибаюсь?
2. да, вы правы, сделайте функцию асинхронной и добавьте ожидание, извините, что пропустил это
3. другой более простой ответ-разделить состояние на const [сеанс, сеанс] = useState(null) const [пользователи, пользователи] = useState(null) Таким образом, вам не придется беспокоиться
4. Да, я сделал это, и это сработало. Я просто хотел понять, почему версия с одним состоянием не работает.
5. это потому, что он асинхронен, поэтому состояние …смотрит на текущее состояние до запуска функции, поэтому второе завершение будет заменено первым завершенным. Я надеюсь, что мой первоначальный ответ правильно объяснил это