useEffect hook problem, use redux to get data from backend to frontend - redux

Hello i am new to programming, and i have learn to use MERN to make a sign up,i have no issue with backend but when i tried to use redux ,i have this problem in the frontend
enter image description here
This is the code for the SignInScreen.js
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { Link, useLocation, useNavigate } from 'react-router-dom'
import { login } from '../actions/userAction'
export const SignInScreen = (props, history) => {
const navigate = useNavigate
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const submitHandler = (e) => {
e.preventDefault()
dispatch(login(email, password))
}
const { search } = useLocation()
const redirectInUrl = new URLSearchParams(search).get('redirect')
const redirect = redirectInUrl ? redirectInUrl : '/'
const userlogin = useSelector((state) => state.userlogin)
const { userInfo, loading, error } = userlogin
const dispatch = useDispatch()
useEffect(() => {
if (userInfo) {
navigate(redirect)
}
}, [navigate, userInfo, redirect])
I dont know what wrong with the code,but i do know that it connected with the redux store which have reducer,action and constant..this is for the redux store.js
import { createStore, combineReducers, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import {
userLoginReducer,
} from './reducers/userReducer'
const userInfo = localStorage.getItem('userInfo')
? JSON.parse(localStorage.getItem('userInfo'))
: null
const initialState = {
userLogin: { userInfo },
}
const reducer = combineReducers({
userLogin: userLoginReducer,
})
const middleware = [thunk]
const store = createStore(
reducer,
initialState,
compose(applyMiddleware(...middleware))
)
export default store
This is for constant
export const USER_LOGIN_REQUEST = 'USER_LOGIN_REQUEST'
export const USER_LOGIN_SUCCESS = 'USER_LOGIN_SUCCESS'
export const USER_LOGIN_FAIL = 'USER_LOGIN_FAIL'
export const USER_LOGOUT = 'USER_LOGOUT'
this is userReducer.js
import {
USER_LOGIN_REQUEST,
USER_LOGIN_SUCCESS,
USER_LOGIN_FAIL,
USER_LOGOUT,
} from '../constants/userConstant'
function userLoginReducer(state = {}, action) {
switch (action.type) {
case USER_LOGIN_REQUEST:
return { loading: true }
case USER_LOGIN_SUCCESS:
return { loading: false, userInfo: action.payload }
case USER_LOGIN_FAIL:
return { loading: false, error: action.payload }
case USER_LOGOUT:
return {}
default:
return state
}
}
export userLoginReducer
and lastly for user.js
import Axios from 'axios'
import {
USER_LOGIN_REQUEST,
USER_LOGIN_SUCCESS,
USER_LOGIN_FAIL,
} from '../constants/userConstant'
const login = (email, password) => async (dispatch) => {
try {
dispatch({ type: USER_LOGIN_REQUEST })
const config = { headers: { 'Content-Type': 'application/json' } }
const { data } = await Axios.post(
'/api/users/login',
{ email, password },
config
)
dispatch({ type: USER_LOGIN_SUCCESS, payload: data })
localStorage.setItem('userInfo', JSON.stringify(data))
} catch (error) {
dispatch({
type: USER_LOGIN_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
})
}
}
export login
I just want to get the data into the userInfo but it dont recognized them and said it is a TypeError..I hope u can help me with this..im using the redux latest version

The problem is in userLoginReducer. Each reducer should return a complete new copy of the store.
If you return just the changes, the object you return replaces the entire state.
For example in this code:
switch (action.type) {
case USER_LOGIN_REQUEST:
return { loading: true };
}
The state { userLogin: { userInfo } } will be replaced with { loading: true }. Then you will not have userLogin anymore in the state. That's why you get the error.
To overcome this problem, spread the previous state in returned object (for all actions):
switch (action.type) {
case USER_LOGIN_REQUEST:
return { ....state, loading: true }; // ...state copies exist state to the new copy of state
}
Note: To easily solve similar bugs in the future, I recommend to use redux devtools extension. It is a great extension for debugging and look at changes in redux store.

I have tried that but still cannot fix my problem,this is the rest of my signinScreen,js
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { Link, useLocation, useNavigate } from 'react-router-dom'
import { login } from '../actions/userAction'
export const SignInScreen = (props, history) => {
const navigate = useNavigate
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const submitHandler = (e) => {
e.preventDefault()
dispatch(login(email, password))
}
const { search } = useLocation()
const redirectInUrl = new URLSearchParams(search).get('redirect')
const redirect = redirectInUrl ? redirectInUrl : '/'
const userlogin = useSelector((state) => state.userlogin)
const { userInfo, loading, error } = userlogin
const dispatch = useDispatch()
useEffect(() => {
if (userInfo) {
navigate(redirect)
}
}, [navigate, userInfo, redirect])
return (
<Container className="small-container">
<h1 className="my-3">Sign In</h1>
<Form onSubmit={submitHandler}>
<Form.Group className="mb-3" controlId="email">
<Form.Label>Email</Form.Label>
<Form.Control
type="email"
required
onChange={(e) => setEmail(e.target.value)}
/>
</Form.Group>
<Form.Group className="mb-3" controlId="password">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
required
onChange={(e) => setPassword(e.target.value)}
/>
</Form.Group>
<div className="mb-3">
<Button type="submit">Sign In</Button>
</div>
<div className="mb-3">
New Customer?{' '}
<Link to={`/signup?redirect=${redirect}`}>Create new account</Link>
</div>
</Form>
</Container>
)
}
it still show the same error like before,i have no idea to solve this

Related

TypeError: (0 , _react.createContext) is not a function on Nextjs using Redux

im new in using redux and tried to use it with the help of online tutorials. nut im seeing this error:
Server Error TypeError: (0 , _react.createContext) is not a function
This error happened while generating the page. Any console logs will be displayed in the terminal window.
im providing my codes bellow:
authSlice.js file:
import { createSlice } from "#reduxjs/toolkit";
import { HYDRATE } from "next-redux-wrapper";
const initialState = {
authState: false,
userInfo: {},
authToken: ""
}
export const authSlice = createSlice({
name: "auth",
initialState,
reducers: {
setAuthState(state, action) {
state.authState = action.payload
}
, setUserInfo(state, action) {
state.userInfo = action.payload
},
setToken(state, action) {
state.authToken = action.payload
},
setAuth(state, action) {
state.authState = action.payload.authState;
state.userInfo = action.payload.userInfo;
state.authToken = action.payload.authToken;
}
},
extraReducers: {
[HYDRATE]: (state, action) => {
return {
...state,
...action.payload.auth
}
}
}
})
export const { setAuthState, setUserInfo, setToken, setAuth } = authSlice.actions
export const selectAuthState = (state) => state.auth.authState;
export const selectAuthToken = (state) => state.auth.authToken;
export const selectUserInfo = (state) => state.auth.userInfo;
export default authSlice.reducer
store.js codes:
import { configureStore, ThunkAction, Action } from "#reduxjs/toolkit";
import { authSlice } from "./authSlice";
import { createWrapper } from "next-redux-wrapper";
const makeStore = () =>
configureStore({
reducer: {
[authSlice.name]: authSlice.reducer,
},
devTools: true,
});
const store = makeStore()
export default store
components where i tried to use redux:
import { useRouter } from "next/navigation";
import { useDispatch, useSelector } from "react-redux";
import {
selectAuthState,
selectAuthToken,
selectUserInfo,
setAuthState,
setUserInfo,
} from "#/data/authSlice";
function Sidebar() {
const router = useRouter();
const dispatch = useDispatch();
const authState = useSelector(selectAuthState);
const authToken = useSelector(selectAuthToken)
const userInfo = useState(selectUserInfo)
useEffect(() => {
if (!authState) {
router.push("/")
}
}, [router.asPath]);
// rest of the codes
import axios from "axiosConfig";
import { useDispatch, useSelector } from "react-redux";
import {
selectAuthState,
selectAuthToken,
setAuthState,
setUserInfo,
} from "#/data/authSlice";
// toastify
// import { toast } from 'react-toastify';
// import 'react-toastify/dist/ReactToastify.css';
// const notify = (text) => {
// toast.success(text , {position: toast.POSITION.BOTTOM_RIGHT})
// }
const LoginPage = () => {
const router = useRouter();
const dispatch = useDispatch();
const authState = useSelector(selectAuthState);
useEffect(() => {
const token = authState;
if (token) {
router.push("/Dashboard");
} else {
}
}, [router.asPath]);
// rest of the codes
i tried to reinstall redux and react-redux but didnt helped.
also tried Remove the node_modules
Remove package-lock.json
npm install again.
but nothing happend.

next-redux-wrapper HYDRATION failed

I am trying to integrate next-redux-wrapper to Next.js RTK project. When invoking async action from getServerSideProps, I am getting state mismatch error (see the image below).
When I dispatch action from client side (increment/decrement), everything works well. I think issue is related to HYDRATION but so far all my efforts have failed.
I tried mapping redux state to props, storing props in component state, added if statements to check values but nothing seem to work. I've been stuck on this for 2 weeks. I'm not sure what else to try next.
"next": "12.3.1",
"next-redux-wrapper": "^8.0.0",
"react":
"18.2.0",
"react-redux": "^8.0.4"
store/store.js
import { configureStore, combineReducers } from "#reduxjs/toolkit";
import counterReducer from "./slices/counterSlice";
import { createWrapper, HYDRATE } from "next-redux-wrapper";
const combinedReducer = combineReducers({
counter: counterReducer,
});
const reducer = (state, action) => {
if (action.type === HYDRATE) {
const nextState = {
...state, // use previous state
...action.payload, // apply delta from hydration
};
return nextState;
} else {
return combinedReducer(state, action);
}
};
export const makeStore = () =>
configureStore({
reducer,
});
export const wrapper = createWrapper(makeStore, { debug: true });
store/slices/counterSlice.js
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
import axios from "axios";
const initialState = {
value: 0,
data: { quote: "" },
pending: false,
error: false,
};
export const getKanyeQuote = createAsyncThunk(
"counter/kanyeQuote",
async () => {
const respons = await axios.get("https://api.kanye.rest/");
return respons.data;
}
);
export const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload;
},
},
extraReducers: (builder) => {
builder
.addCase(getKanyeQuote.pending, (state) => {
state.pending = true;
})
.addCase(getKanyeQuote.fulfilled, (state, { payload }) => {
state.pending = false;
state.data = payload;
})
.addCase(getKanyeQuote.rejected, (state) => {
state.pending = false;
state.error = true;
});
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
pages/index.js
import React, { useState } from "react";
import { useSelector, useDispatch, connect } from "react-redux";
import {
decrement,
increment,
getKanyeQuote,
} from "../store/slices/counterSlice";
import { wrapper } from "../store/store";
function Home({ data }) {
const count = useSelector((state) => state.counter.value);
// const { data, pending, error } = useSelector((state) => state.counter);
const dispatch = useDispatch();
const [quote, setQuote] = useState(data.quote);
return (
<div style={{ display: "flex", flexDirection: "column" }}>
{/* <span>{pending && <p>Loading...</p>}</span>
<span>{error && <p>Oops, something went wrong</p>}</span> */}
<div>{quote}</div>
<span>Count: {count}</span>
<div>
<button
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
Increment
</button>
<button
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
Decrement
</button>
</div>
</div>
);
}
export const getServerSideProps = wrapper.getServerSideProps(
(store) =>
async ({ req, res, ...etc }) => {
console.log(
"2. Page.getServerSideProps uses the store to dispatch things"
);
await store.dispatch(getKanyeQuote());
}
);
function mapStateToProps(state) {
return {
data: state.counter.data,
};
}
export default connect(mapStateToProps)(Home);
Errors in console
This might stem from a known issue where next-redux-wrapper 8 hydrates too late. Please try downgrading to version 7 for now and see if that resolves the problem.

Refactoring reducers and actions in a feature based architecture style

This is a purely hypothetical example while I am trying to learn NextJs and I added Redux following the documentation from the official link
but I want to format the code in a certain way, similar to feature based architecture where each feature is encapsulated in a separate folder.
Please feel free to add, remove and suggest any changes, they are all most welcoming.
This is my general folder structure
components
public
pages
|-accounts
| |-index.tsx
|-_app.tsx
|-index.tsx
store
|-account
| |-accountsApi.ts
| |-actions.ts
| |-index.ts
| |-reducer.ts
| |-types.ts
|-index.ts
styles
package.json
...
And this is the Accounts component under accounts folder at pages.
I am dispatching the login action from action creators, is there any way to make the action accept an object with the credentials?
//pages/account/index.tsx
import Link from 'next/link'
import { useState } from 'react'
import Layout from '../../components/Layout'
import { useDispatch } from "react-redux"
import type { Account } from '../../store/account/types'
import { accoutActions } from '../../store/account'
type Props = {
accounts: Account[]
}
const Accounts: React.FunctionComponent<Props> = ({ accounts }) => {
const dispatch = useDispatch();
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const handleSubmit = (event: any) => {
let obj = {
username: username,
password: password
}
dispatch(accoutActions.login(obj)); // how to set it up, so that the action accepts an object with credentials
}
return (
<Layout title="Accounts | Demo">
<h1>Accounts</h1>
<p>You are currently on: /accounts</p>
<p>
<Link href="/">
<a>Go home</a>
</Link>
</p>
<div>
<p>Login</p>
<div>
<label htmlFor="username">Username:</label>
<input onChange={e => setUsername(e.target.value)} type="text" id="username" name="username" value={username} />
<label htmlFor="password">Last name:</label>
<input onChange={e => setPassword(e.target.value)} type="text" id="password" name="password" value={password} />
<button type="submit" onClick={handleSubmit}>Login</button>
</div>
</div>
</Layout>
)
}
export default Accounts
Below is my account store with its respective files:
accountsAPI.ts where the api calls to the server are made, I will try to refactor later in a singleton pattern using axios
however I am learning what NextJs allows and what not.
import { CreateAccountInputModel, LoginInputModel } from "./types"
const url = 'https://127.0.0.1:8888/api/accounts'
export const createAccount = async (objModel: CreateAccountInputModel): Promise<any> => {
const response = await fetch(`${url}/create`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(objModel),
})
const result = await response.json()
return result
}
export const loginAccount = async(objModel: LoginInputModel): Promise<any> => {
const response = await fetch(`${url}/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(objModel),
})
const result = await response.json()
return result
}
export const logoutAccount = async () : Promise<any> => {
const response = await fetch(`${url}/logout`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
})
const result = await response.json()
return result
}
actions.ts contains account actions
import { createAction } from '#reduxjs/toolkit'
import { AccountActionTypes } from './types'
const create = createAction(AccountActionTypes.CREATE)
const login = createAction(AccountActionTypes.LOGIN)
const logout = createAction(AccountActionTypes.LOGOUT)
export const actionCreators = {
create,
login,
logout,
};
index.ts where I combine reducer from the reducer file and make a slice so that I can join all reducers to the store
I want to be able the reducers in a separate file
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit'
import { CreateAccountInputModel } from './types'
import type { AppState, AppThunk } from '..'
import { createAccount, loginAccount, logoutAccount } from './accountsAPI'
import { reducer, initialState } from './reducer'
export const createAsync = createAsyncThunk(
'account/create',
async (objModel: CreateAccountInputModel) => {
const response = await createAccount(objModel)
// The value we return becomes the `fulfilled` action payload
return response.data
}
)
export const accountSlice = createSlice({
name: 'account',
initialState,
reducers: reducer, // error: Type 'ReducerWithInitialState<AccountState>' is not assignable to type 'ValidateSliceCaseReducers<AccountState, SliceCaseReducers<AccountState>>'.
extraReducers: (builder) => {
builder
.addCase(createAsync.pending, (state) => {
state.status = 'loading'
})
.addCase(createAsync.fulfilled, (state, action) => {
state.status = 'idle'
})
},
})
export { actionCreators as accoutActions } from './actions';
export const isAuthenticated = (state: AppState) => state.account.isAuthenticated
export default accountSlice.reducer
Here are reducers reducer.ts and the initial state of the given reducer
import { createReducer, PayloadAction } from '#reduxjs/toolkit'
import { actionCreators } from './actions';
import { Account, AccountState } from './types';
export const initialState: AccountState = {
isAuthenticated: false,
token: '',
status: 'idle',
accounts: [] as Account[],
}
export const reducer = createReducer(initialState, (builder) => {
builder
.addCase(actionCreators.login, (state, action: PayloadAction<any>) => {
state.isAuthenticated = action.payload.isAuthenticated
state.token = action.payload.token
// can I do, return { ...state, ...} so that the previous state is preserved?
})
.addCase(actionCreators.logout, (state, action) => {
state.isAuthenticated = false
})
.addCase(actionCreators.create, (state, action: PayloadAction<any>) => {
state.accounts = [...state.accounts, action.payload]
}).addDefaultCase((state, action) => {
state.isAuthenticated = false
state.token = ''
})
})
All the types and models are stored in types.ts file.
I can write types.ts if it is required, but you can assume a general case of an account.
is there any way to make the action accept an object with the credentials?
const login = createAction<TypeOfObjectWithCredentials>(AccountActionTypes.LOGIN)
Calling login with an object will put it inside the payload automatically.

How do I use redux toolkit's createSlice to create localStorage?

I am making a cart functionality using redux toolkit's createSlice. But in the browser's application tab, the value of the localStorage is showing as [object Object]. Can someone help me with this please?
cartSlice.js
import { createSlice } from '#reduxjs/toolkit';
import axios from 'axios'
const cartItemsFromStorage = localStorage.getItem('cartItems') ? localStorage.getItem('carts') : []
export const cartSlice = createSlice({
name: 'cart',
initialState: {
cartItems: cartItemsFromStorage,
},
reducers: {
add: (state, action) => {
const item = action.payload
const existItem = state.cartItems.find(x => x.product === item.product)
if (existItem) {
const currentItems = state.cartItems.map(x => x.product === existItem.product ? item : x)
state.cartItems = [...currentItems]
} else {
state.cartItems = [...state.cartItems, item]
localStorage.setItem('cartItems', state.cartItems)
}
},
// remove: (state, action) => {
// },
},
});
const { add } = cartSlice.actions;
export const selectCartItems = state => state.cart.cartItems;
export const addToCart = (id, qty) => async (dispatch) => {
const { data } = await axios.get(`/api/products/${id}`)
dispatch(add({
product: data._id,
name: data.name,
imgae: data.imgae,
price: data.price,
countInStock: data.countInStock,
qty
}))
}
export default cartSlice.reducer;
CartScreen.js
import React, { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { selectCartItems, addToCart } from '../features/cartSlice'
const CartScreen = ({ match, location, history }) => {
const productId = match.params.id
const qty = location.search ? Number(location.search.split('=')[1]) : 1
const dispatch = useDispatch()
const cartItems = useSelector(selectCartItems)
useEffect(() => {
if (productId) {
dispatch(addToCart(productId, qty))
}
console.log(`cartItems: ${cartItems}`)
}, [dispatch, productId, qty])
return (
<div>
Cart
</div>
)
}
export default CartScreen
The next time I refresh the page, the initialState is not there, insted it shows [object Object]. I know the problem is with localStorage. Please correct me.
I think the problem is you are doing localStorage stuff in a reducer action. Reducer only can do simple operations and modify the state, so I encourage you to try to pass localStorage calls into the thunk action.

Redux: TypeError: e is undefined

https://github.com/reduxjs/redux/issues/3017
Problem: Occurs when I wrap my action creator with a dispatch in the container area where I utilize the connect method--I followed the style from redux documentation.
I am utilizing redux, and redux thunk. I am attempting to create a login action, so far it does not work when I dispatch an action, which dispatch's an another one.
LoginContainer.js
import CONFIG from "../../../config";
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import {authenticateUser} from "../../../actions/authenticateUser";
import Login from '../../../components/views/login/Login'
import {store} from '../../../store';
function handleSubmit(e) {
e.preventDefault();
let calpersId = parseInt(e.target[0].value || e.target[1].value, 10) || 0;
store.dispatch(authenticateUser(calpersId))
}
const mapStateToProps = (state) => {
return {
authentication: state.authentication
}
}
const mapDispatchToProps = (dispatch) => {
return {
handleSubmit: (e) => {dispatch(handleSubmit(e))}
}
}
const LoginContainer = connect(mapStateToProps, mapDispatchToProps)(Login);
export default LoginContainer;
authenticateUser.action.js
import CONFIG from '../config'
export const AUTHENTICATE_USER = 'AUTHENTICATE_USER'
export const initiateUserAuthentication = (token) => ({
type: AUTHENTICATE_USER,
token
})
export const AUTHENTICATATION_SUCCEEDED = 'AUTHENTICATATION_SUCCEEDED'
export const authenticatationSucceeded = (payload) => ({
type: AUTHENTICATE_USER,
payload
})
export const USER_ID_DOES_NOT_EXIST = 'USER_ID_DOES_NOT_EXIST'
export const userIdDoesNotExist = (uid) => ({
type: USER_ID_DOES_NOT_EXIST,
uid,
message: "User id does not exist"
})
export function authenticateUser(id) {
return function (dispatch) {
let guidMap = {
7103503579: "dad08fde-0ac1-404a-ba8a-cc7c76d5810f",
6632408185: "6632408185-guid",
6581985123: "6581985123-guid",
1226290314: "a3908aa7-c142-4752-85ea-3741cf28f75e",
4618604679: "4618604679-guid",
6452522440: "6452522440-guid",
3685610572: "3685610572-guid",
5564535492: "5564535492-guid",
5600493427: "5600493427-guid",
3996179678: "3996179678-guid",
7302651964: "7302651964-guid",
3148148090: "3148148090-guid",
5826752269: "5826752269-guid",
6827859055: "6827859055-guid",
1677401305: "1677401305-guid",
2640602392: "dbed1af6-0fc9-45dc-96a3-ab15aa05a7a2",
6474994805: "6474994805-guid"
};
let guid = guidMap[id]
return fetch(CONFIG.API.MY_CALPERS_SERVER.LOCATION + 'ept/development/rest/simulatedAuth.json?guid=' + guid, {
credentials: 'include'
})
.then(
response => response.json(),
error => console.log('An error occured.', error))
.then(json => {
document.cookie = "authentication=" + guid + "; max-age=" + (60 * 30);
dispatch(authenticatationSucceeded(json))
})
}
}
authenticateUser.reducer.js
import {AUTHENTICATE_USER, AUTHENTICATATION_SUCCEEDED} from "../actions/authenticateUser";
const initialState = {
calpersIds: [
5600493427,
6474994805,
6452522440,
5564535492,
6632408185,
4618604679,
5826752269,
3996179678,
7302651964,
1677401305,
6827859055,
3685610572,
6581985123,
3148148090
],
guidMap: {
7103503579: "dad08fde-0ac1-404a-ba8a-cc7c76d5810f",
6632408185: "6632408185-guid",
6581985123: "6581985123-guid",
1226290314: "a3908aa7-c142-4752-85ea-3741cf28f75e",
4618604679: "4618604679-guid",
6452522440: "6452522440-guid",
3685610572: "3685610572-guid",
5564535492: "5564535492-guid",
5600493427: "5600493427-guid",
3996179678: "3996179678-guid",
7302651964: "7302651964-guid",
3148148090: "3148148090-guid",
5826752269: "5826752269-guid",
6827859055: "6827859055-guid",
1677401305: "1677401305-guid",
2640602392: "dbed1af6-0fc9-45dc-96a3-ab15aa05a7a2",
6474994805: "6474994805-guid"
},
authToken: null,
isAuthenticated: false
};
//#TODO: All fetches, create a seperate reducer for store?
export function authenticateUser(state = initialState, action) {
switch(action.type) {
case AUTHENTICATE_USER:
return Object.assign({}, state, {
authToken: action.token,
})
case AUTHENTICATATION_SUCCEEDED:
return Object.assign({}, state, {
authToken: action.payload.guid,
isAuthenticated: true,
payload: action.payload
})
default:
return state;
}
};
You should'nt use connect mapDispatchToProps like you are doing.
This callback is supposed to create or use functions that will dispatch an action.
For your case you can use it like that:
const mapDispatchToProps = (dispatch) => {
return {
authenticate: calpersId => authenticateUser(calpersId)(dispatch)
}
}
And in your component have a function/method that handle the submit:
class Login extends Component {
...
handleSubmit = e => {
e.preventDefault();
const calpersId = parseInt(e.target[0].value || e.target[1].value, 10) || 0;
this.props.authenticate(calpersId)
}
...
By the way a reducer is supposed to represent the state of an entity. An entity named autenticateUser is pretty ambigious. You should propably named it user. You should read more redux examples to really catch the concept that at first a bit complicated to understand. There are good videos on Youtube.
Turns out I was calling an action creator which did not exist, I simply needed to pass my dispatch to the handler, and let it handle the the event.
Login.js
import CONFIG from "../../../config";
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import {authenticateUser} from "../../../actions/authenticateUser";
import Login from '../../../components/views/login/Login'
function handleSubmit(e, dispatch) {
e.preventDefault();
let calpersId = parseInt(e.target[0].value || e.target[1].value, 10) || 0;
dispatch(authenticateUser(calpersId))
}
const mapStateToProps = (state) => {
return {
authentication: state.authentication
}
}
const mapDispatchToProps = (dispatch) => {
return {
handleSubmit: (e) => {handleSubmit(e, dispatch)}
}
}
const LoginContainer = connect(mapStateToProps, mapDispatchToProps)(Login);
export default LoginContainer;
What is the proper way of doing this, I utillized bindActionCreators which yields the same result.

Resources