Changing boolean value with React Bootstrap checkbox and hooks - javascript

Trying to change the isVegan object (nested boolean) with React Bootstrap checkbox and hooks. I can access the object without any issues (e.g. checkbox is checked if isVegan is true), but have been unable to modify the state. As you can see in the Redux dev tools (image link included), the isVegan object is passed through my state and is accessible. I have also used similar code for the other objects in the chef collection without any issues so believe the issue is either related to the checkbox or how the isVegan object is nested in the chef collection. (Lastly, I know some of the code below may be extra, I slimmed down my original file to simplify this example)
import React, { useState, useEffect, setState } from 'react';
import { Form, Button, Row, Col, Tabs, Tab } from 'react-bootstrap';
import { LinkContainer } from 'react-router-bootstrap';
import { useDispatch, useSelector } from 'react-redux';
import { getChefDetails, updateChefProfile } from '../../actions/chefActions';
import { CHEF_UPDATE_PROFILE_RESET } from '../../constants/chefConstants';
import FormContainer from '../../components/FormContainer/FormContainer.component';
import './ProfileEditPage.styles.scss';
const ProfileEditPage = ({ location, history }) => {
const [first_name, setFirstName] = useState('')
const [last_name, setLastName] = useState('')
const [username, setUsername] = useState('')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [confirmPassword, setConfirmPassword] = useState('')
const [isVegan, setIsVegan] = useState('')
const [bio, setBio] = useState('')
const [message, setMessage] = useState(null)
const dispatch = useDispatch()
const chefDetails = useSelector(state => state.chefDetails)
const { loading, error, chef } = chefDetails
const chefLogin = useSelector(state => state.chefLogin)
const { chefInfo } = chefLogin
const chefUpdateProfile = useSelector(state => state.chefUpdateProfile)
const { success } = chefUpdateProfile
useEffect(() => {
if(!chefInfo) {
history.push('/login')
} else {
if(!chef || !chef.username || success) {
dispatch({ type: CHEF_UPDATE_PROFILE_RESET })
dispatch(getChefDetails('profile'))
} else {
setFirstName(chef.first_name)
setLastName(chef.last_name)
setUsername(chef.username)
setEmail(chef.email)
setBio(chef.bio)
setIsVegan(chef.isVegan)
}
}
}, [dispatch, history, chefInfo, chef, success])
const submitHandler = (e) => {
e.preventDefault()
if (password !== confirmPassword) {
setMessage('Passwords do not match')
} else {
dispatch(updateChefProfile({
id: chef._id,
first_name,
last_name,
username,
email,
password,
bio,
isVegan
}))
}
}
const [key, setKey] = useState('auth')
//const isVegan = chef.diets[0].isVegan
//const isVegetarian = chef.diets[0].isVegetarian
console.log(isVegan)
return (
<FormContainer className="profileEditPage">
<h1>Chef Profile</h1>
<Form className='profileEditPageForm' onSubmit={submitHandler}>
<Tabs id="profileEditPageTabs" activeKey={key} onSelect={(k) => setKey(k)}>
<Tab eventKey='auth' title="Auth">
<Form.Group controlId='first_name'>
<Form.Label>First Name</Form.Label>
<Form.Control
type='text'
placeholder='Enter your first name'
value={first_name}
onChange={(e) => setFirstName(e.target.value)}
required
>
</Form.Control>
</Form.Group>
<Form.Group controlId='last_name'>
<Form.Label>Last Name</Form.Label>
<Form.Control
type='text'
placeholder='Enter your last name'
value={last_name}
onChange={(e) => setLastName(e.target.value)}
required
>
</Form.Control>
</Form.Group>
<Form.Group controlId='username'>
<Form.Label>Username</Form.Label>
<Form.Control
type='text'
placeholder='Enter a username'
value={username}
onChange={(e) => setUsername(e.target.value)}
required
>
</Form.Control>
<Form.Text className='muted'>Your username will be public</Form.Text>
</Form.Group>
<Form.Group controlId='email'>
<Form.Label>Email</Form.Label>
<Form.Control
type='email'
placeholder='Enter your email'
value={email}
onChange={(e) => setEmail(e.target.value)}
required
>
</Form.Control>
</Form.Group>
<Form.Group controlId='password'>
<Form.Label>Password</Form.Label>
<Form.Control
type='password'
placeholder='Enter your password'
value={password}
onChange={(e) => setPassword(e.target.value)}
>
</Form.Control>
</Form.Group>
<Form.Group controlId='confirmPassword'>
<Form.Label>Confirm Password</Form.Label>
<Form.Control
type='password'
placeholder='Confirm password'
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
>
</Form.Control>
</Form.Group>
</Tab>
<Tab eventKey='chef-detail' title="Chef Detail">
<Form.Group controlId='isVegan'>
<Form.Check
type='checkbox'
label='Vegan?'
checked={isVegan}
value={isVegan}
onChange={(e) => setIsVegan(e.target.checked)}
/>
</Form.Group>
<Form.Group controlId='bio'>
<Form.Label>Chef Bio</Form.Label>
<Form.Control
as='textarea'
rows='5'
maxLength='240'
placeholder='Enter bio'
value={bio}
onChange={(e) => setBio(e.target.value)}
>
</Form.Control>
<Form.Text className='muted'>Your bio will be public</Form.Text>
</Form.Group>
</Tab>
</Tabs>
<Button type='submit' variant='primary'>
Update
</Button>
</Form>
</FormContainer>
)
}
export default ProfileEditPage;
Actions
export const getChefDetails = (id) => async (dispatch, getState) => {
try {
dispatch({
type: CHEF_DETAILS_REQUEST
})
const { chefLogin: { chefInfo} } = getState()
const config = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${chefInfo.token}`
}
}
const { data } = await axios.get(
`/api/chefs/${id}`,
config
)
dispatch({
type: CHEF_DETAILS_SUCCESS,
payload: data
})
} catch (error) {
dispatch({
type: CHEF_DETAILS_FAILURE,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
})
}
}
export const updateChefProfile = (chef) => async (dispatch, getState) => {
try {
dispatch({
type: CHEF_UPDATE_PROFILE_REQUEST
})
const { chefLogin: { chefInfo } } = getState()
const config = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${chefInfo.token}`
}
}
const { data } = await axios.put(
`/api/chefs/profile`,
chef,
config
)
dispatch({
type: CHEF_UPDATE_PROFILE_SUCCESS,
payload: data
})
dispatch({
type: CHEF_LOGIN_SUCCESS,
payload: data
})
localStorage.setItem('chefInfo', JSON.stringify(data))
} catch (error) {
dispatch({
type: CHEF_UPDATE_PROFILE_FAILURE,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
})
}
}
Reducers
export const chefDetailsReducer = (state = { chef: { } }, action) => {
switch(action.type) {
case CHEF_DETAILS_REQUEST:
return { ...state, loading: true }
case CHEF_DETAILS_SUCCESS:
return { loading: false, chef: action.payload }
case CHEF_DETAILS_FAILURE:
return { loading: false, error: action.payload }
case CHEF_DETAILS_RESET:
return {
chef: {}
}
default:
return state
}
}
export const chefUpdateProfileReducer = (state = { }, action) => {
switch(action.type) {
case CHEF_UPDATE_PROFILE_REQUEST:
return { loading: true }
case CHEF_UPDATE_PROFILE_SUCCESS:
return { loading: false, success: true, chefInfo: action.payload }
case CHEF_UPDATE_PROFILE_FAILURE:
return { loading: false, error: action.payload }
case CHEF_UPDATE_PROFILE_RESET:
return { }
default:
return state
}
}
Controller
// #description Get chef profile
// #route GET /api/chefs/profile
// #access Private
const getChefProfile = asyncHandler(async (req, res) => {
const chef = await Chef.findById(req.chef._id)
if(chef) {
res.json({
_id: chef._id,
first_name: chef.first_name,
last_name: chef.last_name,
username: chef.username,
email: chef.email,
bio: chef.bio,
isVegan: chef.isVegan
})
} else {
res.status(404)
throw new Error('Chef not found')
}
})
// #description Update chef profile
// #route PUT /api/chefs/profile
// #access Private
const updateChefProfile = asyncHandler(async (req, res) => {
const chef = await Chef.findById(req.chef._id)
if(chef) {
chef.first_name = req.body.first_name || chef.first_name
chef.last_name = req.body.last_name || chef.last_name
chef.username = req.body.username || chef.username
chef.email = req.body.email || chef.email
chef.bio = req.body.bio || chef.bio
chef.isVegan = req.body.isVegan || chef.isVegan
if (req.body.password) {
chef.password = req.body.password
}
const updatedChef = await chef.save()
res.json({
_id: updatedChef._id,
first_name: updatedChef.first_name,
last_name: updatedChef.last_name,
username: updatedChef.username,
email: updatedChef.email,
bio: updatedChef.bio,
isVegan: updatedChef.isVegan,
token: generateToken(updatedChef._id),
})
} else {
res.status(404)
throw new Error('Chef not found')
}
})

Issue
After much back and forth I believe the issue is with how the response "payload" is stored back in state by the reducer. The response object is a flat object with isVegan at the root, but in state isVegan is in a nested diets array.
res.json({
_id: updatedChef._id,
first_name: updatedChef.first_name,
last_name: updatedChef.last_name,
username: updatedChef.username,
email: updatedChef.email,
bio: updatedChef.bio,
isVegan: updatedChef.isVegan,
token: generateToken(updatedChef._id),
})
The reducer takes the payload and also saves it directly to a chefInfo property and overwriting any existing data.
export const chefUpdateProfileReducer = (state = { }, action) => {
switch(action.type) {
...
case CHEF_UPDATE_PROFILE_SUCCESS:
return { loading: false, success: true, chefInfo: action.payload }
...
}
}
Solution
Reducer should merge in response payload. In your redux screenshot I don't see a chefInfo key so I'll write this to match the screenshot as closely as possible.
export const chefUpdateProfileReducer = (state = { }, action) => {
switch(action.type) {
...
case CHEF_UPDATE_PROFILE_SUCCESS:
const {
_id,
isVegan,
token,
...chefDetails // i.e. first & last name, username, email, bio
} = action.payload;
return {
...state, // <-- shallow copy state
loading: false,
success: true,
chef: {
...state.chef, // <-- shallow copy existing chef details
...chefDetails, // shallow copy new chef details
diets: state.chef.diets.map(diet => diet._id === _id ? { // <-- map existing state
...diet, // <-- shallow copy diet object
isVegan // <-- overwrite isVegan property
} : diet),
},
};
...
}
}
Note: This is a best guess to state structures and types since your reducers appear to have a very minimally defined initial state, so this likely needs to be tweaked to fits your exact state structure.

Related

handleRegister not returning value, undefined

I'm trying to return a value from my handleRegister function, but it gives me undefined, I checked before returning the value and it exists but as soon as I return, it's undefined. Can someone please help me? The object exists:
{
"signup": {
"name": "riced",
"id": 7135,
"email": "riced#gmail.com",
"password": "U24jg2xwSbF4R6k",
"error": null,
"__typename": "User"
}
}
The following part fails:
handleRegister(this.state).then(
(data) => {
console.log(data);
return;
});
import * as React from "react"
import { navigate } from "gatsby"
import {isLoggedIn } from "../services/auth"
import fetch from 'isomorphic-fetch';
import {ApolloClient, HttpLink, InMemoryCache, gql} from '#apollo/client';
const client = new ApolloClient({
cache: new InMemoryCache(),
link: new HttpLink({
uri: 'http://10.0.0.212:9000/',
fetch
})
});
export const REGISTER_USER = gql
mutation Mutation($signupInput: SignUpInput) {
signup(signupInput: $signupInput) {
name
id
email
password
error
}
}
;
export async function handleRegister ({ username, email, password }) {
/*
if (username === `john` && password === `pass`) {
return setUser({
username: `john`,
name: `Johnny`,
email: `johnny#example.org`,
})
*/
/*
client.query({ query: ALL_USERS }).then(result => console.log(result));
*/
//let [errors, setErrors] = React.useState([]);
var gottenToken = "";
//let gottenName;
await client.mutate({ mutation: REGISTER_USER, variables: {
"signupInput": {
"name": username,
"password": password,
"email": email,
}
} }).then(result => {
console.log(result);
console.log(result.data!.signup);
//let signup = JSON.stringify(result.data!.signup);
return result.data!.signup
//Promise.resolve(result.data!.signup);
}).catch(err => {
console.log(err);
//setUser({})
//React.useEffect(() => {
//localStorage.setItem('signupError', JSON.stringify(err));
//}, [errors]);
//return haveErrorOccured("Signing up error"); // send error to browser
});
//return haveErrorOccured("Signing up error"); // send error to browser
}
class Register extends React.Component {
state = {
username: ``,
email: ``,
password: ``,
}
handleUpdate = async event => {
await this.setState({
[event.target.name]: event.target.value,
})
}
handleSubmit = async event => {
event.preventDefault()
//handleRegister(this.state)
/*
handleRegister(this.state).then(r => {
console.log(r);
}).catch(err => {
console.log(err);
});
*/
handleRegister(this.state).then(
(data) =>{
console.log(data);
return;
//return 43;
});
//console.log("signed up: " + signup);
//console.table(signup);
}
render() {
if (isLoggedIn()) {
navigate(`/app/profile`)
}
//let errorSignup;
//if(window.localStorage.getItem("signupError")){
// errorSignup = JSON.stringify(JSON.parse(window.localStorage.getItem("signupError")));
//}
//navigate("/app/register?registered", { state: { foo: "bar" }});
//<p>{errorSignup}</p>
return (
<>
<h1>Register</h1>
<form
method="post"
onSubmit={event => {
this.handleSubmit(event);
}}
>
<label>
Username
<input type="text" name="username" onChange={this.handleUpdate} />
</label>
<label>
Email
<input type="text" name="email" onChange={this.handleUpdate} />
</label>
<label>
Password
<input
type="password"
name="password"
onChange={this.handleUpdate}
/>
</label>
<input type="submit" value="Log In" />
</form>
</>
)
}
}
export default Register

console error :Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'data') at handleClick

In this page the user can login, but if the untilDate is bigger than the current date it should log out the user. The code runs fine 1/2 times, the other giving me the error on the title.
I am working with createContext for user login. This is the AuthContext file
import React from "react";
import { createContext, useEffect, useReducer } from "react";
const INITIAL_STATE = {
user: JSON.parse(localStorage.getItem("user")) || null,
loading: false,
error: null,
};
export const AuthContext = createContext(INITIAL_STATE);
const AuthReducer = (state, action) => {
switch (action.type) {
case "LOGIN_START":
return {
user: null,
loading: true,
error: null,
};
case "LOGIN_SUCCESS":
return {
user: action.payload,
loading: false,
error: null,
};
case "LOGOUT":
return {
user: null,
loading: false,
error: null,
};
case "LOGIN_FAILURE":
return {
user: null,
loading: false,
error: action.payload,
};
case "UPDATE_USER_DATE":
const updatedUser = { ...state.user };
updatedUser.activeUntil = action.payload;
return {
...state,
user: updatedUser,
};
default:
return state;
}
};
export const AuthContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(AuthReducer, INITIAL_STATE);
useEffect(() => {
localStorage.setItem("user", JSON.stringify(state.user));
}, [state.user]);
return (
<AuthContext.Provider
value={{
user: state.user,
loading: state.loading,
error: state.error,
dispatch,
}}
>
{children}
</AuthContext.Provider>
);
};
When the user clicks the login button, it runs the handleClick function:
const handleClick = async (e) => {
e.preventDefault();
dispatch({ type: "LOGIN_START" });
let date = new Date().toJSON();
let userdate = date;
try {
const res = await axios.post("/auth/signin", credentials);
dispatch({ type: "LOGIN_SUCCESS", payload: res.data.details });
userdate = user.activeUntil;
//do if date is <=current datem dispatch logout
} catch (err) {
if (userdate > date) {
console.log("undefined data");
} else {
dispatch({ type: "LOGIN_FAILURE", payload: err.response.data });
}
}
if (userdate > date) {
dispatch({ type: "LOGOUT" });
console.log("If you are seeing this your contract has expired");
} else {
// navigate("/myinfo");
}
};
The console error happens from this line dispatch({ type: "LOGIN_FAILURE", payload: err.response.data });
Is there a way I can bypass this error or a different way I can write my code to make it work?
This is the full code of login page
import React from "react";
import axios from "axios";
import { useContext, useState } from "react";
import { useNavigate } from "react-router-dom";
import { AuthContext } from "../../context/AuthContext";
import {
Container,
FormWrap,
FormContent,
Form,
FormInput,
FormButton,
Icon,
FormH1,
SpanText,
IconWrapper,
IconL,
} from "./signinElements";
import Image from "../../images/Cover.png";
const Login = () => {
const [credentials, setCredentials] = useState({
namekey: undefined,
password: undefined,
});
/* */
// to view current user in console
const { user, loading, error, dispatch } = useContext(AuthContext);
let msg;
const navigate = useNavigate();
const handleChange = (e) => {
setCredentials((prev) => ({ ...prev, [e.target.id]: e.target.value }));
};
const handleClick = async (e) => {
e.preventDefault();
dispatch({ type: "LOGIN_START" });
let date = new Date().toJSON();
let userdate = date;
try {
const res = await axios.post("/auth/signin", credentials);
dispatch({ type: "LOGIN_SUCCESS", payload: res.data.details });
userdate = user.activeUntil;
//do if date is <=current datem dispatch logout
} catch (err) {
if (userdate > date) {
console.log("undefined data");
} else {
dispatch({ type: "LOGIN_FAILURE", payload: err.response.data });
}
}
if (userdate > date) {
dispatch({ type: "LOGOUT" });
console.log("If you are seeing this your contract has expired");
} else {
// navigate("/myinfo");
}
};
// console.log(user.activeUntil); //type to view current user in console
return (
<>
<Container>
<IconWrapper>
<IconL to="/">
<Icon src={Image}></Icon>
</IconL>
</IconWrapper>
<FormWrap>
<FormContent>
<Form action="#">
<FormH1>
Sign in with the namekey and password written to you on your
contract.
</FormH1>
<FormInput
type="namekey"
placeholder="Namekey"
id="namekey"
onChange={handleChange}
required
/>
<FormInput
type="password"
placeholder="Password"
id="password"
onChange={handleChange}
/>
<FormButton disabled={loading} onClick={handleClick}>
Login
</FormButton>
<SpanText>{msg}</SpanText>
{error && <SpanText>{error.message}</SpanText>}
{error && (
<SpanText>
Forgot namekey or password? Contact our support team +355 69
321 5237
</SpanText>
)}
</Form>
</FormContent>
</FormWrap>
</Container>
</>
);
};
export default Login;
The problem was i was trying to call a localy stored user and 1 time it wasnt loaded and the other it was. Simply fixed it by changing the if statement to check directly in result details without having to look in local storage.
const [expired, setExpired] = useState(false);
const handleClick = async (e) => {
e.preventDefault();
dispatch({ type: "LOGIN_START" });
try {
const res = await axios.post("/auth/signin", credentials);
dispatch({ type: "LOGIN_SUCCESS", payload: res.data.details });
let date = new Date().toJSON();
if (res.data.details.activeUntil < date) {
dispatch({ type: "LOGOUT" });
console.log("Users contract has expired");
setExpired(!expired);
} else {
navigate("/myinfo");
}
} catch (err) {
dispatch({ type: "LOGIN_FAILURE", payload: err.response.data });
}
};

How can I check the return value of dispatch in redux hook

I am trying to make user login page with react and redux.
//loginAction.js
export function loginRequest(user){
return{
type: types.LOGIN_REQUEST,
user
}
}
export function loginSuccess(user){
return{
type: types.LOGIN_SUCCESS,
user
}
}
export function loginFailure(err){
return{
type: types.LOGIN_FAILURE,
err
}
}
export function login(username, password){
return function(dispatch) {
dispatch(loginRequest({ username }));
userApi.userLogin(username, password)
.then(
user => {
dispatch(loginSuccess(user));
},
err => {
dispatch(loginFailure(err.toString()))
}
)
}
}
//userReducer.js
const login = (state=initialState, action) => {
switch(action.types){
case types.LOGIN_SUCCESS:
case types.LOGIN_REQUEST:
return {
loggedIn: true,
user: action.user
}
case types.LOGIN_FAILURE:
return {}
default:
return state
}
};
LoginPage.js
import React, { useState, useEffect} from 'react';
import PropTypes from 'prop-types';
import {useDispatch, useSelector} from 'react-redux';
import {Redirect} from 'react-router-dom';
import {login} from '../redux/actions/loginAction'
const LoginPage = () => {
const [inputs, setInputs] = useState({
username: '',
password: ''
})
const [isauthenticated, setIsAuthenticated] = useState(false);
const { username, password } = inputs
const dispatch = useDispatch();
const loggingIn = useSelector(state => state.user);
const handleChange = (e) => {
const {name, value} = e.target;
setInputs(inputs =>({...inputs, [name]: value}))
}
const handleSubmit = (event) => {
event.preventDefault();
// setIsAuthenticated(true)
// this line needs to be improved
if( username && password ){
dispatch(login(username, password))
}
}
return(
<div className='field col-xs-5 col-lg-3'>
{
isAuthenticated ?
<Redirect to='/uploadfile' /> :
(
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={handleChange}
name="username"
label="username"
className="form-control"
placeholder="ID"
value={username}
/>
<input
type="password"
onChange={handleChange}
name="password"
label="password"
className="form-control"
placeholder="Password"
/>
<button type="submit" className="btn btn-primary">
Submit
</button>
</form>
)
}
</div>
)
}
export default LoginPage;
Inside of LoginPage component, I have isAuthenticated state that I want to set true when its action returns 'LOGIN_SUCCESS', not always set true.
Is there a way to check it, like this?
const handleSubmit = (event) => {
event.preventDefault();
// setIsAuthenticated(true)
if( username && password ){
// can I do?
if(dispatch(login(username, password)) === 'types.LOGIN_SUCCESS'){
setIsAuthticated(true)
}
}
}
const login = (state=initialState, action) => {
switch(action.types){
case types.LOGIN_REQUEST:
return {
loggedIn: false,
user: null
}
case types.LOGIN_SUCCESS:
return {
loggedIn: true,
user: action.user
}
case types.LOGIN_FAILURE:
return {
loggedIn: false,
user: null
}
default:
return state
}
};
I would write it like that, you're not logged in before login_success. And inside loginform you should only check props - loggedIn and user.

Change password reducer in react app(axios + redux + jwt + bcrypt)

I want to change password, while I'm logged in.
Here's my function:
authActions.js(without catch because that will be implemented if anything start to works)
// Change password
export const changePassword = (newPassword) => (dispatch, getState) => {
// Headers
const config = {
headers: {
'Content-Type': 'application/json'
}
}
axios.post(`/api/auth/user/changePassword/`, newPassword, tokenConfig(getState))
.then(res => dispatch({
type: CHANGE_PASSWORD,
payload: res.data
}))
}
// Setup config/headers and token
export const tokenConfig = getState => {
// Get token from localstorage
const token = getState().auth.token;
// Headers
const config = {
headers: {
// "Accept": "application/json, multipart/form-data"
"Content-type": "application/json"
}
}
// If token, add to headers
if (token) {
config.headers['x-auth-token'] = token;
}
return config;
}
and authReducer.js:
...
const initialState = {
token: localStorage.getItem('token'),
isAuthenticated: false,
user: null
};
export default function (state = initialState, action) {
switch (action.type) {
...
case CHANGE_PASSWORD:
return {
...state,
token: null,
user: action.payload,
isAuthenticated: false,
isLoading: false
};
default:
return state;
}
}
and routes/api/auth.js
router.post('/user/changePassword/', (req, res) => {
console.log(req.body);
const { email, oldPassword, newPassword } = req.body
// find if old password is valid
User.findOne({ email })
.then(user => {
bcrypt.compare(oldPassword, user.password)
.then(isMatch => {
if (isMatch) {
// change to new password
user.password = newPassword
user
.save()
.then(newUser => {
res.status(200).send(newUser)
})
.catch(err => {
const message = err.message
res.status(500).json({
status: "change password failed",
msg: message
})
})
} else {
return res.status(401).send("Invalid old password")
}
})
})
.catch(err => {
res.status(500).send(err)
})
});
I have console.log(req.body); in routes just to check if anything works, but it don't works(didn't give me any message).
And component in the end(but it's not the source of problem):
import React, { useState, useEffect } from 'react';
import {
Button,
Modal,
ModalHeader,
ModalBody,
Form,
FormGroup,
Label,
Input,
NavLink
} from 'reactstrap';
import { connect } from 'react-redux';
import { changePassword } from '../../actions/authActions';
import PropTypes from 'prop-types';
const ChangePassword = ({ auth }) => {
const [modal, setModal] = useState(false);
const [enterPassword, setEnterPassword] = useState({
oldPassword: '',
newPassword: ''
});
const [takeEmail, setTakeEmail] = useState(null);
useEffect(() => {
const createArray = () => {
const { user } = auth;
setTakeEmail({ email: user.email });
};
createArray();
}, [auth.user]);
const toggle = () => {
setModal(!modal);
};
const onChange = e => {
setEnterPassword({
...enterPassword,
[e.target.name]: e.target.value
});
};
const onSubmit = (event) => {
event.preventDefault();
const { email } = takeEmail;
const { oldPassword, newPassword } = enterPassword;
console.log(enterPassword);
console.log(takeEmail);
const newUser = {
email,
oldPassword,
newPassword
}
// Add content via changePassword action
changePassword(newUser);
toggle();
}
return (
<div>
<NavLink onClick={toggle} href="#">
Change Password
</NavLink>
<Modal
isOpen={modal}
toggle={toggle}
className="open-modal"
>
<ModalHeader toggle={toggle}>Dodaj do listy ogłoszeń</ModalHeader>
<ModalBody>
<Form onSubmit={onSubmit}>
<FormGroup>
<Label for="oldPassword">Nagłówek</Label>
<Input
type="password"
name="oldPassword"
id="oldPassword"
placeholder="Wprowadź stare hasło..."
onChange={onChange}
/>
<Label for="newPassword">Nagłówek</Label>
<Input
type="password"
name="newPassword"
id="newPassword"
placeholder="Wprowadź stare hasło..."
onChange={onChange}
/>
<Button
color="dark"
style={{ marginTop: '2rem' }}
block>
Zmień hasło
</Button>
</FormGroup>
</Form>
</ModalBody>
</Modal>
</div>
);
}
ChangePassword.propTypes = {
isAuthenticated: PropTypes.bool,
changePassword: PropTypes.func.isRequired
}
const mapStateToProps = state => ({
auth: state.auth,
isAuthenticated: state.auth.isAuthenticated
});
export default connect(mapStateToProps, { changePassword })(ChangePassword);

React Reducer is not updating State

I was driven crazy by my first react-redux app. My Redux State is never updated.
I tried to find every solution on the website but they are not helping. One very similar question here used promise but I am not.
Redux-dev-tools catch the actions about the login_success but the global state is never updated, please tell me what should I do to debug if you could, thank you so much.
First Index.js
import ... from ...
const compostEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const rootReducer = combineReducers({
auth: authReducer,
})
const store = createStore(rootReducer, compostEnhancer(
applyMiddleware(thunk)
));
const app = (<BrowserRouter><App /></BrowserRouter>)
ReactDOM.render(<Provider store={store}>{app}</Provider>, document.getElementById('root'));
registerServiceWorker();
authReducer.js :
import * as actionTypes from '../actions/actionTypes';
const initialState = {
token: null,
userId: null,
error: null,
loading: false
}
const authReducer = (state = initialState, action) => {
switch (action.types) {
case actionTypes.LOGIN_START:
return {
...state,
};
case actionTypes.LOGIN_SUCCESS:
return {
...state,
token: action.idToken,
userId: action.userId,
loading: false,
error:null
}
default:
return state;
}
}
export default authReducer;
authAction.js:
import * as actionTypes from './actionTypes';
import axios from 'axios';
export const loginStart= () =>{
return {
type: actionTypes.LOGIN_START
};
}
export const loginSuccess = (token,userId) =>{
return {
type: actionTypes.LOGIN_SUCCESS,
userId: userId,
idtoken: token,
}
}
export const loginFail = (error) =>{
return {
type:actionTypes.LOGIN_FAIL,
error:error
}
}
export const auth = (email,password,isSignup ) =>{
return dispatch =>{
dispatch(loginStart());
const authData = {
email: email,
password: password,
returnSecureToken:true
}
let url = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=...'
if(!isSignup ){
url = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=...'
}
axios.post(url, authData)
.then( response =>{
console.log(response);
dispatch(loginSuccess(response.data.idToken, response.data.localId))
})
.catch(err=>{
console.log(err);
dispatch(loginFail(err));
})
}
}
Login.js (Component):
import ... from ...;
class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: "",
isSignup: false,
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit = e => {
e.preventDefault();
// This will trigger actions
this.props.onAuth(this.state.email, this.state.password, this.state.isSignup);
console.log(this.props.token) //here I can Get Token
}
handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
}
render() {
let form =
<div className={classes.ContactData} >
<h4>Sign IN</h4>
<form onSubmit={this.handleSubmit} >
<Input
elementType='input'
name="email"
required
label="Email"
placeholder="Email"
value={this.state.email}
margin="normal"
onChange={this.handleChange}
/>
<br />
<Input
elementType='input'
required
name="password"
value={this.state.password}
label="Password"
onChange={this.handleChange}
/>
<br />
<Button
color="primary"
type="submit"
>Submit</Button>
<Button color="primary" href="/"> CANCLE</Button>
</form>
</div>
if (this.props.loading) {
form = <Spinner />
}
let errorMessage = null;
if (this.props.error) {
errorMessage =(
<p>{this.props.error.message} </p>
)
}
let token = null;
if(this.props.token){
token = this.props.token.toString()
}
return (
<div>
{errorMessage}
{ form }
</div>
)
}
}
const mapStateToProps = state => {
return {
loading: state.auth.loading,
error: state.auth.error,
token:state.auth.idToken,
}
}
const mapDispatchToProps = dispatch => {
return {
onAuth: (email, password, isSignup) => dispatch(actions.auth(email, password, isSignup)),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Login);
And also, the problem leads to my Spinner and show ErrorMessage not working.
I suppose its a typo.
instead of
switch (action.types)
try this
switch (action.type)
In reducer, we get an object returned from the actions, on the argument action.

Categories