how can I get the value of the state from the store?
I already tried using store.getState().isLoggedIn but it didn't work:
import * as actions from 'actions/actions'
import store from 'store/store'
store.dispatch(actions.login(this.state.username,this.state.password)).then(() => {
state = store.getState();
alert(state.isLoggedIn);
});
I'm trying to get the value of the state "isLoggedIn" but it returns "undefined". how can i properly get the state value from the store?
here is my source code
Reducer:
const INITIAL_STATE={
isLoggedIn:false,
isLoading:false,
userData:{},
error:undefined
}
export default function auth(state=INITIAL_STATE,action){
console.log(action.type);
switch (action.type) {
case 'LOGIN_ATTEMPT':
return{
...state,
isLoading:true,
isLoggedIn:false
}
break;
case 'LOGIN_SUCCESS':
return {
...state,
isLoading:false,
isLoggedIn:true,
userData:action.userData,
error:undefined
}
break;
case 'LOGIN_FAILED':
return{
...state,
isLoading:false,
isLoggedIn:false,
error:action.error
}
break;
case 'LOGOUT':
return{
...state,
isLoading:false,
isLoggedIn:false
}
break;
default:
return state
}
}
actions:
export function isLoading(bool:Boolean){
return{
type:'LOGIN_ATTEMPT',
isLoading:bool
}
}
export function loginSuccess(userData:Object){
return{
type:'LOGIN_SUCCESS',
userData
}
}
export function loginFailed(error:Object){
return{
type:'LOGIN_FAILED',
error
}
}
export function login(uname,pass){
return dispatch => {
dispatch(isLoading(true));
return fetch('http://192.168.99.1:5000/user/login',{
method:'POST',
headers:{
'Content-Type':'application/json'
},
body:JSON.stringify({
username:uname
})
})
.then((response) => {
if(response.status < 300){
dispatch(isLoading(false))
response.json().then((responseJSON) => {
if(responseJSON['message'] == "True" && bcrypt.compareSync(pass, responseJSON['hash']) ){
console.log("responseJSON",responseJSON);
dispatch(loginSuccess(responseJSON));
}
else{
dispatch(isLoading(false))
dispatch(loginFailed(responseJSON.message))
}
})
}
else{
response.json().then((responseJSON) => {
console.log("responseJSON",responseJSON);
dispatch(isLoading(false))
dispatch(loginFailed(responseJSON.message))
})
}
})
.catch((error) => {
console.log("error",error);
dispatch(isLoading(false))
dispatch(loginFailed(error))
})
}
}
store:
import {combineReducers} from 'redux'
import auth from './authReducer'
import {createStore,applyMiddleware} from 'redux'
import thunk from 'redux-thunk';
const rootReducer = combineReducers({
auth
})
let store = createStore(rootReducer,applyMiddleware(thunk))
export default store;
I was able to view the data by using JSON.stringify
JSON.stringify(store.getState())
it returns json data. using this method makes it easy to view objects
You're using combineReducers to produce rootReducer.
The state produced by combineReducers() namespaces the states of each reducer under their keys as passed to combineReducers()
So store.getState() will return object like this
{ auth:
{ isLoggedIn:false,
isLoading:false,
userData:{},
error:undefined
}
}
To refer to isLoggedIn you have to do alert(state.auth.isLoggedIn);
Related
Im implementing redux in my react native app, I need to create 2 reducers, one of them is designed to manage login state and the other to manage some process status.
Reducers:
isLogged
processStatus
processStatus Actions:
export function start() {
return {
type: 'START_LOADING'
}
}
export function stop() {
return {
type: 'END_LOADING'
}
}
isLogged Actions:
export function signIn() {
return {
type: 'SIGN_IN'
}
}
export function loadFinished() {
return {
type: 'LOAD_FINISHED'
}
}
export const signOut = userAuthParams => (
{
type: 'SIGN_OUT'
}
);
export const firstLogin = userAuthParams => (
{
type: 'FIRST_LOGIN'
}
);
export const fbLogin = userAuthParams => (
{
type: 'FB_LOGIN'
}
);
process Reducer:
const isLoading = (state = false, action) => {
switch (action.type) {
case 'START_LOADING':
return 1;
case 'END_LOADING':
return 0
default:
return 0
}
}
export default isLoading
isLogged Reducer
const isLogged = (state = false, action) => {
switch (action.type) {
case 'SIGN_IN':
return 2;
case 'LOAD_FINISHED':
return 1
case 'SIGN_OUT':
return -1
case 'FIRST_LOGIN':
return 3
case 'FB_LOGIN':
return 4
default:
return 0
}
}
export default isLogged
root Reducer:
import isLogged from './IsLoggedReducer'
import process from './ProcessReducer'
import { combineReducers } from 'redux'
const allReducers = combineReducers({
isLogged: isLogged,
processState: process
})
export default allReducers
store
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk';
import allReducers from './index'
const store = createStore(allReducers, applyMiddleware(thunk))
export default store
Mapping state and actions to props
const mapStateToProps = (state) => {
const { processState, isLogged} = state
return { processState, isLogged }
};
const mapDispatchToProps = (dispatch) => {
return {
//loadFinished: () => dispatch({ type: 'LOAD_FINISHED' })
startProcess: bindActionCreators(start, dispatch),
endProcess: bindActionCreators(stop, dispatch),
}
};
export default connect(mapStateToProps, mapDispatchToProps)(ReportStickerStatus);
When I dispatch startProcess my app got redirected to a empty page
THERES NO ANY ERRORS ON CONSOLE
UPDATE [ SOLVED ]
I had to return the state in each action, even in default
import initialState from './initialState'
const isLogged = (state = initialState, action) => {
switch (action.type) {
case 'SIGN_IN':
return {
...state,
value: 2
};
case 'LOAD_FINISHED':
return {
...state,
value: 1
};
case 'SIGN_OUT':
return {
...state,
value: -1
};
case 'FIRST_LOGIN':
return {
...state,
value: 3
};
case 'FB_LOGIN':
return {
...state,
value: 4
};
default:
return {
...state
};
}
}
export default isLogged
I try to save three values in redux for counters. This is my file with the combineReducers:
import { combineReducers } from 'redux';
const SET_FREUNDE = 'SET_FREUNDE';
const SET_CHATS = 'SET_CHATS';
const SET_VOTES = 'SET_VOTES';
export function setFreunde(value) {
return {
type: SET_FREUNDE,
value,
}
}
export function setChats(value) {
return {
type: SET_CHATS,
value,
}
}
export function setVotes(value) {
return {
type: SET_VOTES,
value,
}
}
const defaults = [
{
countervotes: 1,
counterchats: 1,
counterfreunde: 1
}
];
function counter(state=defaults, action) {
switch (action.type) {
case SET_FREUNDE:
return [
...state,
{
...state.counterfreunde = action.value
}
];
case SET_CHATS:
return [
...state,
{
...state.counterchats = action.value
}
];
case SET_VOTES:
return [
...state,
{
...state.countervotes = action.value
}
];
default:
return state;
}
}
const counters = combineReducers({
counter
});
export default counters;
In my App.tsx I create the store and want to get the values:
import { Provider, useSelector } from "react-redux";
import { createStore} from "redux";
import counters from './src/redux/counter';
const store = createStore(counters);
const AppStack = () => {
const counterfreunde = useSelector((state)=>state.counterfreunde);
const counterchats = useSelector((state)=>state.counterchats);
const countervotes = useSelector((state)=>state.countervotes);
return(
...
)
}
const App = () => {
...
return(
<Provider store={store} >
<AppStack/>
</Provider>
);
}
export default App;
But counterfreunde, ... are undefined. If I set the values directly in the store like this:
const store = createStore(() => ({
counterfreunde: 1,
counterchats: 1,
counterfreunde: 1
}));
all works fine. I think the problem is with the definition of the combineReducer.
When you use combineReducers, you provide it with a "Reducers Map Object" that maps the property names of the state to reducer that controls that property.
const counters = combineReducers({
counter
});
Calling combineReducers like above says that your root state has a property counter which is controlled by your counter reducer.
This is fine in isolation, but it changes your selectors because the properties like counterfreunde are not properties of the top-level state, they are properties of state.counter. So your selectors would need to be like this:
const counterfreunde = useSelector((state) => state.counter.counterfreunde);
If counter is the only reducer that you have then you don't need to use combineReducers. You can keep your selectors the same as they are now by providing your counter reducer as the reducer for the root state.
function counter(state=defaults, action) {
...
}
// remove the combineReducers
export default counter;
const store = createStore(counter);
Yes, the issue is in your combineReducers invocation. Attached is a working, nested reducer formulated by combining other reducers.
import { combineReducers } from 'redux';
import {
RESETPLAN,
SELECTEDPLAN,
selectPlanActionType,
} from './selectedPlan/types';
import { planType } from './plans/types';
import { PROTECTEDPLANS, plansActionType, plansType } from './plans/types';
const defaultSelectedState: planType = {};
const defaultPlanState: plansType = {
list: [],
};
function plansReducer(
state: plansType = defaultPlanState,
action: plansActionType
): plansType {
switch (action.type) {
case PROTECTEDPLANS: {
return {
...state,
...action.payload,
};
}
default: {
return state;
}
}
}
function selectedReducer(
state: planType = defaultSelectedState,
action: selectPlanActionType
): planType {
switch (action.type) {
case SELECTEDPLAN: {
return {
...state,
...action.payload,
};
}
case RESETPLAN: {
return defaultSelectedState;
}
default: {
return state;
}
}
}
export default combineReducers({
myPlans: plansReducer,
selected: selectedReducer,
});
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
})
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.
So I loaded data with axios in my action like this:
import * as types from './actionTypes';
import axios from 'axios';
export function fetchQuestions(city) {
return function (dispatch) {
axios.get('http://rest.learncode.academy/api/test123/tweets')
.then((response) => {
dispatch({type: "FETCH_QUESTIONS_SUCCESS", payload: response.data})
})
.catch((err) => {
dispatch({type: "FETCH_QUESTIONS_FAILURE", payload: err})
})
}
};
And it loads into questions in my reducer:
const initialState = {
fetching: false,
fetched: false,
questions: [],
error: null,
}
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case types.FETCH_QUESTIONS_SUCCESS:
return {
...state,
questions: action.payload
};
case types.FETCH_QUESTIONS_FAILURE:
return {
...state,
error: action.payload
};
default:
return state;
}
}
I call it like this on the first page of my app in the constructor:
constructor(props) {
super(props);
if(!this.props.questions){
this.props.actions.fetchQuestions();
}
}
So with that I want to only use it when questions is empty.
On another page when I do console.log(questions) it tells me questions is empty. While I did load it before so it should still be loaded since I am using redux-persist.
When I click a button on that page and console.log(questions) again it does show the data.