2016-05-22 12 views
4

Hard Refreshes auf meinem SPA React/Firebase-Anwendung nicht auth Zustand bei sofortiger Ausführung einer Funktion. Ich habe einen Workaround, aber es ist skizzenhaft.firebase auth bei Aktualisierung verzögert

Meine Reaktionsrouten verwenden die onEnter-Funktion, um festzustellen, ob der Benutzer authentifiziert ist oder nicht. Zum Beispiel

<Route path="/secure" component={Dashboard} onEnter={requireAuth}/> 

Des Weiteren sieht meine requireAuth Funktion wie folgt aus:

function (nextState, replace) { 
     console.log('requireAuth', firebase.auth().currentUser); 
     if (!firebase.auth().currentUser) { 
      console.log('attempting to access a secure route. please login first.'); 
      replace({ 
       pathname: '/login', 
       state: { nextPathname: nextState.location.pathname } 
      }); 
     } 
}; 

jedoch auf eine harte Refresh gibt es eine leichte Verzögerung auf firebase.auth().currentUser. Es ist zuerst null und führt dann einen POST zu Firebase-Servern aus, um den Auth-Status zu ermitteln. Bei der Rückgabe wird das Objekt currentUser aufgefüllt. Diese Verzögerung verursacht jedoch Probleme.

Meine hacky Lösung ist folgende: Update: dies tatsächlich nicht funktioniert ...

function (nextState, replace) { 
    setTimeout(function() { 
     console.log('requireAuth', firebase.auth().currentUser); 
     if (!firebase.auth().currentUser) { 
      console.log('attempting to access a secure route. please login first.'); 
      replace({ 
       pathname: '/login', 
       state: { nextPathname: nextState.location.pathname } 
      }); 
     } 
    }, 50); 
}; 

einfach wickeln Sie es in einem Timeout. Allerdings mag ich das wirklich nicht ... irgendwelche Gedanken?

Update:

Ich habe auch versucht, es innerhalb eines onAuthStateChanged Zuhörer zu setzen, das als ein setTimeout mit einer endgültigen Zeitverzögerung genauer sein sollte. Code wie folgt:

function (nextState, replace) { 
    var unsubscribe = firebase.auth().onAuthStateChanged(function (user) { 
     if (!user) { 
      console.log('attempting to access a secure route'); 
      replace({ 
       pathname: '/login', 
       state: { nextPathname: nextState.location.pathname } 
      }) 
      console.log('should have called replace'); 
     } 
     unsubscribe(); 
    }); 
    // setTimeout(function() { 
    //  console.log('requireAuth', firebase.auth().currentUser); 
    //  if (!firebase.auth().currentUser) { 
    //   console.log('attempting to access a secure route. please login first.'); 
    //   replace({ 
    //    pathname: '/login', 
    //    state: { nextPathname: nextState.location.pathname } 
    //   }); 
    //  } 
    // }, 50); 
}; 

Die beiden Log-Anweisungen ausgeführt werden, reagieren aber-Router replace scheint nicht korrekt ausgeführt werden. Vielleicht ist das eine andere Frage für die React-Router-Experten.

Update 2:

Es war spät in der Nacht, wenn ich daran gearbeitet wurde. Offensichtlich funktioniert setTimeout auch nicht.

+0

Ich glaube, ich antwortete nur eine ähnliche Frage hier: http://stackoverflow.com/questions/37370462/refs-not-rerunning-after-onauthstatechanged-changes-firebase-3-0-0 –

+0

@FrankvanPuffelen ich tatsächlich versucht so ähnlich. Von der Funktion requireAuth wrapp den Inhalt in firebase.auth(). OnAuthStateChanged (function (user) {...}); Die Replace-Funktion von react-router scheint in diesem Kontext jedoch nichts zu tun. Ich werde die Frage aktualisieren. –

Antwort

6

Okay. Also konnte ich dies lösen, indem ich die Variable localStorage nutzte, die Firebase zur Verfügung stellt, um die Benutzerinformationen zu speichern.

function (nextState, replace) { 
    if (!firebase.auth().currentUser) { 
     let hasLocalStorageUser = false; 
     for (let key in localStorage) { 
      if (key.startsWith("firebase:authUser:")) { 
       hasLocalStorageUser = true; 
      } 
     } 
     if (!hasLocalStorageUser) { 
      console.log('Attempting to access a secure route. Please authenticate first.'); 
      replace({ 
       pathname: '/login', 
       state: { nextPathname: nextState.location.pathname } 
      }); 
     } 
    } 
}; 
2

Zwar ist dies ein Beitrag zu ReactJS verwendet, ich kam vor kurzem in dem gleichen Problem, wenn mein eigenen Authentifizierung/Autorisierung Service für AngularJS zu schreiben. Bei Seitenaktualisierung übergibt der onAuthStateChanged einen Benutzer, der null ist, da Firebase noch (asynchron) initialisiert wird.

Die einzige Lösung, die für mich arbeitete, war das Speichern der Benutzer-UID in localStorage, nachdem der Benutzer sich angemeldet hat und den Wert löschen, nachdem der Benutzer sich abgemeldet hat.

Da ich eine authService und separat verwende, habe ich einen Listener in der authService registriert, die ausgelöst wird, sobald der Benutzer an-/abgemeldet ist.

Codebeispiel authService (nicht die volle authService):

var loginListeners = []; 
var logoutListeners = []; 

function addLoginListener(func) { 
    loginListeners.push(func); 
} 

function addLogoutListener(func) { 
    logoutListeners.push(func); 
} 

function login(email, password) { 
    return firebase.auth().signInWithEmailAndPassword(email, password).then(function(user) { 
     for(var i = 0; i < loginListeners.length; i++) { 
      loginListeners[i](user); // call registered listeners for login 
     } 
    }); 
} 

function logout() { 
    return firebase.auth().signOut().then(function() { 
     for(var i = 0; i < logoutListeners.length; i++) { 
      logoutListeners[i](); // call registered listeners for logout 
     } 
    }); 
} 

Codebeispiel Userservice (nicht die volle Userservice):

.provider('userService', ['authServiceProvider', 
function UserService(authServiceProvider) { 

var usersRefUrl = '/users'; 
var userInfo = null; 
var userDetails = null; 

// refreshHack auto-executed when this provider creates the service 
var storageId = 'firebase:uid'; // storing uid local because onAuthStateChanged gives null (when async initializing firebase) 
(function addRefreshHackListeners() { 
    authServiceProvider.addLoginListener(function(user) { 
     userInfo = user; 
     localStorage.setItem(storageId, user.uid); // store the users uid after login so on refresh we have uid to retreive userDetails 
    }); 
    authServiceProvider.addLogoutListener(function() { 
     userInfo = null; 
     localStorage.removeItem(storageId); 
    }); 
    firebase.auth().onAuthStateChanged(function(user) { 
     if(user) { // when not using refreshHack user is null until async initializing is done (and no uid is available). 
      localStorage.setItem(storageId, user.uid); 
      userInfo = user; 
      resolveUserDetails(); 
     } else { 
      localStorage.removeItem(storageId); 
      userInfo = null; 
      userDetails = null; 
     } 
    }); 
})(); 

function isLoggedIn() { 
    return userInfo ? userInfo.uid : localStorage.getItem(storageId); // check localStorage for refreshHack 
} 

function resolveUserDetails() { 
    var p = null; 
    var uid = isLoggedIn(); 
    if(uid) 
     p = firebase.database().ref(usersRefUrl + '/' + uid).once('value').then(function(snapshot) { 
      userDetails = snapshot.val(); 
      return userDetails; 
     }).catch(function(error) { 
      userDetails = null; 
     }); 

    return p; // resolve by returning a promise or null 
} 
}]); 

Und in einem Laufblock können Sie einen Benutzer global registrieren und lösen Sie die Benutzer-Informationen/Details jeder Route ändern (macht es sicherer):

Vielleicht Dies kann jemandem helfen, der das gleiche Problem hat. Außerdem können Sie die Code-Beispiele optimieren und diskutieren.

+0

Warum müssen Sie den Benutzer in einer separaten lokalen Speichervariablen speichern? Es ist schon da. Sonst schöne Lösung für eckig. –

+0

Es ist nur eine Frage der Design-Entscheidungen. Natürlich können Sie den gesamten Benutzer in localStorage speichern, aber ich mag nicht viele Verweise auf die localStorage. In meiner Lösung habe ich spezifische Benutzerinformationen (Details) in einer Firebase-Referenz wie "/ users" und dies ermöglicht das Abhören von "child_added" -Ereignissen oder anderen Ereignissen, die von Firebase gepusht werden. Wenn sich dies in Ihrer Situation für Sie als Overhead anfühlt, können Sie einen anderen Ansatz wählen. –

Verwandte Themen