如何在Redux应用中从Firebase制定多个退订功能

时间:2020-04-15 23:19:51

标签: reactjs firebase react-native firebase-authentication

在一个本机化的redux应用程序中,当用户注销时,我想取消订阅多个onSnapshot侦听器(5)。香港专业教育学院试图退订我的多个侦听器,但注销过程后继续出现错误:

enter image description here

这是我的注销操作:

export const logoutUser = (uid, role) => {

    return (dispatch) => {
        onProfileChange(uid, true);
        getConversations(uid, true);
        getAgenda(uid, role, true)
        onAccountChange(uid, true)

        return new Promise((resolve, reject) => {

            firebase
                .auth()
                .signOut()
                .then(() => {
                    dispatch({ type: types.RESET_APP });
                    console.log('Dispatching types.RESET_APP');
                    resolve();
                })
                .catch((error) => reject(error.message));
        });
    };
};

这是onProfileChange动作(此动作的结构与其余动作相似,带有用于取消订阅参数的条件语句)。显然,它位于导入到组件中并映射到props的外部文件中。

export const onProfileChange = (uid, isUnsubscribe) => {
    return (dispatch) => {
        const unsubscribe = firebase.firestore().collection('profiles').doc(uid).onSnapshot((doc) => {
            console.log('Profile: ' + JSON.stringify(doc.data()));
            dispatch({ type: types.LOAD_PROFILE, payload: doc.data() });
        });
        if ( typeof isUnsubscribe != "undefined" || isUnsubscribe == true) {
            unsubscribe()
        } 
    };
};

在组件的构造函数中,我像上面这样调用函数:

constructor (props) {
    super(props);
    props.onAccountChange(props.authUser.uid);
}

然后在同一组件内的此功能在按下按钮时执行:

logout = () => {
        this.props
            .logoutUser(this.props.authUser.uid, this.props.role)
            .then(() => {
                this.props.navigation.navigate('Login');
            })
            .catch((err) => alert(err));
    };

编辑: Firebase规则集:

service cloud.firestore {

  // allow only authenticated users to view database data
  match /databases/{database}/documents {
    allow read: if request.auth.uid != null
  }

  // allow only the logged in user to write their own documents
  // i.e Account data, Profile data, Messages, User-Conversations
  match /databases/{database}/documents {
    match /users/{uid} {
      allow read, write, update, delete: if request.auth.uid == uid
    }
    match/users/{uid} {
      allow update: if request.auth.uid != null 
    }
    match /profiles/{uid} {
        allow write, update, delete: if request.auth.uid == uid
    }

    // allow reading of all profile data if authenticated
    match /profiles/{uid} {
      allow read: if request.auth.uid != null
    }
    match /users/{uid} {
      allow read: if request.auth.uid != null
    }
    match /mostWanted/{docId} {
      allow read, update, create: if request.auth.uid != null
    }

    // Get multiple Docs by single owner (User-Conversations)
    match /user-conversations/{docId} {
      allow read, delete:
        if resource.data.owner == request.auth.uid
    }
    // Do the same ^ for /messages/{docId}
    // => requires 'receiver' data property to integrate
    match /messages/{docId} {
      allow create, read, update: if request.auth.uid != null
    }
    // allow read/write of user-convo's if authenticated
    match /user-conversations/{docId} {
      allow read, write, update: if request.auth.uid != null
    }    
  }
}

如果监听器已取消订阅,为什么仍会出现此firebase/permission-denied错误?我唯一想到的另一件事就是在所有这些操作中设置一个身份验证状态侦听器。并在更改时调用unSubscribe参考

2 个答案:

答案 0 :(得分:0)

经过多次尝试,我仍然能够规避该错误,尽管我对执行错误的方式并不满意,因为它似乎污染了该组件。无论如何,这是我为希望在本机项目中添加和分离Firestore快照侦听器的人做的。

我有我的抽屉组件,用于在其构造函数中设置侦听器:

imports {...}

class DrawerContentComponent extends React.Component {
    constructor (props) {
        super(props);

        this.unsubscribeAccount = firebase.firestore().collection('users').doc(props.authUser.uid).onSnapshot((doc) => {
            console.log("EXECUTING ONSNAPSHOT()...")
            if (doc.get('role') == 'Influencer') {
                onProfileChange(props.authUser.uid);
            }
        });
    }

    logout = () => {
        this.props
            .logoutUser()
            .then(() => {
                this.props.navigation.navigate('Login');
            })
            .catch((err) => alert(err));
    };

    // CALL REFERENCE TO LISTENER ON UNMOUNT
    componentWillUnmount() {
        console.log("EXECUTING COMPONENT WILL UNMOUNT")
        this.unsubscribeAccount();
    }

    < rest of component >
}

注销组件函数中执行的操作:

export const logoutUser = () => {
    return (dispatch) => {
        return new Promise((resolve, reject) => {
            firebase
                .auth()
                .signOut()
                .then(() => {
                    dispatch({ type: types.RESET_APP });
                    console.log('Dispatching types.RESET_APP');
                    resolve();
                })
                .catch((error) => reject(error.message));
        });
    };
};

最初,我想要的是一种调用一个动作(logoutUser())并取消订阅所有侦听器的方法,就像最初一样,我在我的一个动作文件中设置了所有侦听器。无论我如何顽固地尝试,这都不起作用。

但是,我仍然对此感到好奇的是,这实际上在消除permission-denied错误中起作用的原因。按照我的逻辑,执行顺序为:

  1. 按下按钮
  2. 输入
  3. 组件的logout()函数
  4. 它调用动作logoutUser()
  5. 开关导航器从抽屉式导航器变回并且抽屉部件已卸载
  6. 已达到对this.unsubscribeAccount()的引用。

这对我而言没有意义,因为signOut()firebase auth操作在取消订阅之前仍处于 状态,因此暂时将侦听器仍附加在点auth状态是有意义的变化。我确认是这样。侦听器引用是最后要执行的事情。

答案 1 :(得分:0)

当我说“您的取消订阅变量必须具有对您创建的原始侦听器的引用”时,您的答案就是我的意思。您不能仅在注销后创建新的订阅,然后立即取消订阅。这对您先前打开的旧版本没有任何作用。

仅将取消订阅操作传递给操作创建者?在您的logoutButtonHandler中,调用this.props.logoutUser(unsubscribeAccount)。如果有更多内容,则可以将它们放入数组中,然后将其传递给logOutuser,然后在其中进行映射,调用该数组的每个元素(取消订阅函数)。

export const logoutUser = (unsubscribeAccount) => {
    return (dispatch) => {
        return new Promise((resolve, reject) => {
            unsubscribeAccount();
            firebase
                .auth()
                .signOut()
                .then(() => {
                    dispatch({ type: types.RESET_APP });
                    console.log('Dispatching types.RESET_APP');
                    resolve();
                })
                .catch((error) => reject(error.message));
        });
    };
};