有条件地react useEffect 钩子加载 onsnapshot

IT技术 reactjs firebase google-cloud-firestore react-hooks
2021-05-27 17:15:29

我正在尝试执行以下操作,但似乎无法获得最合适的解决方案:在我的 React 应用程序(应用程序组件)的顶层,我正在加载 firebase 和 firestore。我有一个存储我的身份验证信息的react上下文。身份验证上下文不会立即加载,而是在一段时间后加载。我正在使用 onAuthStateChanged(来自 firebase)来等待它。我的代码如下所示:

const firebase = new Firebase(); //custom class, instantiates firebase
const [authData, setAuthData] = useState({
  firebase,
  user: null,
  isInitializing: true,
  userdata: { info: null, data: null }
});

useEffect(() => {
  // listen for auth state changes
  const unsubscribe = firebase.auth.onAuthStateChanged(async returnedUser => {
    //set Auth data into state to pass into context
    setAuthData({ ...authData, user: returnedUser, isInitializing: false });
  });
  // unsubscribe to the listener when unmounting
  return () => unsubscribe();
}, []);

现在我想向这个组件添加一个监听器来监听 firebase cloud firestore 中的配置文件数据。他们为此提供了一个 onShapshot 函数。

在哪里添加该侦听器?因为不想打太多电话。我可以安全地将侦听器放在取消订阅功能中吗?卸载时如何确保侦听器订阅?

首先,我尝试了一个额外的 useEffect 钩子,如下所示:

useEffect(() => {
  if (authData.isInitializing || authData.user === null) return;
  const ref = firebase.db.doc("/env/" + process.env.REACT_APP_FIRESTORE_ENVIRONMENT + "/users/" + authData.user.uid);
  return ref.onSnapshot(doc => {
    setAuthData({ ...authData, userinfo: doc.data() });
  });
}, []);

这不起作用,因为它只运行一次(挂载时),然后它什么都不返回,因为它仍在初始化,我不能将它设置为在每次更新时运行,因为它会不断查询 firestore、更新状态和查询(无限循环) )。

在上下文中传递状态有效,我可以在任何地方使用它(身份验证状态)。我只是希望能够在上下文中获得基本的用户数据。现在,我可以手动添加它(当用户登录时,获取用户配置文件 + 数据并存储在状态中),但我也想处理此数据的更新,因此我需要 onSnapshot 侦听器。如何确保我只给听众打电话一次?

1个回答

在上下文中传递状态有效,我可以在任何地方使用它(身份验证状态)。我只是希望能够在上下文中获得基本的用户数据。现在,我可以手动添加它(当用户登录时,获取用户配置文件 + 数据并存储在状态中),但我也想处理此数据的更新,因此我需要 onSnapshot 侦听器。如何确保我只给听众打电话一次?

听起来您想在React.useEffect(). 的第二个参数React.useEffect()是依赖项数组。当这些依赖项中的任何一个发生变化时,效果将再次执行。

如果您使用的是 eslint,当您使用钩子捕获闭包中的变量但不将这些变量放入 deps 数组时react-hooks eslint 插件将突出显示。

以下更改将确保每个组件onSnapshot仅添加一次

useEffect(() => {
  if (authData.isInitializing || authData.user === null) return;
  const ref = firebase.db.doc("/env/" + process.env.REACT_APP_FIRESTORE_ENVIRONMENT + "/users/" + authData.user.uid);
  return ref.onSnapshot(doc => {
    setAuthData({ ...authData, userinfo: doc.data() });
  });
}, [authData.isInitializing, authData.user]);

执行的提取数量将与您拥有的组件数量成线性比例。如果您预计此钩子会被多次使用,您可能需要考虑一种模型,该模型firebase.db.doc() 仅在一个组件中调用调用,但许多组件可以订阅该存储。这与 React Redux 的工作方式非常相似,我建议您查看他们的代码以了解如何实现这一点。

哦,如何以这种方式返回(订阅监听器)?我可以按照我在原始第二个代码块中写的那样做吗?那么如果 isInitializing === true --> 就返回,否则返回取消订阅功能?

是的,那会很好用。除非onSnapshot()被调用,否则不需要取消订阅功能