I want to create a facebook login on my app, using react-native, redux and react-native-fbsdk. Actually, I'm face to a a problem, when I am on the first state (not logged in) I see this state :
and when I click on the login button I have this state :
The authenticating is set to true, but the application stay on authenticating, and even if I wait like 5 minutes, nothing happened after .. I think that it missing something in my code but I don't know what.
I past my code here.
The reducer :
import {
AUTH_SUCCESS,
AUTH_FAILURE,
AUTH_STARTED,
AUTH_ERROR,
AUTH_FAILURE_REMOVE,
LOGOUT
} from '../actions/types';
const initialState = {
authenticating: false,
authToken: null,
authError: null,
facebookToken: null,
facebookProfile: null
}
function authReducer(state = initialState, action) {
switch(action.type) {
case AUTH_STARTED:
return Object.assign({}, state, {
authenticating: true,
loginText: 'Connexion..'
});
case AUTH_SUCCESS:
return Object.assign({}, state, {
authenticating: false,
authToken: action.authToken,
facebookToken: action.facebookToken,
facebookProfile: action.facebookProfile,
});
case AUTH_FAILURE:
return Object.assign({}, state, {
authenticating: false,
authError: action.authError.message,
});
case AUTH_FAILURE_REMOVE:
return Object.assign({}, state, {
authError: null,
});
case LOGOUT:
return Object.assign({}, state, {
authenticating: false,
authToken: null,
facebookToken: null,
facebookProfile: null,
loginText: null,
});
default:
return state;
}
}
export default authReducer;
The action : (all actions are in separate file called types.js)
import { facebookLoginAPI, getFacebookInfoAPI } from '../src/facebook';
import { getServerAuthToken } from '../src/auth';
import {
AUTH_STARTED,
AUTH_SUCCESS,
AUTH_FAILURE,
AUTH_ERROR,
AUTH_FAILURE_REMOVE,
LOGOUT
} from './types';
export function authStarted() {
return {
type: AUTH_STARTED,
};
}
export function authSuccess(facebookToken, facebookProfile, serverAuthToken){
return {
type: AUTH_SUCCESS,
facebookToken,
facebookProfile,
authToken: serverAuthToken,
};
}
export function authFailure(authError){
return {
type: AUTH_FAILURE,
authError,
};
}
export function authFailureRemove() {
return {
type: AUTH_FAILURE_REMOVE,
};
}
export function logout() {
return {
type: LOGOUT,
};
}
export function facebookLogin() {
return (dispatch) => {
dispatch(authStarted());
const successValues = [];
facebookLoginAPI().then((facebookAuthResult) => {
successValues.push(facebookAuthResult.accessToken);
return getFacebookInfoAPI(facebookAuthResult.accessToken);
}).then((facebookProfile) => {
successValues.push(serverAuthToken);
dispatch(authSuccess(...successValues));
}).catch((error) => {
dispatch(authFailure(error));
setTimeOut(() => {
dispatch(authFailureRemove());
}, 4000);
});
};
}
The facebook API :
import {
LoginManager,
AccessToken,
GraphRequest,
GraphRequestManager,
} from 'react-native-fbsdk';
const facebookParams = 'id,name,email,picture.width(100).height(100)';
export function facebookLoginAPI() {
return new Promise((resolve, reject) => {
LoginManager.logInWithReadPermissions(['public_profile', 'user_friends', 'email'])
.then((FBloginResult) => {
if (FBloginResult.isCancelled) {
throw new Error('Login cancelled');
}
if (FBloginResult.deniedPermissions) {
throw new Error('We need the requested permissions');
}
return AccessToken.getCurrentAccessToken();
console.log(FBloginResult);
})
.then((result) => {
resolve(result);
})
.catch((error) => {
reject(error);
});
});
}
export function getFacebookInfoAPI() {
return new Promise((resolve, reject) => {
const profileInfoCallback = (error, profileInfo) => {
if (error) reject(error);
resolve(profileInfo);
};
const profileInfoRequest =
new GraphRequest(
'/me',
{
parameters: {
fields: {
string: facebookParams,
},
},
},
profileInfoCallback
);
new GraphRequestManager().addRequest(profileInfoRequest).start();
});
}
export function getFacebookFriends() {
return new Promise((resolve, reject) => {
const profileInfoCallback = (error, profileInfo) => {
if (error) reject(error);
console.log(profileInfo);
resolve(profileInfo);
};
const profileFriendsRequest =
new GraphRequest(
'/me/friends',
{
parameters: {
fields: {
string: facebookParams,
},
},
},
profileInfoCallback
);
new GraphRequestManager().addRequest(profileFriendsRequest).start();
});
}
The Button for login :
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { View } from 'react-native';
import { bindActionCreators } from 'redux';
import { facebookLogin } from '../../actions/auth';
import { Button } from '../common';
import FBSDK, { LoginManager } from 'react-native-fbsdk';
class Login extends Component {
componentWillMount() {
this.authCheck(this.props.authToken);
}
componentWillReceiveProps(nextProps) {
this.authCheck(nextProps.authToken);
}
authCheck(authToken){
if (authToken) {
return authToken;
}
}
renderError() {
if(this.props.authError){
console.log(this.props.authError);
}
return null;
}
render() {
return(
<Button onPress={this.props.onLoginPressed}> Login with facebook </Button>
);
}
}
export default Login;
And the AuthContainer :
import React, { Component, PropTypes } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { facebookLogin } from '../actions/auth';
import Login from '../components/Login';
class AuthContainer extends Component {
onLoginPressed() {
this.props.actions.facebookLogin();
console.log(this.props);
}
render(){
console.log(this.props);
return (
<Login {...this.props} onLoginPressed={this.onLoginPressed.bind(this)} />
);
}
}
AuthContainer.propTypes = {
welcomeText: PropTypes.string,
authenticating: PropTypes.bool.isRequired,
authToken: PropTypes.oneOfType([
PropTypes.bool,
PropTypes.string,
]),
authError: PropTypes.oneOfType([
PropTypes.bool,
PropTypes.string,
]),
actions: PropTypes.shape({
facebookLogin: PropTypes.func.isRequired,
}).isRequired,
};
function mapStateToProps(state) {
return {
authenticating: state.auth.authenticating,
authToken: state.auth.authToken,
authError: state.auth.authError,
loginText: state.auth.loginText,
};
console.log(state);
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({ facebookLogin }, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(AuthContainer);
and the App.js :
import React, { Component } from 'react';
import { Text, View, AsyncStorage } from 'react-native';
import { createStore, compose, applyMiddleware } from 'redux';
import { connect, Provider } from 'react-redux';
import { persistStore } from 'redux-persist';
import thunkMiddleware from 'redux-thunk';
import reducers from './reducers';
import { Header, Card, CardSection } from './components/common';
import AuthContainer from './containers/AuthContainer';
const store = compose(
applyMiddleware(thunkMiddleware)
)(createStore)(reducers);
persistStore(store, { storage: AsyncStorage });
class App extends Component {
render() {
return(
<Provider store={store}>
<View>
<Header headerText="Kiwee" />
<Card>
<CardSection>
<AuthContainer />
</CardSection>
</Card>
</View>
</Provider>
);
}
}
export default App;
Does someone could help me ? It's really important for me to do that, and I passed 1 month searching for a solutions without any responses ..
try to remove .push() function from your reducers as it is mutating operation.Try to use concat or spread operator. like try replacing
successValues.push(facebookAuthResult.accessToken);
with
[...successValues, ..facebookAuthResult.accessToken]
it is something related to Immutability, you can check about that in redux tutorial or may find on google (I am not in good position to explain that behavior right now)