有条件地反应useEffect钩子加载快照 [英] React useEffect hook load onsnapshot conditionally

查看:38
本文介绍了有条件地反应useEffect钩子加载快照的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to do the following and cannot seem to get the most suitable solution: On the top level of my React app (App component) I am loading firebase and firestore. I have a react context where my auth info is stored in. The auth context is not immediately loaded but after some time. I am using the onAuthStateChanged (from firebase) to wait for it. My code looks as follows:

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功能.

Now I want to add a listener to this component to listen for profile data in firebase cloud firestore. They provide an onShapshot function for that.

在何处添加该侦听器?因为我不想打太多电话.我可以安全地将侦听器置于取消订阅功能吗?卸载时如何确保侦听器ubsubscribe?

Where to add that listener? Because I dont want to do too many calls. Can I safely place the listener in the unsubscribe function? How to make sure the listener ubsubscribes when unmounting?

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

First I tried with an additional useEffect hook, like this:

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,更新状态和查询(无限循环).

This doesnt work because it only runs once (on mount), and then it just returns nothing because it is still initializing, and I cannot set it to run on every update because then it keeps querying firestore, updating state, and query (endless loop).

在上下文中传递状态是可行的,我可以在任何地方使用它(身份验证状态).我只希望能够在上下文中具有基本的用户数据.现在,我可以手动添加它(当用户登录时,获取用户个人资料+数据并存储在状态中),但是我也想处理这些数据的更新,因此我需要onSnapshot侦听器.如何确保我只调用一次监听器?

Passing down the state in the context works and I can use it anywhere (auth state). I just want to be able to have the basic user data in context. Now, I could add this manually (when user logs in, get the user profile + data and store in state), but I want to handle updates on this data as well, so I need the onSnapshot listener. How to make sure I call the listener only once?

推荐答案

在上下文中传递状态是可行的,我可以在任何地方使用它(身份验证状态).我只希望能够在上下文中具有基本的用户数据.现在,我可以手动添加它(当用户登录时,获取用户个人资料+数据并存储在状态中),但是我也想处理这些数据的更新,因此我需要onSnapshot侦听器.如何确保我只调用一次监听器?

Passing down the state in the context works and I can use it anywhere (auth state). I just want to be able to have the basic user data in context. Now, I could add this manually (when user logs in, get the user profile + data and store in state), but I want to handle updates on this data as well, so I need the onSnapshot listener. How to make sure I call the listener only once?

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

It sounds like you want to utilise the array argument in React.useEffect(). The second argument to React.useEffect() is an array of dependencies. When any of those dependencies change, the effect will be executed again.

如果您使用的是eslint,请 react-hooks eslint插件将突出显示.

If you're using eslint, the react-hooks eslint plugin will highlight when you use hooks that capture variables in a closure but do not put those variables in the deps array.

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

The following change would ensure that onSnapshot is only added once per component:

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]);

执行的取数将与您拥有的组件数成线性比例.如果您预计此挂钩将被多次使用,则可能需要考虑一个模型,其中仅在 one 组件中调用 firebase.db.doc()调用,但是许多组件都可以订阅商店.这与React Redux的工作原理非常相似,我建议您看一下他们的代码,以了解如何实现这一目标.

The number of fetches executed would scale linearly with the number of components you have. If you anticipate this hook becoming used more than once, you may want to instead consider a model where the firebase.db.doc() call is only called in one component, but many components can subscribe to the store. This is very similar to how React Redux works and I would recommend taking a look at their code to get an idea of how to accomplish that.

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

Oh and how to return (ubsubscribe listener) this way? Can I just do it as I wrote in my original second codeblock? So if isInitializing === true --> just return, and else return unsubscribe function?

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

Yes, that will work fine. There is no unsubscription function required unless onSnapshot() is called.

这篇关于有条件地反应useEffect钩子加载快照的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆