Help me to understand. i use useDispatch but i don't change the state. what am I doing wrong. I read a lot of information, but I can not understand what is my mistake.
i tried other hooks but nothing works.
I've marked up the code below to make it clear. Reducer, action, store, component
component:
import React from 'react';
import {useDispatch} from "react-redux";
import {loginAction, passwordAction} from "../action/userAction";
import storeConfiguration from "../store/storeConfiguration";
import {changeLogin, changePassword} from "../utils/Const";
const Login = () => {
const dispatch = useDispatch()
const stateUser = storeConfiguration.getState()
const senData = () => {
localStorage.setItem(stateUser.login, stateUser.password)
console.log(storeConfiguration.getState());
let keys = Object.keys(localStorage);
for(let key of keys) {
console.log(`${key}: ${localStorage.getItem(key)}`);
dispatch(loginAction())
dispatch(passwordAction())
}
}
function clear() {
localStorage.clear();
}
return (
<div>
<p>Please, enter Username</p>
<input placeholder={'your login'}
onChange={e => changeLogin( e.target.value)}/>
<p>Please, enter Password</p>
<input placeholder={'your password'}
onChange={e => changePassword( e.target.value)}/>
<p></p>
<button onClick={()=>senData()}>Enter</button>
<button onClick={()=>clear()}>clear</button>
</div>
);
};
export default Login;
action:
export const LOGIN = 'loginAction'
export const PASSWORD = 'passwordAction'
export const loginAction = login =>(
{
type: LOGIN,
payload: login
})
export const passwordAction = password =>(
{
type: PASSWORD,
payload: password
})
reducer:
import {LOGIN, PASSWORD} from "../action/userAction";
function userReducer (state, action)
{
switch (action.type){
case LOGIN:
return {...state, login: action.payload }
case PASSWORD:
return {...state, password: action.payload }
default:
return state
}
}
export default userReducer
store:
import userReducer from "../reducer/userReducer";
import { legacy_createStore as createStore} from 'redux'
const initialState =
{
login:'',
password: '',
}
const store = createStore(userReducer, initialState)
export default store
const:
export const currentLogin = 'Admin'
export const currentPassword = '12345'
export const changeLogin = (login) => {
return login
}
export const changePassword = (password) => {
return password
}
In this two lines of code
dispatch(loginAction())
dispatch(passwordAction())
You haven't passed any payload, so nothing can be changed actually
Related
I am currently trying to access my data using the Spotify API. This works very well. Thats the function I am using to get my Data. I assume the other stuff is not important. I can post that, if you need that.
export const getSpotifyUser = (access_token:string) =>{
setAuthorizationHeader(access_token)
axios.get('https://api.spotify.com/v1/me').then((res) => {
console.log(res.data)
})
}
I have set up a redux store and trying to put the credentials into the store, by dispatching the right type (SET_USER).
export const getSpotifyUser = (access_token:string) => (dispatch: any) => {
console.log("function is not called") // Function is not even called why ?
setAuthorizationHeader(access_token)
axios.get('https://api.spotify.com/v1/me').then((res) => {
console.log(res.data)
dispatch ({
type: SET_USER,
payload: res.data
})
}
but as soon as I use dispatch, the function is no longer called.
I really do not see my mistake. Is that a typescript error ?. ( I am using react typescript)
store.js
import { createStore, applyMiddleware } from 'redux'
import rootReducer from './rootReducer'
import { composeWithDevTools } from 'redux-devtools-extension'
import thunk from 'redux-thunk'
const store = createStore(
rootReducer,
composeWithDevTools(applyMiddleware(thunk))
)
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch
export default store
rootReducer.ts
import { combineReducers } from 'redux'
import userReducer from './User/userReducer'
const rootReducer = combineReducers({
user: userReducer,
})
export default rootReducer
userReducer.ts
import { AnyAction } from 'redux'
import { SET_USER } from './userTypes'
interface Credentials {
username: string
email: string
profilepicture: string
id: number
}
interface InitialState {
authenticated: boolean
loadding: boolean
credentials?: Credentials
}
const initialState: InitialState = {
authenticated: false,
loadding: false,
credentials: {} as Credentials,
}
const reducer = (state = initialState, action: AnyAction) => {
switch (action.type) {
case SET_USER: {
return {
...state,
loading: false,
credentials: action.payload,
}
}
default:
return state
}
}
export default reducer
Login.tsx ( I am making the login here. It is working fine, if am not using dispatch
import { IonButton } from '#ionic/react'
import React, { useEffect } from 'react'
import {
getAuthorizeHref,
getHashParams,
removeHashParamsFromUrl,
getSpotifyUser,
} from '../../Helpers/login'
const Login: React.FC = () => {
// const user = useSelector((state: RootState) => state.user.credentials)
useEffect(() => {
const hashParams = getHashParams()
const access_token = hashParams.access_token
// const expires_in = hashParams.expires_in
removeHashParamsFromUrl()
getSpotifyUser(access_token)
}, [])
return (
<IonButton onClick={() => window.open(getAuthorizeHref(), '_self')}>
)}
export default Login
since you're using typescript with react, I believe you have added the getSpotifyUser function to your interface, now if you want to access that i think you should call it like this
props.getSpotifyUser(access_token)
and finally add it to your connect as a dispatch function that's wrapping your component
your login component should be like this one
import { IonButton } from '#ionic/react'
import React, { useEffect } from 'react'
import { connect } from 'react-redux'
import {
getAuthorizeHref,
getHashParams,
removeHashParamsFromUrl,
getSpotifyUser,
} from '../../Helpers/login'
interface ILogin {
getAuthorizeHref: () => any;
getHashParams: () => any;
removeHashParamsFromUrl: () => any;
getSpotifyUser: (access_token) => any;
}
const Login: React.FC = (props: ILogin) => {
// const user = useSelector((state: RootState) => state.user.credentials)
useEffect(() => {
const hashParams = props.getHashParams()
const access_token = hashParams.access_token
// const expires_in = hashParams.expires_in
props.removeHashParamsFromUrl()
props.getSpotifyUser(access_token)
}, [])
return (
<IonButton onClick={() => window.open(props.getAuthorizeHref(), '_self')}>
)}
export default connect(null, {getAuthorizeHref, getHashParams, removeHashParamsFromUrl, getSpotifyUser})(Login)
Basicly Shamim has given the right answer.Any function that uses that dispatch is a redux action, and you have to follow the docs specifically to call that function. You have to use connect to dispatch actions. As an alternative you can use the dispatchHook. If am wrong please please correct me !!!!
Thats the right code I just had to correct Login.tsx
import { IonApp, IonButton } from '#ionic/react'
import React, { useEffect } from 'react'
import { connect } from 'react-redux'
import {
getAuthorizeHref,
getHashParams,
removeHashParamsFromUrl,
getSpotifyUser,
} from '../../Helpers/login'
const style = {
Logo: {
display: 'flex',
justifyContent: 'space-evenly',
color: 'white',
position: 'relative',
top: '70%',
} as const,
}
const Login: React.FC = (props: any) => {
// const user = useSelector((state: RootState) => state.user.credentials)
useEffect(() => {
const hashParams = getHashParams()
const access_token = hashParams.access_token
// const expires_in = hashParams.expires_in
removeHashParamsFromUrl()
console.log('halloeuseeffect')
props.getSpotifyUser(access_token)
console.log('halloeuseeffect')
}, [])
return (
<IonApp>
<IonButton onClick={() => window.open(getAuthorizeHref(), '_self')}>
knsnan
</IonApp>
)
}
export default connect(null, {
getSpotifyUser,
})(Login)
I have spent so much time on this, still can't tell what's going on, I'm working on a react app to send post request to an api, each time i click the add button to dispatch action, I get an error "Action must be plain object"
Here is actions/index.js
import { createAction } from "redux-actions";
import * as actions from "../actionTypes
export const CreateUserRequest = createAction(actions.CREATE_USER_REQUEST);
export const CreateUserSuccess = createAction(actions.CREATE_USER_SUCCESS);
export const CreateUserError = createAction(actions.CREATE_USER_ERROR);
actionTypes/index.js
export const CREATE_USER_REQUEST = "CREATE_USER_REQUEST";
export const CREATE_USER_SUCCESS = "CREATE_USER_SUCCESS";
export const CREATE_USER_ERROR = "CREATE_USER_ERROR";
reducers/createUser.js
import * as actions from "../actionTypes";
const initialState = {
isLoading: false,
isSuccess: false,
isError: false
};
const createUser = (state = initialState, action) => {
switch (action.type) {
case actions.CREATE_USER_REQUEST:
return {
...state,
isLoading: true,
isSuccess: false,
isError: false
};
case actions.CREATE_USER_SUCCESS:
return {
...state,
isLoading: false,
isSuccess: true,
isError: false,
result: action.payload,
};
case actions.CREATE_USER_ERROR:
return {
...state,
isLoading: false,
isSuccess: false,
isError: true,
result: action.payload,
};
default:
return state;
}
};
export default createUser;
reducers/index.js
import { combineReducers } from "redux";
import CreateUserReducer from './createUser';
const rootReducer = combineReducers({
CreateUserStatus:CreateUserReducer,
//logout:logout,
})
export default rootReducer
saga/createUser.js
import { call, put, takeLatest } from "redux-saga/effects";
import { CreateUserSuccess, CreateUserError, CreateUserRequest } from '../actions/index';
import axiosCall from "../../services";
import * as actions from "../actionTypes"
export function* createUserRequest(action) {
try {
const response = yield call(
axiosCall,
"POST",
'/api/createUser',
action.payload,
);
if (response) {
yield put(CreateUserSuccess({ response : response.data}));
} else {
yield put(CreateUserError({ error: "Invalid details" }));
}
} catch (error) {
console.log('errrrrrrrrr::',error)
yield put(CreateUserError({ error: "Invalid detailssssssss" }));
}
yield takeLatest(actions.CREATE__USER_REQUEST, CreateUserRequest);
}
saga/index.js
import { fork, all } from "redux-saga/effects";
import {createUserRequest} from './createUser';
function* rootSaga() {
{
yield all([
fork(createUserRequest),
]);
}
}
export default rootSaga;
store.js
import { createStore, applyMiddleware } from "redux";
import createSagaMiddleware from "redux-saga";
import rootReducer from "../reducers";
import rootSaga from "../saga/index";
import { createLogger } from "redux-logger";
const middleWares = [];
import { composeWithDevTools } from "redux-devtools-extension";
const logger = createLogger({
predicate: () => process.env.NODE_ENV === 'development',
collapsed: true
});
const sagaMiddleware = createSagaMiddleware();
middleWares.push(sagaMiddleware);
middleWares.push(logger);
const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(...middleWares)));
sagaMiddleware.run(rootSaga);
export default store;
Component createUser.js
import React, { useState} from 'react';
import { useDispatch } from "react-redux"
import {
Container,
} from '../../components';
import { createUserRequest } from '../../redux/actions';
const createUser = () => {
const dispatch = useDispatch()
const [newuser,setNewuser] = useState({
first_name : 'Jon',
last_name : 'Doe',
email : 'jondoe#gmail.com',
phone_number: '+91234455',
status:0
})
const handleChange = (e) =>{
setNewuser({...newuser,[e.target.name] : e.target.value});
}
//Add new user
const add_user = ()=>{
dispatch(createUserRequest(newuser));
}
return (
<Container>
<form>
<input type="text" className="form-control" name="first_name" onChange={handleChange}/>
<input type="text" className="form-control" name="last_name" onChange={handleChange}/>
<input type="text" className="form-control" name="email" onChange={handleChange}/>
<input type="text" className="form-control" name="phone_number" onChange={handleChange}/>
<input type="button" className="form-control" onClick={()=>{add_user()}}/>
</form>
</Container>
)
}
export default createUsers;
Error message I am getting now
Uncaught TypeError: Object(...) is not a function
at add_user (createusers.js?2170:55)
at onClick (createusers.js?2170:82)
at HTMLUnknownElement.callCallback (react-dom.development.js?61bb:188)
at Object.invokeGuardedCallbackDev (react-dom.development.js?61bb:237)
at invokeGuardedCallback (react-dom.development.js?61bb:292)
at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js?61bb:306)
at executeDispatch (react-dom.development.js?61bb:389)
at executeDispatchesInOrder (react-dom.development.js?61bb:414)
at executeDispatchesAndRelease (react-dom.development.js?61bb:3278)
at executeDispatchesAndReleaseTopLevel (react-dom.development.js?61bb:3287)
It points out to this line
dispatch(createUserRequest(newuser));
Issue
It appears you are attempting to dispatch your saga function, createUserRequest.
import { createUserRequest } from '../../redux/saga/createUser';
...
//Add new user
const add_user = () => {
dispatch(createUserRequest({ newuser: newuser }));
}
Solution
You need to create an action that accepts a "new user" payload. Perhaps you meant to use CreateUserRequest.
import { createAction } from "redux-actions";
import * as actions from "../actionTypes;
export const CreateUserRequest = createAction(actions.CREATE_USER_REQUEST); // <-- ***
export const CreateUserSuccess = createAction(actions.CREATE_USER_SUCCESS);
export const CreateUserError = createAction(actions.CREATE_USER_ERROR);
So in the createUser component.
import React, { useState } from 'react';
import { useDispatch } from "react-redux";
import { Container } from '../../components';
import { CreateUserRequest } from '../../redux/actions';
const createUser = () => {
const dispatch = useDispatch()
const [newuser, setNewuser] = useState({
first_name : '',
last_name : '',
email : '',
phone_number: '',
status:0
});
...
//Add new user
const add_user = () => {
dispatch(CreateUserRequest({ newuser })); // <-- dispatch action payload
}
return (
...
)
}
For some reason, even though the reducer runs and console.log shows that the correct data was passed to it, the redux store was not updated.
Relevant files:
App.jsx
import {Provider} from 'react-redux';
import store from './store';
const Stack = createStackNavigator();
export default class App extends Component {
render() {
return (
<Provider store={store()}>
Store.js
import {createStore, applyMiddleware} from 'redux';
import rootReducer from '../reducers';
import thunk from 'redux-thunk';
const store = (initialState = {}) =>{
return createStore(
rootReducer,
initialState,
applyMiddleware(thunk)
)
}
export default store;
Register.tsx
...
<Pressable
style={styles.button}
onPress={() => this.props.submitRegistration(this.state)}
>
...
const mapDispatchToProps = (dispatch: any) => {
return {
submitRegistration: (data: any) => {
dispatch(UserActions.submitRegister(data))
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Register);
UserActions
import { signUp } from '../../services/backend';
import { setUser } from '../../actions';
export function submitRegister(data: any) {
return async (dispatch: any) => {
const response = await signUp(data);
const responseData = await response.json();
if(responseData.token) {
console.log('here', responseData);
dispatch(setUser(responseData.user));
}
};
}
Action creator
export const setUser = (user: any) => ({
type: 'SET_USER',
user
});
User Reducer
import { SET_USER } from "../actions/actionTypes"
const initialState = {
user: {}
}
const User = (state = initialState, action: any) => {
switch(action.type) {
case SET_USER:
console.log('here action', action.user);
return { user: action.user}
default:
return state
}
}
export default User;
I would really appreciate any help possible. Seems like I misconfigured in someway because even when I set initial state :
const initialState = {
user: {firstName: "John"}
}
it's not reflected in the redux store.
In your action creator:
export const setUser = (user: any) => (
return {
type: 'SET_USER',
user
});
I am trying to route the user to specific route according to user's role. I am using redux as state management tool. I am trying to access the user object from auth state to route the user.
When I login to the system below are the logical steps that take user to route.
Login to the system
Authenticate and load the user
Redirect user to route according to role
I am trying to access the user.role in DashBoard component which is causing the error.
Please help me understand the bug.
1. Action to login to system
export const login = (email, password) => async dispatch => {
const config ={
headers:{
'Content-Type': 'application/json'
}
};
const body = JSON.stringify({email, password});
try {
const res = await axios.post('/api/v1/midasUsers/login',body,config);
dispatch({
type:LOGIN_SUCCESS,
payload:res.data
});
dispatch(loadUser());
} catch (err) {
console.log(err)
const errors = err.response.data.errors;
if(errors){
errors.forEach(error =>dispatch(setAlert(error.msg,'danger')));
}
dispatch({
type:LOGIN_FAIL
})
}
}
2.loaduser to localstorage to authenticate:
export const loadUser = () => async dispatch => {
console.log("I am inside loaduser");
if(localStorage.token){
setAuthToken(localStorage.token)
}
try {
const res = await axios.get('/api/v1/midasUsers/auth');
dispatch({
type: USER_LOADED,
payload:res.data
})
} catch (err) {
dispatch({
type: AUTH_ERROR
})
}
}
3. DashBord.js - Component to route the user
import React,{useEffect} from 'react';
import {Redirect} from 'react-router-dom';
import { connect} from 'react-redux';
import PropTypes from 'prop-types';
//import store from '../../store';
//import {loadUser} from '../../action/auth';
const Dashboard = ({auth:{user,loading,isAuthenticated}}) => {
if(user.role === 'admin'){
return <Redirect to='/adminLandingPage'/>
}
}
Dashboard.propTypes = {
auth:PropTypes.object.isRequired
}
const mapStateToProps = state => ({
auth : state.auth
})
export default connect(mapStateToProps,{})(Dashboard);
I am calling loadUser everytime App.js is mounted the same function after LOGIN_SUCCESS action calls this route
GET /%3Canonymous%3E
Please help me understand the issue
auth.reducer
import {REGISTER_SUCCESS,
REGISTER_FAIL,
USER_LOADED,
AUTH_ERROR,
LOGIN_SUCCESS,
LOGIN_FAIL,
LOGOUT} from '../action/types';
const initialState = {
token : localStorage.getItem('token'),
isAuthenticated : null,
loading: true,
user:null
}
export default function(state= initialState, action){
const {type, payload} = action;
switch (type) {
case USER_LOADED:
return{
...state,
isAuthenticated:true,
loading: false,
user:payload
}
case REGISTER_SUCCESS:
case LOGIN_SUCCESS:
localStorage.setItem('token', payload.token);
return{
...state,
...payload,
isAuthenticated:true,
loading:false,
}
case REGISTER_FAIL:
case AUTH_ERROR:
case LOGIN_FAIL:
case LOGOUT:
localStorage.removeItem('token');
return{
...state,
token:null,
isAuthenticated:false,
loading:false
}
default:
return state;
}
}
store.js
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
index.js in reducers folder
import { combineReducers } from 'redux';
import alert from './alert';
import auth from './auth';
export default combineReducers ({
alert,
auth
});
import React, { Fragment, useState } from 'react';
import { Link, Redirect } from 'react-router-dom';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { login } from '../../action/auth';
const Login = ({ login, isAuthenticated, user }) => {
const [formData, setFormData] = useState({
email: '',
password: ''
});
const { email, password } = formData;
const onChange = e =>
setFormData({ ...formData, [e.target.name]: e.target.value });
const onSubmit = async e => {
e.preventDefault();
login(email, password);
};
//Redirect if logged in
if(isAuthenticated){
//console.log(user.role);
return <Redirect to ="/dashboard"/>
}
return (
<Fragment>
<h1 className='large text-primary'>Sign In</h1>
<p className='lead'>
<i className='fas fa-user' /> Sign Into Your Account
</p>
<form className='form' onSubmit={e => onSubmit(e)}>
<div className='form-group'>
<input
type='email'
placeholder='Email Address'
name='email'
value={email}
onChange={e => onChange(e)}
/>
</div>
<div className='form-group'>
<input
type='password'
placeholder='Password'
name='password'
value={password}
onChange={e => onChange(e)}
minLength='6'
/>
</div>
<input type='submit' className='btn btn-primary' value='Login' />
</form>
<p className='my-1'>
Don't have an account? <Link to='/register'>Sign Up</Link>
</p>
</Fragment>
);
};
Login.propTypes = {
login: PropTypes.func.isRequired,
isAuthenticated:PropTypes.bool
};
const mapStateToProps = state =>({
isAuthenticated : state.auth.isAuthenticated,
user:state.auth.user
})
export default connect(mapStateToProps,
{ login }
)(Login);
I have this login component which redirects user to dashbaord and at dashbaord i am not able to access the user.role from state
I'm using https://arc.js.org/ to setup a new project and am confused on how data is passed about. I'm just using a form to post some login data and can't seem to get the data to the post itself in my actions/sagas (meaning, my login form component has the data and tries to send it on, but after dispatching the action, value is undefined).
Intending to get the form values into redux store (updating onChange to be accessible onSubmit, not passing up as I am now), but wanted to get this version working first and then move on to that so I know what's actually happening.
Let me know if missing necessary info here.
LoginFormContainer:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { userLoginRequest } from 'store/actions'
import { fromUser } from 'store/selectors'
import { LoginForm } from 'components'
class LoginFormContainer extends Component {
static propTypes = {
login: PropTypes.func.isRequired,
}
onSubmit = (event) => {
event.preventDefault()
const serialize = new FormData(event.target)
const loginData = {
email: serialize.get('email'),
password: serialize.get('password'),
}
this.props.login(loginData)
}
render() {
return <LoginForm handleSubmit={this.onSubmit} />
}
}
const mapStateToProps = (state) => ({
user: fromUser.getUser(state),
})
const mapDispatchToProps = (dispatch, { loginData }) => ({
login: () => dispatch(userLoginRequest(loginData)),
})
export default connect(mapStateToProps, mapDispatchToProps)(LoginFormContainer)
LoginFormComponent:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import { Field } from 'components'
const Form = styled.form`
width: 100%;
`
const LoginForm = ({ handleSubmit }) => {
return (
<Form onSubmit={handleSubmit}>
<Field
label="Email"
name="email"
type="text"
/>
<Field
label="Password"
name="password"
type="text"
/>
<button type="submit">Login</button>
</Form>
)
}
LoginForm.propTypes = {
handleSubmit: PropTypes.func.isRequired,
handleChange: PropTypes.func.isRequired,
}
export default LoginForm
Actions:
export const USER_LOGIN_REQUEST = 'USER_LOGIN_REQUEST'
export const USER_LOGIN_SUCCESS = 'USER_LOGIN_SUCCESS'
export const USER_LOGIN_FAILURE = 'USER_LOGIN_FAILURE'
// This doesn't know what data is (undefined)
export const userLoginRequest = (data, resolve, reject) => ({
type: USER_LOGIN_REQUEST,
data,
resolve,
reject,
})
export const userLoginSuccess = detail => ({
type: USER_LOGIN_SUCCESS,
detail,
})
export const userLoginFailure = error => ({
type: USER_LOGIN_FAILURE,
error,
})
Sagas:
import { take, put, call, fork } from 'redux-saga/effects'
import api from 'services/api'
import * as actions from './actions'
// This doesn't know what loginData is (undefined)
export function* login(loginData) {
try {
const encoded = window.btoa(`${loginData.email}:${loginData.password}`)
const data = yield call(api.post, '/login', { Authorization: `Basic ${encoded}` })
yield put(actions.userLoginSuccess(data))
} catch (e) {
yield put(actions.userLoginFailure(e))
}
}
export function* watchUserLoginRequest() {
while (true) {
const { data } = yield take(actions.USER_LOGIN_REQUEST)
yield call(login, data)
}
}
export default function* () {
yield fork(watchUserLoginRequest)
}
Thanks to #dagatsoin for helping lead in right direction!
mapDispatchToProps should be:
const mapDispatchToProps = (dispatch) => ({
login: (loginData) => dispatch(userLoginRequest(loginData)),
})