在 Firebase React Native 和 ASyncStorage 中更新用户个人资料照片时出现问题

问题描述 投票:0回答:1

我尝试更新登录用户的个人资料照片,我使用 Asyncstorage 登录会话,并使用 APP.js 中的状态重置它,但在另一个选项卡中,我使用 Asyncstorage 登录会话,但是当更新个人资料照片时出现此错误可能未处理的承诺拒绝(id:4): 类型错误:未定义不是函数

这是我的App.js

const AuthenticatedUserProvider = ({ children }) => {
  const [user, setUser] = useState(null);

  return (
    <AuthenticatedUserContext.Provider value={{ user, setUser }}>
      {children}
    </AuthenticatedUserContext.Provider>
  );
};

function RootNavigator() {
  const auth = getAuth();
  const { user, setUser } = useContext(AuthenticatedUserContext);
  const [sesiones, setSesiones] = useState(null);

  useEffect(() => {
    const storeData = async (user) => {
      try {
        await AsyncStorage.setItem("cookie", JSON.stringify(user));
      } catch (e) {}
    };

    const obtenerSesion = async () => {
      try {
        const session = await AsyncStorage.getItem("cookie");
      setSesiones(JSON.parse(session));
      } catch (e) {}
    };

    if (user) {
      storeData(user);
      obtenerSesion();
    }

    if (!user) {
      obtenerSesion();
    }

    const unsubscribe = onAuthStateChanged(auth, async (authenticatedUser) => {
      authenticatedUser ? setUser(authenticatedUser) : setUser(null);
    });

    return () => unsubscribe();
  }, [user]);


  return (
    <NavigationContainer>
      {user || sesiones ? <AppNavigation /> : <GuestNavigation />}
    </NavigationContainer>
  );
}

export default function App() {
  return (
    <>
      <AuthenticatedUserProvider>
        <RootNavigator />
      </AuthenticatedUserProvider>

      <Toast />
    </>
  );
}

这是Profile.js 从 profile.js 我通过 Asyncstorage 获取用户的会话,问题是当我想更新个人资料照片时,他的博客告诉你它是这样完成的: updateProfile(auth.currentUser, { photoURL: imageUrl } );

但是我通过 Asyncstorage 获得了会话:

updateProfile(photoURLs, { photoURL: imageUrl });

photoURLs 是保存用户会话的状态,但是在更新时它给了我这个错误: 警告可能未处理的承诺拒绝(id:3): 类型错误:未定义不是函数

 const uploadImage = async (uri) => {
    setLoading(true);
    const imageBlob = [];
    const metadata = {
      contentType: "image/jpeg",
    };
    const img = await fetch(uri);
    const blob = await img.blob();
    const storage = getStorage();
    const archivoRef = ref(storage, `avatar/${id()}`);
    uploadBytes(archivoRef, blob, metadata).then((snapshot) => {
      updatePhotoAvatar(snapshot.metadata.fullPath);
    });
  };

  const updatePhotoAvatar = async (imagePath) => {
    try {
      const storage = getStorage();
      const archivoRef = ref(storage, imagePath);
      const imageUrl = await getDownloadURL(archivoRef);
      updateProfile(photoURLs, { photoURL: imageUrl });//error line
      setAvatar(imageUrl);
      setLoading(false);
    } catch (error) {
      console.log(error);
    }
  };

当我使用 Asycnstorage 进行会话时,我无法更新个人资料照片,因此使用显示名称时,我会收到相同的错误。 React Native 和 Expo 中的代码

firebase react-native expo asyncstorage
1个回答
0
投票

在您的代码中,您已确定这一行是罪魁祸首。

updateProfile(photoURLs, { photoURL: imageUrl });

此行创建了一个浮动 Promise,因为您错过了

await
语句。此外,尚不清楚
photoURLs
是什么 - 它是一个物体吗?这是一个
auth.User
物体

为了使

updateProfile
正常工作,您必须传入
auth.User
对象。

基于这一行,您有时会传入一个

auth.User
对象,有时会传入一个普通 JavaScript 对象(这是 auth.User
JSON 可序列化版本):

user || sesiones ? <AppNavigation /> : <GuestNavigation />

为了有效地处理这个问题,我们应该查看您的身份验证相关代码并对其进行修改,以便更加清晰。


首先,您应该将

onAuthStateChanged
逻辑移至
AuthenticatedUserProvider
组件中。这节省了在组件外部处理
setUser
的麻烦,这可能会导致令人困惑的行为。

// ./components/AuthenticatedUserContext.jsx
import { createContext, useContext, useState } from "react";
import { getAuth, User } from "firebase/auth";

export const AuthenticatedUserContext = createContext(null);

export default function AuthenticatedUserProvider({ children }) {
  // define state
  const auth = getAuth();
  const [user, setUser] = useState(() => auth.currentUser || undefined);
  
  // define helpers
  const loading = user === undefined;
  /** @type {Promise<User | null>} */
  const getUser = () => new Promise(resolve => { // <-- this helper is for later
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      unsubscribe();
      resolve(user);
    });
  });

  // define listeners
  // Note: You may want to swap out for onIdTokenChanged (by replacing
  // "onAuthStateChanged" with "onIdTokenChanged") depending on your use case.
  // Swapping means profile updates and token refreshes are detected, not just
  // sign-in and sign-out events.
  useEffect(() => onAuthStateChanged(auth, setUser), []);

  return (
    <AuthenticatedUserContext.Provider
      value={{
        getUser,
        loading,
        /** @type {User | null} */
        user: user || null
      }}>
      {children}
    </AuthenticatedUserContext.Provider>
  );
}

在上面的代码中,这一行:

useEffect(() => onAuthStateChanged(auth, setUser), []);

替换:

useEffect(() => {
  // ... this code is being moved ...

  const unsubscribe = onAuthStateChanged(auth, async (authenticatedUser) => { // <-- shouldn't be async
    authenticatedUser ? setUser(authenticatedUser) : setUser(null);
  });

  return () => unsubscribe();
}, [user]); // <-- this should be [], not [user]

接下来,您应该遵循为该上下文编写自定义挂钩的最佳实践,而不是使用

useContext(AuthenticatedUserContext)
。在下面的代码中,我定义了一个名为
useAuth()
的钩子(而不是更长的
useAuthenticatedUser()
):

// ./components/AuthenticatedUserContext.jsx

/* code from above here */

export function useAuth() {
  const context = useContext(AuthenticatedUserContext);
  if (context === undefined) throw new Error("Missing parent AuthenticatedUserProvider");
  return context;
}

接下来,因为您的

RootNavigator
正在将
auth.User
对象存储到
AsyncStorage
中,所以您应该将此逻辑移至其自己的钩子中,或者使其成为
AuthenticatedUserContext
useAuth()
的一部分。我将选择将其放入自己的钩子中,因为这意味着输出
user
对象始终可以是 auth.User 对象
JSON 可序列化版本
。如果需要实际的
auth.User
对象(如
updateProfile
中所示),我们将通过
firebaseUser
使其可访问。

在此代码块中,请注意“从 AsyncStorage 读取”和“更新 AsyncStorage”逻辑如何在其自己的使用效果调用中保持分离。这是因为我们只需要从 AsyncStorage 读取一次,但我们可能需要多次更新它。

// ./components/AuthenticatedUserContext.jsx

/* code from above here */

// TODO: When user is signed out, clear cookie from AsyncStorage and set sesiones to `null`
export function useAuthWithAsyncStorage() {
  const { getUser, loading: firebaseLoading, user } = useAuth();
  const [sesiones, setSesiones] = useState(null);

  // define helpers
  const loading = firebaseLoading && sesiones == null;

  // handles keeping AsyncStorage#cookie updated
  useEffect(() => {
    if (!user) return; // do nothing, no user to store

    const objectUser = user.toJSON();
    const storedSesiones = JSON.stringify(objectUser);

    // store sesiones object
    AsyncStorage.setItem("cookie", storedSesiones)
      .catch((e) => { /* ignore */ });

    // update sesiones object
    setSesiones(objectUser);
  }, [user]);

  // handles reading from AsyncStorage#cookie once, only when first attached.
  // any further updates are handled by the useEffect above.
  useEffect(() => {
    let unsubscribed = false;

    AsyncStorage.getItem("cookie")
      .then((value) => {
        if (!unsubscribed) return; // unsubscribed, do nothing
        setSesiones(JSON.parse(value))
      })
      .catch((e) => { /* ignore */ });

    return () => unsubscribed = true;
  }, []);
  
  return {
    firebaseUser: user, // pass through in case some of its methods are needed
    getUser,
    loading,
    user: user && user.toJSON() || sesiones
  }
}

现在通过

AuthenticatedUserContext
处理此问题,您可以将
RootNavigator
组件简化为:

// ./components/RootNavigator.jsx
import { useState } from "react";
import { useAuthWithAsyncStorage } from "./AuthenticatedUserContext";

export default function RootNavigator() {
  const { user } = useAuthWithAsyncStorage();

  return (
    <NavigationContainer>
      {user ? <AppNavigation /> : <GuestNavigation />}
    </NavigationContainer>
  );
}
// ./App.jsx
import AuthenticatedUserProvider from "./components/AuthenticatedUserContext";

export default function App() {
  return (
    <>
      <AuthenticatedUserProvider>
        <RootNavigator />
      </AuthenticatedUserProvider>

      <Toast />
    </>
  );
}

回到您的问题组件,您现在可以将代码更新为:

const { getUser } = useAuthWithAsyncStorage();

// Note: `firebaseUser` may be null if the user is signed out or
// Firebase is still initializing, so we use getUser instead. This
// guarantees that we can talk to Firebase to perform the update.

const updatePhotoAvatar = async (imagePath) => {
  try {
    const firebaseUser = await getUser();
    if (!firebaseUser) throw new Error("Not logged in on server!");

    const storage = getStorage();
    const archivoRef = ref(storage, imagePath);
    const imageUrl = await getDownloadURL(archivoRef);
    await updateProfile(firebaseUser, { photoURL: imageUrl });
    setAvatar(imageUrl);
    setLoading(false);
  } catch (error) {
    console.log(error);
  }
};
© www.soinside.com 2019 - 2024. All rights reserved.