Invariant Violation : Invalid Hooks Call. Hooks Call can only be made inside body of function components - react-native

Hi am getting this error at Connect() function in LoginScreen.js. On commenting it its working fine. I am guessing either my Store is not properly setup or i am not able to connect LoginScreen Component to Redux Store.
LoginScreen.js
import React, { Component } from "react";
import PhoneInput from "react-native-phone-input";
import { connect } from "react-redux";
import { View, StatusBar } from "react-native";
import { Container, Item, Input, Button, Text } from "native-base";
import {
phoneChanged,
codeChanged,
onCodeDispatched,
onPhoneLogin,
clearAuth,
onSignOut
} from "../Actions/AuthActions";
//import firebase from "react-native-firebase";
import { auth } from "../Config/firebase";
export class LoginScreen extends Component {
}
export default connect(
null, // passing null just for testing
null
)(LoginScreen);
Store.js
import ReduxThunk from "redux-thunk";
import { createStore, applyMiddleware, compose } from "redux";
import reducer from "../Reducers/index";
let composeEnhancers = compose;
/* eslint no-undef: 0 */
if (__DEV__) {
/* eslint no-underscore-dangle: 0 */
composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
}
const store = createStore(
reducer,
{},
composeEnhancers(applyMiddleware(ReduxThunk))
);
export { store };
AuthReducer.js
import {
LOGIN_FAIL,
LOGIN_SUCCESS,
LOGIN_USER,
PHONE_CHANGED,
CODE_SENT_ERROR,
CODE_CHANGED,
CODE_DISPATCHED,
LOGIN_USER_PHONE,
CODE_SENT,
CODE_NOT_CONFIRMED,
LOGOUT,
SET_USER_OBJECT,
CLEAR_AUTH
} from "../Actions/ActionTypes";
const INITIAL_STATE = {
phone: "+30",
user: null,
message: "",
loading: false,
codeInput: "",
confirmResult: null
};
const AuthReducer = (state = INITIAL_STATE, action) => {
console.log(action);
switch (action.type) {
case PHONE_CHANGED:
return {
...state,
phone: action.payload
};
case CODE_CHANGED:
return {
...state,
codeInput: action.payload
};
case LOGIN_USER:
return {
...state,
loading: true,
message: ""
};
case LOGIN_USER_PHONE:
return {
...state,
loading: true,
message: "Sending code...",
phone: action.payload
};
case CODE_DISPATCHED:
return {
...state,
loading: true,
message: ""
};
case CODE_SENT:
return {
...state,
loading: true,
message: "Code has been sent!",
confirmResult: action.payload
};
case CODE_SENT_ERROR:
return {
...state,
loading: false,
message: `Sign in with Phone number error: ${action.payload}`,
confirmResult: null
};
case SET_USER_OBJECT:
return {
...state,
user: action.payload
};
case CODE_NOT_CONFIRMED:
return {
...state,
message: `Code confirmation error: ${action.payload}`
};
case LOGIN_SUCCESS:
return {
...INITIAL_STATE,
user: action.payload,
message: "login Success"
};
case LOGIN_FAIL:
return {
...state,
message: "Authentication Failed.",
loading: false,
password: "",
phone: "+91"
};
case LOGOUT:
return {
...state,
message: "",
user: null
};
case CLEAR_AUTH:
return {
...state,
...INITIAL_STATE
};
default:
return state;
}
};
export default AuthReducer;
Root.js
import React from "react";
import { Provider } from "react-redux";
import { Navigator } from "./Navigation/Index";
import { store } from "./Store/Index";
export default class Root extends React.Component {
render() {
return (
<Provider store={store}>
<Navigator />
</Provider>
);
}
}
AuthActions.js
//import { firebase } from "react-native-firebase";
import * as actionTypes from "./ActionTypes";
import { auth } from "../Config/firebase";
const phoneChanged = text => {
return {
type: actionTypes.PHONE_CHANGED,
payload: text
};
};
const onLoginSuccess = (dispatch, user) => {
dispatch({
type: actionTypes.LOGIN_SUCCESS,
payload: user
});
};
const signOut = dispatch => {
dispatch({
type: actionTypes.LOGOUT
});
};
const onPhoneLogin = phone => {
return dispatch => {
dispatch({
type: actionTypes.LOGIN_USER_PHONE
});
auth
.signInWithPhoneNumber(phone)
// sign in success
.then(confirmResult => {
onCodeSent(dispatch, confirmResult);
})
// sign in error
.catch(error => onCodeSentError(dispatch, error));
};
};
const codeChanged = text => {
return {
type: actionTypes.CODE_CHANGED,
payload: text
};
};
const onCodeSent = (dispatch, confirmResult) => {
dispatch({
type: actionTypes.CODE_SENT,
payload: confirmResult
});
};
const onCodeConfirmError = (dispatch, error) => {
dispatch({
type: actionTypes.CODE_NOT_CONFIRMED,
payload: error
});
};
const onCodeDispatched = code => {
return (dispatch, getState) => {
getState()
.auth.confirmResult.confirm(code)
.then(user => onLoginSuccess(dispatch, user))
.catch(error => onCodeConfirmError(dispatch, error));
};
};
const onCodeSentError = (dispatch, error) => {
dispatch({
type: actionTypes.CODE_SENT_ERROR,
payload: error
});
};
const onSignOut = () => {
return dispatch => {
auth
.signOut()
.then(() => signOut(dispatch))
.catch(error => console.log(error));
};
};
const clearAuth = () => {
return dispatch => {
dispatch({
type: actionTypes.CLEAR_AUTH
});
};
};
export {
onSignOut,
clearAuth,
codeChanged,
onPhoneLogin,
phoneChanged,
onCodeDispatched
};
The Idea is basically to call LoginScreen which is part of 'Auth' StackNavigator and Render PhoneNumberInput and OTP.

React-redux (> 7.0.1) uses hook & React-native (< 0.59) doesn’t support Hooks yet.
You can run npm ls react-native in your application folder to check which version you’re using.
If you find more than one of them, this might also create problems. more on it
There are two solution
Upgrade the react native version to 0.59.0
OR
Downgrade the react redux version to 6.0.1
I hope it help you.

Related

Redux thunk dispatch does not work for multiple api calls

My Redux thunk dispatch was working until i made a single API call but stopped working after using combineReducers for multiple api calls.
This is my component code :
const mapStateToProps = state => ({
loading: state.loading,
data: state.data,
error: state.error,
networkError: state.networkError
})
const mapDispatchToProps = {
login
}
These are my Actions :
export const GET_LOGIN_LOADING = "GET_LOGIN_LOADING"
export const GET_LOGIN_SUCCESS = "GET_LOGIN_SUCCESS"
export const GET_LOGIN_ERROR = "GET_LOGIN_ERROR"
export const GET_REGISTER_LOADING = "GET_REGISTER_LOADING"
export const GET_REGISTER_SUCCESS = "GET_REGISTER_SUCCESS"
export const GET_REGISTER_ERROR = "GET_REGISTER_ERROR"
This is my reducer for login and register actions :
import * as Actions from './Actions'
const loginState = {
loginLoading: false,
loginData: [],
loginError: '',
}
const registerState = {
registerLoading: false,
registerData: [],
registerError: '',
}
export const loginReducer = (state = { loginState }, action) => {
switch (action.type) {
case Actions.GET_LOGIN_LOADING:
return {
...state,
loginLoading: action.payload
}
case Actions.GET_LOGIN_SUCCESS:
return {
...state,
loginData: action.payload,
loginLoading: false,
}
case Actions.GET_LOGIN_ERROR:
return {
...state,
loginError: action.payload,
loginLoading: false,
}
default: return loginState
}
}
export const registerReducer = (state = { registerState }, action) => {
switch (action.type) {
case Actions.GET_REGISTER_LOADING:
return {
...state,
registerLoading: action.payload
}
case Actions.GET_REGISTER_SUCCESS:
return {
...state,
registerData: action.payload,
registerLoading: false,
}
case Actions.GET_REGISTER_ERROR:
return {
...state,
registerError: action.payload,
registerLoading: false,
}
default: return registerState
}
}
My Redux Store Code :
import { createStore, applyMiddleware, combineReducers } from 'redux'
import thunk from 'redux-thunk'
import{ loginReducer, registerReducer } from '../redux/Reducer'
const reducer = combineReducers({loginReducer, registerReducer})
export default createStore(reducer, applyMiddleware(thunk))
Finally my thunk code used for making API calls :
export const login = (countryCode, phone, password) => {
const userName = {
countryCode,
phone
}
return dispatch => {
dispatch(getLoginLoading(true))
service.post('login', {
userName,
password
})
.then(response => {
console.log(response.data)
dispatch(getLoginSuccess(response.data))
})
.catch(error => {
console.log(error)
dispatch(getLoginError(error.response.data))
})
}
}
export const register = (name, countryCode, phone) => {
return dispatch => {
dispatch(getRegisterLoading(true))
service.post('register', {
name,
countryCode,
phone,
})
.then(response => {
console.log(response.data)
dispatch(getRegisterSuccess(response.data))
})
.catch(error => {
console.log(error.response)
dispatch(getRegisterError(error.response))
})
}
}
Finally found an answer by myself. When you are use combine reducers, Redux creates a nested state object for each reducer, Hence when accessing the state you should use :
const reducer = combineReducers({loginReducer, registerReducer})
const mapStateToProps = state => ({
loading: state.loginReducer.loading,
data: state.loginReducer.data,
error: state.loginReducer.error,
networkError: state.loginReducer.networkError
})

React native redux after login statetoprops not updating before render

After login and redirecting to Home page component here I am calling:
mapStateToProps = (state) => ({
getUser: state.userReducer.getUser
});
And trying to render the value:
render() {
const {getUser: {userDetails}} = this.props;
return(
<View><Text>{userDetails.EmployeeID}</Text></View>
)
}
USER Reducer
import { combineReducers } from 'redux';
const getUser = (state = {}, action) => {
switch (action.type) {
case "GET_USER_LOADING":
return {
isLoading: true,
isError: false,
isSuccess: false,
userDetails: null,
errors: null
}
case "GET_USER_SUCCESS":
return {
isLoading: false,
isError: false,
isSuccess: true,
userDetails: action.payload,
errors: null
}
case "GET_USER_FAIL":
return {
isLoading: false,
isError: true,
isSuccess: false,
userDetails: null,
errors: action.payload
}
default:
return state;
}
}
export default combineReducers({
getUser
});
And loginuser action
export const loginUser = (payload) => {
return async (dispatch) => {
try {
dispatch({
type: "LOGIN_USER_LOADING"
});
const response = await fetchApi("front/authentication/login", "POST", payload, 200);
if(response.responseBody.status) {
dispatch({
type: "LOGIN_USER_SUCCESS",
});
dispatch({
type: "AUTH_USER_SUCCESS",
token: response.token
});
dispatch({
type: "GET_USER_SUCCESS",
payload: response.responseBody.data
});
return response.responseBody.status;
} else {
throw response;
}
} catch (error) {
dispatch({
type: "LOGIN_USER_FAIL",
payload: error.responseBody
});
return error;
}
}
}
But getting an error:
TypeError: Undefined is not an object(Evaluating 'userDetails.EmployeeID'
If I Remove the userDetails.EmployeeID and navigate to next page and then come back it show the EmployeeID fine.
Try this way,
render() {
const {getUser} = this.props;
return(
<View><Text>{getUser.userDetails.EmployeeID}</Text></View>
)}
if that doesn't work, kindly post redux code
If your initial state resolves to something "falsy" for userDetails you can "wait" for its value:
render() {
const {getUser: {userDetails}} = this.props;
return <View>
<Text>{userDetails ? userDetails.EmployeeID : 'Loading'}</Text>
</View>
}

store.getstate() evaluate to undefined

in a react-native project,I keep hitting in this error:
state is undefined, evaluating store.getstate()
//store.js
const composeEnhancers =
typeof window === "object" && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
: compose;
const enhancer = composeEnhancers(applyMiddleware(thunk));
const Store = createStore(
combineReducers(
{
form: formReducer,
appointmentsReducer
},
enhancer
)
);
console.log(Store.getState());
export default Store;`
//reducer.js
import {
FETCH_APPOINTMENTS_BEGIN,
FETCH_APPOINTMENTS_SUCCESS,
FETCH_APPOINTMENTS_FAILURE
} from '../actions/appointmentsAction';
const initialState = {
data: [],
loading: false,
error: null
};
export default function appointmentsReducer(state = initialState, action) {
switch(action.type) {
case FETCH_APPOINTMENTS_BEGIN:
return {
...state,
loading: true,
error: null
};
case FETCH_APPOINTMENTS_SUCCESS:
return {
...state,
loading: false,
data: action.payload.appointments
};
case FETCH_APPOINTMENTS_FAILURE:
return {
...state,
loading: false,
error: action.payload.error,
data: []
};
default:
return state;
}
}
//actions.js
import { listUrl } from "../cst";
export const FETCH_APPOINTMENTS_BEGIN = "FETCH_APPOINTMENTS_BEGIN";
export const FETCH_APPOINTMENTS_SUCCESS = "FETCH_APPOINTMENTS_SUCCESS";
export const FETCH_APPOINTMENTS_FAILURE = "FETCH_PRODUCTS_FAILURE";
export const fetchAppointmentsBegin = () => ({
type: FETCH_APPOINTMENTS_BEGIN
});
export const fetchAppointmentsSuccess = appointments => ({
type: FETCH_APPOINTMENTS_SUCCESS,
payload: { appointments }
});
export const fetchAppointmentsFailure = error => ({
type: FETCH_APPOINTMENTS_FAILURE,
payload: { error }
});
export function fetchAppointments() {
return dispatch => {
dispatch(fetchAppointmentsBegin());
return fetch(listUrl)
.then(handleErrors)
.then(res => res.json())
.then(json => {
dispatch(fetchApointmentsSuccess(json.appointment));
return json.appointment;
})
.catch(error => dispatch(fetchAppointmentsFailure(error)));
};
}
// Handle HTTP errors since fetch won't.
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
// app.js
export default function App() {
return (
<Provider store={Store}>
<Navigation />
</Provider>
);
}
//list rendrer component :
const mapStateToProps = state => ({
data: state.appointments.data,
loading: state.loading,
error: state.error
});
the console.log of store.getstate() gives :
Object {
"appointmentsReducer": Object {
"data": Array [],
"error": null,
"loading": false,
},
"form": Object {},
I'm not sure where the problem is.
Is it due to the asynchronous call not being handled properly?
If I use saga to handle the fetch, will it resolve the problem?
Any help would be appreciated .

How to configure redux-thunk properly?

I've been trying to implement a ChatApp in order to learn React Native. I'm trying to use redux-thunk in order to sign users up in firebase.
The problem I'm running into is that everyone seems to do things slightly different in their examples/explanations. Little confused. Can anyone explain what I'm doing wrong?
// Reducer
import * as types from './actionTypes'
const initialState = {
restoring: false,
loading: false,
user: null,
error: null,
}
const session = (state = initialState, action) => {
switch(action.type) {
case types.SESSION_RESTORING:
return { ...state, restoring: true }
case types.SESSION_LOADING:
return { ...state, restoring: false, loading: true, error: null }
case types.SESSION_SUCCESS:
return { restoring: false, loading: false, user: action.user, error: null }
case types.SESSION_ERROR:
return { restoring: false, loading: false, user: null, error: action.error }
case types.SESSION_LOGOUT:
return initialState
default:
return state
}
}
export default session
// Actions
import * as types from './actionTypes'
import firebaseService from '../../services/firebase'
export const signupUser = (email, password) => {
return (dispatch) => {
dispatch(sessionLoading())
firebaseService.auth()
.createUserWithEmailAndPassword(email, password)
.catch(
error => {
dispatch(sessionError(error.message))
}
)
let unsubscribe = firebaseService.auth()
.onAuthStateChanged(
user => {
if (user) {
dispatch(sessionSuccess(user))
unsubscribe()
}
}
)
}
}
//Actions
const sessionSuccess = (user) => ({
type: types.SESSION_SUCCESS,
user
})
const sessionLoading = () => {
type: types.SESSION_LOADING
}
const sessionError = (error) => {
type: types.SESSION_ERROR,
error
}
// Configure store
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import reducer from './session'
const configureStore = () => {
// eslint-disable-next-line no-underscore-dangle
return createStore(reducer, compose(applyMiddleware(thunk)))
}
export default configureStore
// Create store
import React from 'react'
import { Provider } from 'react-redux'
import configureStore from './store'
import Screen from './screens/Authorization'
const store = configureStore()
const App = () =>
<Provider store={store}>
<Screen />
</Provider>
export default App
// MapDispatchToProps
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import SignupComponent from './Component'
import { signupUser } from '../../../store/session/actions'
const SignupContainer = (props) =>
<SignupComponent signupUser={props.signup}/>
SignupContainer.propTypes = {
signup: PropTypes.func.isRequired
}
const mapDispatchToProps = {
signup: signupUser
}
export default connect(null, mapDispatchToProps)(SignupContainer)
The error I get is:
Actions must be plain objects. Use custom middleware for async actions.
You need to wrap your object in parens if you want to use arrow functions like that:
const sessionLoading = () => ({
type: types.SESSION_LOADING
})
const sessionError = (error) => ({
type: types.SESSION_ERROR,
error
})

React native redux props not updated after calling an action

I'm new to react, react native, and redux, I have a react native app that has this login in render
render() {
return (<TouchableOpacity style={Style.btnSubmit} onPress={this.onLoginPressed.bind(this)}>
<Text style={Style.btnText}>Login</Text>
</TouchableOpacity>)
}
and the onLoginPressed function is here
onLoginPressed() {
const { username, password } = this.props.form;
this.props.login({ username, password}); // the login is using the fetch api
// props are not updated with the new state values
console.log(this.props)
}
Everything is working correctly but
the props doesn't update in the onLoginPressed function, however, when I console log the props inside the render function, it's updated.
I understand that redux do a full rendering, but I just don't really understand if it should update the props after calling the login.
Thank you
Update
Here is the end of the component
function mapStateToProps(state) {
return {
...state.login
}
}
function mapDispatchToProps(dispatch) {
return {
login: (formData) => dispatch(login(formData)),
facebookLogin: (formData) => dispatch(facebookLogin(formData)),
setUsername: (username) => dispatch(setUsername(username)),
setPassword: (password) => dispatch(setPassword(password)),
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Login);
here is the action
import { Host, Endpoints } from '../config/server';
import { loginActions } from '../config/constants';
/*
* state props
- form
- inProgress
- error
- data
*/
export function login(form) {
return (dispatch) => {
dispatch(loggingIn(true));
fetch(Host + Endpoints.auth.login, {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify(form)
})
.then(res => res.json())
.then(res => {
dispatch(loggingIn(false));
res.error ? dispatch(loginError(res.error)) :
dispatch(loginSuccess(res.data));
})
.catch(err => dispatch(loginError(err)));
}
}
export function facebookLogin(data) {
return (dispatch) => {
dispatch(loggingIn());
fetch(Host + Endpoints.auth.facebookLogin, {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify(data)
})
.then(res => res.json())
.then(data => dispatch(loginSuccess(data)))
.catch(err => dispatch(loginError(err)));
}
}
export function setUsername(username) {
return {
type: loginActions.setUsername,
username
}
}
export function setPassword(password) {
return {
type: loginActions.setPassword,
password
}
}
function loginSuccess(data) {
return {
type: loginActions.LoginSuccess,
data
}
}
function loginError(error) {
return {
type: loginActions.LoginError,
error
}
}
function loggingIn(val) {
return {
type: loginActions.LoggingIn,
inProgress: val
}
}
and here is the reducer
import { loginActions } from '../config/constants';
const initialState = {
form: {
username: '',
password: ''
},
data: null,
inProgress: false,
error: null
};
export default function loginReducer(state = initialState, action) {
switch(action.type) {
case loginActions.LoggingIn:
return {
...state,
inProgress: action.inProgress
}
case loginActions.LoginError:
return {
...state,
error: action.error,
}
case loginActions.LoginSuccess:
return {
...state,
inProgress: false,
error: null,
data: action.data
}
case loginActions.setUsername:
return {
...state,
form: {
username: action.username,
password: state.form.password
}
}
case loginActions.setPassword:
return {
...state,
form: {
username: state.form.username,
password: action.password
}
}
default:
return {
...state
}
}
}
and the reducer index file
import { combineReducers } from 'redux';
import login from './login';
const rootReducer = combineReducers({
login
});
export default rootReducer;
and the configureStore file
import { createStore, applyMiddleware } from 'redux'
import reducers from './reducers'
import thunk from 'redux-thunk'
export default function configureStore() {
let store = createStore(reducers, applyMiddleware(thunk))
return store
}
of course the root is wrapped with the provider passing the store.
You are doing console.log in the same function call that dispatch the login actions. That won’t work, because JavaScript is non-blocking, it will not wait for the login action to complete and update the props before calling console.log
Try console.log in something like componentWillReceiveProps.