How to access to set redux props to react state? - reactjs

This is my code and I try to set email of user to state and set it with other data:
import React, {Component} from "react";
import PropTypes from "prop-types";
import * as actionCreators from '../../actions/authAction';
import {loadUser, setPass , register} from "../../actions/authAction";
import {connect} from "react-redux";
import { bindActionCreators } from "redux";
import {clearError} from "../../actions/errorAction";
import {toast} from "react-toastify";
import store from "../../store";
class RegisterFinal extends Component {
componentDidMount() {
store.dispatch(loadUser());
}
componentDidUpdate(nextProps) {
if (nextProps.user !== this.props.user) {
this.setState({ email: this.props.user});
}
}
state = {
userName: "",
password: "",
passwordConfirm: "",
email: "",
msg: null
}
static propTypes = {
isAuthenticated: PropTypes.bool,
setPass: PropTypes.bool,
register: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
user : PropTypes.object.isRequired,
};
onSubmit = (e) => {
e.preventDefault();
const {password, userName, email} = this.state
const setPass = {
password, userName, email
}
this.props.setPass(setPass);
const {passwordConfirm} = e.target;
const errors = {};
if (password.value !== passwordConfirm.value) {
errors.passwordMismatch = "Entered passwords do not match.";
console.log(errors)
}
}
onChange = (e) => {
this.setState({
[e.target.name]: e.target.value,
});
};
render() {
return (
<div className={"container"}>
<div className={"row row-of-final-register justify-content-center"}>
<div className={"mt-5 register-teacher-inputs-box final-register-teacher-inputs-box"}>
<div className={"final-register-wrapper"}>
<form className={"mt-5"} onSubmit={this.onSubmit}>
<div className={"row"}>
<div className={"col-12"}>
<label
htmlFor={"userName"} className={"text-right username-label"}>
<span>*</span>
</label>
<input type="text" className="form-control w-100" placeholder={"Username"}
name={"userName"}
autoComplete="true"
value={this.userName}
onChange={this.onChange}
onFocus={(e) => e.target.placeholder = ""}
/>
</div>
</div>
<div className={"row"}>
<div className={"col-12 col-lg-6 mt-3"}>
<label
htmlFor={"password"} className={" text-right"}>
<span>*</span>
</label>
<input type="password" className="form-control " placeholder={"Password"}
name={"password"}
value={this.password}
onChange={this.onChange}
onFocus={(e) => e.target.placeholder = ""}
/>
</div>
</div>
<div className={"row mt-3 pt-2"}>
<div className={"col-12 final-register-wrapper final-register-btn"}>
<button type={"submit"} className={"final-register-btn"}>Submit</button>
</div>
</div>
</form>
</div>
</div>
</div>
)
}
}
function mapStateToProps (state , ownProperties) {
console.log(state.auth.user)
return {
setPass: state.auth.setPass,
isAuthenticated: state.auth.isAuthenticated,
error: state.error,
auth: state.auth,
user : state.auth.user,
}
};
function mapDispatchToProps(dispatch) {
return bindActionCreators(actionCreators, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps
)(RegisterFinal);
When run I have this data in my email state :
But when I try to access email get Error that cannot read property ‘email’ of undefined, I just change email : this.props.user to email : this.props.user.email in componentDidUpdate. I actually need to set redux props to react state. I'm new in redux.
Here share my project : my project

Here is a working example that will async load a user, set the initial value of a textbox with it's value and submit changes:
const { Provider, connect } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
const { createSelector } = Reselect;
const initialState = {
user: null,
loading: true, //initially set loading to true
};
//helper for async
const later = (value) =>
new Promise((resolve) =>
setTimeout(() => resolve(value), 2000)
);
//action types
const CHANGE_USER = 'CHANGE_USER';
const CHANGED_USER = 'CHANGED_USER';
const LOAD_USER = 'LOAD_USER';
const LOADED_USER = 'LOADED_USER';
//action creators
const loadUser = () => ({
type: LOAD_USER,
});
const loadedUser = (user) => ({
type: LOADED_USER,
payload: user,
});
const changeUser = () => ({
type: CHANGE_USER,
});
const changedUser = (user) => ({
type: CHANGED_USER,
payload: user,
});
// action thunks
const loadUserThunk = () => (dispatch) => {
dispatch(loadUser());
return later({
email: 'original-email',
}).then((user) => dispatch(loadedUser(user)));
};
const changeUserThunk = (email) => (dispatch) => {
dispatch(changeUser());
return later({
email,
}).then((user) => dispatch(changedUser(user)));
};
const reducer = (state, { type, payload }) => {
if (type === LOAD_USER || type === CHANGE_USER) {
return { ...state, loading: true };
}
if (type === LOADED_USER || type === CHANGED_USER) {
return {
...state,
user: payload,
loading: false,
};
}
return state;
};
//selectors
const selectUser = (state) => state.user;
const selectLoading = (state) => state.loading;
const selectUserEmail = createSelector(
[selectUser],
//want to use user?.email but SO babel is too old
(user) => user && user.email
);
//creating store with redux dev tools
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
reducer,
initialState,
composeEnhancers(
applyMiddleware(
({ getState, dispatch }) =>
(next) =>
(
action //simple thunk implementation
) =>
typeof action === 'function'
? action(dispatch, getState)
: next(action)
)
)
);
class App extends React.PureComponent {
state = {
email: '',
initial: true, //only initially set from redux
};
componentDidUpdate() {
if (!this.props.loading && this.state.initial) {
this.setState({
email: this.props.email,
initial: false,
});
}
}
//arrow function so you don't need to bind for "this" context
onSubmit = (e) => {
e.preventDefault();
this.props.dispatch(changeUserThunk(this.state.email));
//reset initial
this.setState({ initial: true });
};
emailChanged = (e) =>
this.setState({ email: e.target.value });
componentDidMount() {
this.props.dispatch(loadUserThunk());
}
render() {
return (
<form onSubmit={this.onSubmit}>
<input
type="text"
onChange={this.emailChanged}
value={this.state.email}
// do not edit when loading
disabled={this.props.loading}
/>
<input type="submit" />
{this.props.loading && 'loading...'}
</form>
);
}
}
//do not use store in your components, connect will provide
// dispatch on this.props when mapDispatchToProps is
// undefined or you can pass an object as mapDispatchToProps
const ConnectedApp = connect((state) => ({
email: selectUserEmail(state), //select state.user.email as props.email
loading: selectLoading(state),
}))(App);
ReactDOM.render(
<Provider store={store}>
<ConnectedApp />
</Provider>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reselect/4.0.0/reselect.min.js"></script>
<div id="root"></div>

Related

Multiple args using createAsyncThunk

I try to rewrite authentication tutorial from javascript into typescript. I encounter a problem as for createAsyncThunk arguments.
store.ts
import { configureStore } from "#reduxjs/toolkit";
import userReducer from './features/user';
export const store = configureStore({
reducer: {
user: userReducer,
},
devTools: true
})
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
user.ts
It seams there are some issue with arguments, any idea how to manage it using typescript?
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit'
import {RootState} from "../store"
import { API_URL } from "../config/index";
export interface UserState {
isAuthenticated: boolean,
user: null,
loading: boolean,
registered: boolean
}
const initialState: UserState = {
isAuthenticated: false,
user: null,
loading: false,
registered: false
}
export const register = createAsyncThunk('/api/user/create/', async ({name, email, password}, thunkAPI) => {
const body = JSON.stringify({name, email, password})
try {
const res = await fetch(`${API_URL}/api/user/create/`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body
})
// const data: UserState[] = await res.json();
const data = await res.json();
if (res.status === 201) {
return data;
} else {
return thunkAPI.rejectWithValue(data);
}
} catch(err: any) {
return thunkAPI.rejectWithValue(err.response.data);
}
});
const userSlice = createSlice({
name: 'user',
initialState,
reducers: {
resetRegistered(state) {
state.registered = false;
},
},
extraReducers: builder => {
builder
.addCase(register.pending, state => {
state.loading = true;
})
.addCase(register.fulfilled, state => {
state.loading = false;
state.registered = true;
})
.addCase(register.rejected, state => {
state.loading = false;
})
}
})
export const selectUser = (state: RootState) => state.user;
export const { resetRegistered } = userSlice.actions
export default userSlice.reducer
Register.tsx (here I get error when dispatch register() -> Expected 0 arguments, but got 1)
import { useState } from "react";
import Layout from '../components/Layout';
import { Navigate } from "react-router-dom";
import { MDBInput} from "mdb-react-ui-kit";;
import { useAppDispatch, useAppSelector } from '../hooks'
import { register, selectUser } from '../features/user';
const initialState = {
name: "",
email: "",
password: "",
confirmPassword: "",
}
const RegisterPage = () => {
const [formData, setFormData] = useState(initialState);
const {name, email, password, confirmPassword} = formData;
const [showRegister, setShowRegister] = useState(false);
const {registered, loading} = useAppSelector(selectUser);
const dispatch = useAppDispatch();
const handleChange = (e: any) => {
setFormData({ ...formData, [e.target.name]: e.target.value})
};
const onSubmit = (e: any) => {
e.preventDefault();
dispatch(register({name, email, password})); //Expected 0 arguments, but got 1
}
if (registered) {
return <Navigate to='/login'/>
}
return (
<Layout>
<form onSubmit={onSubmit} className="mt-5">
<div className="form-group">
<label htmlFor="name" className="form-label">
<MDBInput
type="text"
name="name"
value={name}
onChange={handleChange}
label="Name"
className="form-control form-control-lg"
/>
</label>
<label htmlFor="email" className="form-label">
<MDBInput
type="email"
name="email"
value={name}
onChange={handleChange}
label="Email"
className="form-control form-control-lg"
/>
</label>
<label htmlFor="password" className="form-label">
<MDBInput
type="password"
name="password"
value={name}
onChange={handleChange}
label="Password"
className="form-control form-control-lg"
/>
</label>
</div>
<button
className="btn btn-outline-light btn-lg px-5"
>
Register
</button>
</form>
</Layout>
)
}
export default RegisterPage;

update user password using react-router-dom v6

i want to implement update user password form using react-router-dom v6 but this code is not working..
please please.. put your suggestion or explain me about my mistakes on this code.
userReducer.js
import {
UPDATE_PASSWORD_REQUEST,
UPDATE_PASSWORD_SUCCESS,
UPDATE_PASSWORD_RESET,
UPDATE_PASSWORD_FAIL,
CLEAR_ERRORS,
} from "../Constants/userConstant";
export const profileReducer = (state = {}, action) => {
switch (action.type) {
case UPDATE_PASSWORD_REQUEST:
return {
...state,
loading: true,
};
case UPDATE_PASSWORD_SUCCESS:
return {
...state,
loading: false,
isUpdated: action.payload,
};
case UPDATE_PASSWORD_FAIL:
return {
...state,
loading: false,
error: action.payload,
};
case UPDATE_PASSWORD_RESET:
return {
...state,
isUpdated: false,
};
case CLEAR_ERRORS:
return {
...state,
error: null,
};
default:
return state;
}
};
userAction.js
import {
UPDATE_PASSWORD_REQUEST,
UPDATE_PASSWORD_SUCCESS,
UPDATE_PASSWORD_FAIL,
CLEAR_ERRORS,
} from "../Constants/userConstant";
export const updatePassword = (passwords) => async (dispatch) => {
try {
dispatch({ type: UPDATE_PASSWORD_REQUEST });
const config = { headers: { "Content-Type": "application/json" } };
const { data } = await axios.put(
`/api/v1/password/update`,
passwords,
config
);
dispatch({ type: UPDATE_PASSWORD_SUCCESS, payload: data.success });
} catch (error) {
dispatch({
type: UPDATE_PASSWORD_FAIL,
payload: error.response.data.message,
});
}
};
export const clearErrors = () => async (dispatch) => {
dispatch({ type: CLEAR_ERRORS });
};
store.js
import {createStore,combineReducers,applyMiddleware} from 'redux';
import thunk from "redux-thunk";
import {composeWithDevTools} from "redux-devtools-extension";
import { profileReducer } from './Reducers/userReducer';
const reducer = combineReducers({
profile:profileReducer,
})
let initialState = {};
const middleware = [thunk];
const store = createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
UpdatePassword.js
import React, { Fragment, useState, useEffect } from "react";
import "./UpdatePassword.css";
import Loader from "../Loader/Loader";
import { useDispatch, useSelector } from "react-redux";
import { clearErrors, updatePassword } from "../../Actions/userAction";
import { UPDATE_PASSWORD_RESET } from "../../Constants/userConstant";
import {useNavigate} from 'react-router-dom'
const UpdatePassword = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const { error, isUpdated, loading } = useSelector((state) => state.profile);
const [oldPassword, setOldPassword] = useState("");
const [newPassword, setNewPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const updatePasswordSubmit = (e) => {
e.preventDefault();
const myForm = new FormData();
myForm.set("oldPassword", oldPassword);
myForm.set("newPassword", newPassword);
myForm.set("confirmPassword", confirmPassword);
dispatch(updatePassword(myForm));
};
useEffect(() => {
if (error) {
alert(error);
dispatch(clearErrors());
}
if (isUpdated) {
alert("Profile Updated Successfully");
navigate("/account");
dispatch({
type: UPDATE_PASSWORD_RESET,
});
}
}, [dispatch, error, isUpdated]);
return (
<Fragment>
{loading ? (
<Loader />
) : (
<Fragment>
{/* <MetaData title="Change Password" /> */}
<div className="updatePasswordContainer">
<div className="updatePasswordBox">
<h2 className="updatePasswordHeading">Update Profile</h2>
<form
className="updatePasswordForm"
onSubmit={updatePasswordSubmit}
>
<div className="loginPassword">
<input
type="password"
placeholder="Old Password"
required
value={oldPassword}
onChange={(e) => setOldPassword(e.target.value)}
/>
</div>
<div className="loginPassword">
<input
type="password"
placeholder="New Password"
required
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
/>
</div>
<div className="loginPassword">
<input
type="password"
placeholder="Confirm Password"
required
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
</div>
<input
type="submit"
value="Change"
className="updatePasswordBtn"
/>
</form>
</div>
</div>
</Fragment>
)}
</Fragment>
);
};
export default UpdatePassword;
i want to make a form where user update user password.but due to any mistake this form is not working...

I cant get Redux action to work there is some error I cant spot please advice

I cant find what I do wrong but the action saveUser is imported and the mapDispatchToProps looks like it linking the actioncorrect but still I get this error
(Below after this code there is the action code)
The mapDispatchToProps is linking the action saveUser and if I CTRL-click it VSCode jump to the Action like this:
So why the error "TypeError: saveUser is not a function"
Here in the class LinkAccounts.jsx that has the mapDispatchToProps.
/* eslint-disable max-classes-per-file */
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { compose } from 'recompose';
import { AuthUserContext, withAuthorization } from '../../session';
import { withFirebase } from '../../firebase';
import { SIGN_IN_METHODS } from '../../constants/signinmethods';
import * as ROLES from '../../constants/roles';
import '../../styles/link-account.scss';
import asyncSaveUser from '../../redux/userData/user.actions';
class LoginManagementBase extends Component {
constructor() {
super();
this.state = {
activeSignInMethods: [],
anonymousSignIn: null,
error: null,
};
}
componentDidMount() {
this.fetchSignInMethods();
}
fetchSignInMethods = () => {
const { firebase, authUser } = this.props;
const email = authUser.email === null ? 'none#guest.ac' : authUser.email;
firebase.auth
.fetchSignInMethodsForEmail(email)
.then(activeSignInMethods =>
this.setState({
activeSignInMethods,
anonymousSignIn: activeSignInMethods.length === 0,
error: null,
}),
)
.catch(error => this.setState({ error }));
};
onSocialLoginLink = provider => {
const { firebase, saveUser } = this.props;
firebase.auth.currentUser
.linkWithPopup(firebase[provider])
// .linkWithRedirect(this.props.firebase[provider])
.then(saveUser())
.then(this.fetchSignInMethods)
.catch(error => this.setState({ error }));
};
onDefaultLoginLink = password => {
const { firebase, authUser } = this.props;
const credential = firebase.emailAuthProvider.credential(authUser.email, password);
firebase.auth.currentUser
.linkAndRetrieveDataWithCredential(credential)
.then(this.fetchSignInMethods)
.catch(error => this.setState({ error }));
};
onUnlink = providerId => {
const { firebase } = this.props;
firebase.auth.currentUser
.unlink(providerId)
.then(this.fetchSignInMethods)
.catch(error => this.setState({ error }));
};
render() {
const { activeSignInMethods, anonymousSignIn, error } = this.state;
console.log('Debug: ', anonymousSignIn, activeSignInMethods.length);
return (
<div className="provideToggler">
<h1>
You are signed in Anonymously changes you do is only saved in this browser. If you want to access your
progress anywhere please sign in below!
</h1>
<ul>
{SIGN_IN_METHODS.map(signInMethod => {
const onlyOneLeft = activeSignInMethods.length === 1;
const isEnabled = activeSignInMethods.includes(signInMethod.id);
return (
<li key={signInMethod.id}>
{signInMethod.id === 'password' ? (
<DefaultLoginToggle
// accountEmail={this.props.authUser.email}
onlyOneLeft={onlyOneLeft}
isEnabled={isEnabled}
signInMethod={signInMethod}
onLink={this.onDefaultLoginLink}
onUnlink={this.onUnlink}
/>
) : (
<SocialLoginToggle
onlyOneLeft={onlyOneLeft}
isEnabled={isEnabled}
signInMethod={signInMethod}
onLink={this.onSocialLoginLink}
onUnlink={this.onUnlink}
/>
)}
</li>
);
})}
</ul>
{error && error.message}
</div>
);
}
}
const SocialLoginToggle = ({ onlyOneLeft, isEnabled, signInMethod, onLink, onUnlink }) =>
isEnabled ? (
<button type="button" onClick={() => onUnlink(signInMethod.id)} disabled={onlyOneLeft}>
Unlink <i className={signInMethod.icon} aria-hidden="true" /> {signInMethod.name} sign in
</button>
) : (
<button type="button" onClick={() => onLink(signInMethod.provider)}>
Link <i className={signInMethod.icon} aria-hidden="true" /> {signInMethod.name} sign in
</button>
);
class DefaultLoginToggle extends Component {
constructor() {
super();
this.state = { passwordOne: '', passwordTwo: '' };
}
onSubmit = event => {
const { passwordOne } = this.state;
const { onLink } = this.props;
event.preventDefault();
onLink(passwordOne);
this.setState({ passwordOne: '', passwordTwo: '' });
};
onChange = event => {
this.setState({ [event.target.name]: event.target.value });
};
render() {
const { signInMethod } = this.props;
const { passwordOne, passwordTwo } = this.state;
const isInvalid = passwordOne !== passwordTwo || passwordOne === '';
return (
<form onSubmit={this.onSubmit}>
Link <i className={signInMethod.icon} aria-hidden="true" /> {signInMethod.name} sign in
<input
name="passwordOne"
value={passwordOne}
onChange={this.onChange}
type="password"
placeholder="Password for email sign in"
/>
<input
name="passwordTwo"
value={passwordTwo}
onChange={this.onChange}
type="password"
placeholder="Confirm New Password"
/>
<button disabled={isInvalid} type="submit">
Save password for email sign in
</button>
</form>
);
}
}
const LinkAccounts = () => (
<AuthUserContext.Consumer>
{authUser => (
<div>
<LoginManagement authUser={authUser} />
</div>
)}
</AuthUserContext.Consumer>
);
const mapDispatchToProps = dispatch => ({
saveUser: () => dispatch(asyncSaveUser()),
});
const LoginManagement = withFirebase(LoginManagementBase);
const condition = authUser => authUser && authUser.roles.includes(ROLES.ANON);
const enhance = compose(withAuthorization(condition), connect(null, mapDispatchToProps));
export default enhance(LinkAccounts);
This is the Action in file user.actions.js:
import { userActionTypes } from './user.types';
import { withFirebase } from '../../firebase';
import * as ROLES from '../../constants/roles';
const saveUserStart = () => ({
type: userActionTypes.SAVE_USER_START,
});
const saveUserSuccess = user => ({
type: userActionTypes.SAVE_USER_SUCCESS,
payload: user,
});
const saveUserFailure = errMsg => ({
type: userActionTypes.SAVE_USER_FAILURE,
payload: errMsg,
});
const asyncSaveUser = ({ firestore }) => {
return async dispatch => {
const userRef = firestore.userDoc(firestore.auth.currentUser.uid);
dispatch(saveUserStart());
firestore.db
.runTransaction(transaction => {
// This code may get re-run multiple times if there are conflicts.
return transaction.get(userRef).then(doc => {
if (!doc.exists) {
return Promise.reject('Transaction failed: User dont exist!');
}
const newRoles = doc.data().roles;
// new roll
newRoles.push(ROLES.USER);
// remove roll
newRoles.splice(newRoles.indexOf('ANONYMOUS'), 1);
// save it back
transaction.update(userRef, { roles: newRoles });
return newRoles;
});
})
.then(newRoles => {
dispatch(saveUserSuccess());
console.log(`Transaction successfully committed role(s): ${newRoles}`);
})
.catch(error => {
dispatch(saveUserFailure(error));
console.log(error);
});
};
};
export default withFirebase(asyncSaveUser);
saveUser is not passed as a prop in LoginManagementBase component.
It is only passed as prop via enhance for LinkAccounts.
I think you want to compose for LoginManagement instead.
const enhance = compose(
withFirebase,
connect(null, mapDispatchToProps)
);
const LoginManagement = enhance(LoginManagementBase);

Why is this.props.username undefined in React-Redux?

When I try to access this.props.username in:
loginClick = (event) => {
event.preventDefault();
console.log('Login submit click, props.username: ' + this.props.username);
this.props.loginDispatch(this.props.username);
}
I get undefined.
Can you tell me what I'm missing?
Reducer:
import { fromJS } from 'immutable';
import { DEFAULT_ACTION, SET_USERNAME } from './constants';
const initialStateMutable = {
username: ''
};
const initialState = fromJS(initialStateMutable);
function loginReducer(state = initialState, action) {
switch (action.type) {
case DEFAULT_ACTION:
return state;
case SET_USERNAME:
console.log('reducer state.username value: ' + state.username);
return {
...state,
username: action.username
};
default:
return state;
}
}
Action:
import { LOGIN_SUBMIT, SET_USERNAME } from './constants';
export const loginDispatch = (name) => ({
type: LOGIN_SUBMIT,
name,
});
export const setUsername = (username) => {
return {
type: SET_USERNAME,
username,
};
};
Selector:
import { createSelector } from 'reselect';
const selectLoginPageDomain = () => (state) => state.get('loginPage');
const makeSelectLoginPage = () => createSelector(
selectLoginPageDomain(),
(substate) => substate.toJS()
);
export default makeSelectLoginPage;
export {
selectLoginPageDomain,
};
index.tsx:
import makeSelectLoginPage from './selector';
import * as React from 'react';
import { loginDispatch, setUsername } from './actions';
import { connect } from 'react-redux';
interface ILoginProps {
loginDispatch: Function;
setUsername: Function;
username: string;
}
interface ILoginState {
}
class LoginPage extends React.Component<ILoginProps, ILoginState> {
constructor(props, context) {
super(props, context);
}
updateInputValue = (event) => {
event.preventDefault();
console.log('Current value of props.username: ' + this.props.username);
this.props.setUsername(event.target.value);
}
loginClick = (event) => {
event.preventDefault();
console.log('Login submit click, props.username: ' + this.props.username);
this.props.loginDispatch(this.props.username);
}
render() {
return (
<div>
<div className="row">
<div className="col-xs-12">
<div className="card">
<div className="card-inside">
<div className="alignc">
<form onSubmit={this.loginClick} className="row">
<div>
<div className="alignl">
Enter username for calculation history.
</div>
<div>
<input type="text" value={this.props.username} onChange={this.updateInputValue}/>
</div>
<button type="submit" value="Submit">Submit</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => ({
...state,
username: state.username,
...makeSelectLoginPage()(state),
});
const mapDispatchToProps = (dispatch) => ({
dispatch,
setUsername: (value: string) => dispatch(setUsername(value)),
loginDispatch: (name: string) => dispatch(loginDispatch(name)),
});
export default connect(mapStateToProps, mapDispatchToProps)(LoginPage);
UPDATE:
I just realized I'm getting this error from the selector:
Uncaught TypeError: substate.toJS is not a function
at eval (eval at ./app/containers/Login/selector.ts
state is an immutable variable. Try this :
const mapStateToProps = (state) => ({
...state.toJS(),
username: state.get('username'),
...makeSelectLoginPage()(state),
});

Why is the state change not making it's way to the reducer?

I have a file called Login.js in the file I call this.props.onUserLogin and pass the users auth token. I can verify that I am getting the token from the api call via the console log. When I get to the reducer I loose the value from the state somehow. I can verify that with the console log as well. I am trying to figure out why my state.loginToken is empty in the reducer.
Login.js
import React, {Component} from 'react';
import * as actionTypes from '../Store/actions';
import UserInfoButton from '../UserInfoButton/UserInfoButton';
import { connect } from 'react-redux';
const axios = require('axios');
class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
};
}
handleChange = (event) => {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
})
}
handleLogin = (event) => {
axios.post('http://127.0.0.1:8000/api/user/token/', {
email: this.state.email,
password: this.state.password,
}).then( (response) => {
console.log('response: ' + response.data.token);
this.props.onUserLogin(response.data.token);
})
.catch( (error) => {
console.log(error)
})
event.preventDefault();
}
render() {
const isLoggedIn = this.props.loginToken;
let userButton;
if(isLoggedIn) {
userButton = <UserInfoButton/>
} else {
userButton = "";
}
return (
<div>
<form onSubmit={this.handleLogin}>
Email:<br/>
<input type="text" value={this.state.email} onChange={this.handleChange} name="email"/><br/>
Password:<br/>
<input type="password" value={this.state.password} onChange={this.handleChange} name="password"/><br/>
<br/>
<input type="submit" value="Submit"></input>
</form>
<div>{this.props.loginToken}</div>
<br/>
<div>{userButton}</div>
</div>
)
}
}
const mapStateToProps = state => {
return {
loginToken: state.loginToken
};
}
const mapDispatchToProps = dispatch => {
return {
onUserLogin: (userToken) => dispatch( { type: actionTypes.USER_LOGIN_ACTION, loginToken: userToken } ),
};
}
export default connect(mapStateToProps,mapDispatchToProps)(Login);
reducer.js
import * as actionTypes from './actions';
const initialState = {
loginToken: '',
}
const reducer = (state = initialState, action) => {
switch(action.type) {
case actionTypes.USER_LOGIN_ACTION:
console.log('action: ' + state.loginToken);
return {
...state,
loginToken: state.loginToken,
}
default:
return state;
}
};
export default reducer;
Basically a typo inside of your reducer - You're looking for the loginToken inside of state instead of the action.
case actionTypes.USER_LOGIN_ACTION:
console.log('action: ' + action.loginToken); // not state.loginToken
return {
...state,
loginToken: action.loginToken, // not state.loginToken
}

Resources