Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Reactjs 刷新时firebase身份验证延迟_Reactjs_Firebase_React Router_Firebase Authentication - Fatal编程技术网

Reactjs 刷新时firebase身份验证延迟

Reactjs 刷新时firebase身份验证延迟,reactjs,firebase,react-router,firebase-authentication,Reactjs,Firebase,React Router,Firebase Authentication,我的SPA React/Firebase应用程序上的硬刷新不会在立即执行函数时保持身份验证状态。我有一个解决办法,但它是粗略的 My react routes利用oneter功能来确定用户是否经过身份验证。比如说 <Route path="/secure" component={Dashboard} onEnter={requireAuth}/> 但是,在硬刷新时,firebase.auth().currentUser上有轻微延迟。它首先为空,然后对firebase服务器执行POS

我的SPA React/Firebase应用程序上的硬刷新不会在立即执行函数时保持身份验证状态。我有一个解决办法,但它是粗略的

My react routes利用
oneter
功能来确定用户是否经过身份验证。比如说

<Route path="/secure" component={Dashboard} onEnter={requireAuth}/>
但是,在硬刷新时,
firebase.auth().currentUser
上有轻微延迟。它首先为空,然后对firebase服务器执行
POST
,以确定身份验证状态。当它返回时,将填充
currentUser
对象。不过,这种延迟会引起一些问题

我的黑客解决方案如下:更新:这实际上不起作用

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);
};
只需将其包装为超时。然而,我真的不喜欢这个。。。有什么想法吗

更新:

我还尝试将它包装在一个
onAuthStateChanged
侦听器中,这应该比一个带有确定时间延迟的
setTimeout
更准确。代码如下:

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);
};
执行两条日志语句,但react router
replace
似乎没有正确执行。对于路由器专家来说,这可能是一个不同的问题

更新2:


我做这件事的时候已经是深夜了。显然,
setTimeout
实际上也不起作用

好的。因此,我能够通过利用firebase提供的
localStorage
变量来存储用户信息来解决这个问题

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

虽然这是一篇与ReactJS相关的文章,但我最近在为AngularJS编写自己的身份验证/授权服务时遇到了同样的问题。在页面刷新时,
onAuthStateChanged
传递的用户为
null
,因为firebase仍在初始化(异步)

唯一对我有效的解决方案是在用户登录后将用户uid存储在
localStorage
中,并在用户注销后删除该值

由于我分别使用了
authService
userService
,因此我在
authService
中注册了一个侦听器,该侦听器在用户登录/注销后启动

代码示例authService(不是完整的authService):

在运行块中,您可以全局注册用户,并在每次路由更改时解析用户信息/详细信息(使其更安全):


也许这可以帮助那些在同样问题上挣扎的人。除此之外,请随意优化和讨论代码示例。

通过管理本地存储来工作。下面是我如何做到这一点的例子

  constructor(props) {
    super(props);

    let authUser = null;

    // setting auth from localstorage
    for (let key in localStorage) {
      if (key === storageId) {
        authUser = {};
        break;
      }
    }

    this.state = {authUser};
  }

  componentDidMount() {
    firebase
      .auth
      .onAuthStateChanged(authUser => {

        if (authUser) {
          localStorage.setItem(storageId, authUser.uid);
        } else {
          localStorage.removeItem(storageId);
        }

        // change state depending on listener
        authUser
          ? this.setState({authUser})
          : this.setState({authUser: null});
      });
  }

这对我来说很有效。根据firebase.auth的状态,尝试将您的主应用程序包装在if-else语句中

constructor(){
超级();
这个州={
用户:{},
stateChanged:false
};
} 
componentDidMount(){
fire.auth().onAuthStateChanged((用户)=>{
this.setState({stateChanged:true})
});
}
render(){
if(this.state.stateChanged==false){
返回(
加载
)
}
否则{
return(您的代码在这里…)
}

}
我想我刚才回答了一个类似的问题:@FrankvanPuffelen我确实试过类似的方法。从requireAuth函数wrapp中,firebase.auth()中的内容已更改(函数(用户){…});然而,react路由器的replace函数在这种情况下似乎并没有做任何事情。我将更新这个问题。如果在单独的本地存储变量中,为什么需要存储用户?它已经在那里了。另外,角度角度的解决方案也不错。这只是设计选择的问题。当然,您可以将整个用户保存在localStorage中,但我不喜欢对localStorage的太多引用。在我的解决方案中,a在firebase引用中具有特定的用户信息(详细信息),如“/users”,这允许侦听“child_added”事件或firebase推送的任何其他事件。如果在您的情况下这感觉像是开销,您可以选择不同的方法。如果我转到您的网站,打开控制台并编写
localStorage.set(“firebase:authUser:123123123”,“123123”)
,会发生什么情况?firebase现在似乎使用索引数据库,因此此黑客不再有效。
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
        }
    });
}
.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
}
}]);
.run(['$rootScope', 'userService', 'authService',
function($rootScope, userService, authService) {

// make user available to $root in every view
$rootScope.user = userService.getUser();

$rootScope.$on('$routeChangeStart',
        function(event, next, current) {

    // make sure we can add resolvers for the next route
    if(next.$$route) {
        if(next.$$route.resolve == null)
            next.$$route.resolve = {};

        // resolve the current userDetails for every view
        var user = userService.resolveUserDetails();
        next.$$route.resolve.userDetails = function() {
            return user;
        }
    }
});
}]);
  constructor(props) {
    super(props);

    let authUser = null;

    // setting auth from localstorage
    for (let key in localStorage) {
      if (key === storageId) {
        authUser = {};
        break;
      }
    }

    this.state = {authUser};
  }

  componentDidMount() {
    firebase
      .auth
      .onAuthStateChanged(authUser => {

        if (authUser) {
          localStorage.setItem(storageId, authUser.uid);
        } else {
          localStorage.removeItem(storageId);
        }

        // change state depending on listener
        authUser
          ? this.setState({authUser})
          : this.setState({authUser: null});
      });
  }