有条件地反应useEffect hook加载快照

时间:2019-09-03 13:34:31

标签: reactjs firebase google-cloud-firestore react-hooks

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

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

现在,我想向此组件添加一个侦听器,以侦听Firebase Cloud Firestore中的配置文件数据。他们为此提供了onShapshot功能。

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

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

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();
}, []);

这行不通,因为它仅运行一次(在安装时),然后由于它仍在初始化而返回任何内容,因此我无法将其设置为在每次更新时运行,因为这样它将继续查询Firestore,更新状态和查询(无限循环)。

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

1 个答案:

答案 0 :(得分:1)

  

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

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

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

以下更改将确保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]);

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

  

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

是的,可以正常工作。除非调用onSnapshot(),否则不需要取消订阅功能。