React setState sets to default - javascript

I am building a simple login form page in React. The <form> has 2 inputs (email and password), an error message, and a submit button.
The submit button can be set to the Loading state during the authentication request.
So I have the following component:
function LoginForm() {
const [state, setState] = useState({ loading: false, error: "", x: 1 }); // x value only for visualising...
const auth = useAuth();
const navigate = useNavigate();
const login = async ({ email, password }) => {
try {
if (!email || !password) {
return { errors: "Invalid fields" };
}
const { errors } = await auth.authenticate(email, password);
if (!errors) {
return {};
}
console.log("LoginButton", "Error login. Not Redirecting", errors);
return {
errors: "Por favor verifique seu email e/ou senha e tente novamente.",
};
} catch (error) {
return { errors: "Unexpected error. Please, try again later." };
}
};
const inputs = [
{
name: "email",
},
{
name: "password",
type: "password",
},
];
const handleSubmit = (values) => {
setState({ ...state, loading: true, error: "", x: 2 }); // First call
login(values).then(({ errors: error }) => {
if (!error) navigate("/profile");
const newState = { loading: false, error: "Error while login", x: 3 }; // Second call
setState(newState);
});
};
useEffect(() => {
console.log(state); // Only for debugin
});
return (
<Form
inputs={inputs}
onSubmit={handleSubmit}
>
<ErrorMessage text={state.error} />
<div>
<Submit loading={state.loading}>Entrar</Submit>
<Link
to="/forgot-password"
>
Esqueceu sua senha?
</Link>
</div>
</Form>
);
}
The <Form/> component only gets the inputs array and creates the list of inputs...
The login function was called, and it set the state successfully on the first setState call (x: 2), but on the second call, the state was reset to the default value (x: 1).
Why did the second setState reset the default value? How can I fix this?

I think I've solved... But I don't Understand how...
function LoginForm() {
const [state, setState] = useState({ loading: false, error: "", x: 1 });
const auth = useAuth();
const navigate = useNavigate();
const inputs = [
{
name: "email",
},
{
name: "password",
type: "password",
},
];
const handleSubmit = async (values) => {
const { email, password } = values;
setState({ ...state, loading: true, error: "", x: 2 });
auth.authenticate(email, password).then(({ errors }) => {
if (!errors) navigate("/profile");
const newState = { loading: false, error: errors, x: 3 };
setState(newState);
});
};
useEffect(() => {
console.log(state);
});
return (
<Form
inputs={inputs}
onSubmit={handleSubmit}
>
<ErrorMessage text={state.error} />
<div>
<Submit loading={state.loading}>Entrar</Submit>
<Link
css={`
color: white;
`}
to="/forgot-password"
>
Esqueceu sua senha?
</Link>
</div>
</Form>
);
}
export default LoginForm;
This worked...

Related

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 });
}
};

TypeError: Cannot read property when using redux with functional component

I am trying to convert a class component which uses reducer and redux into functional component.
The functional component is giving me error. This is the class component for login page:
import { connect } from "react-redux";
import { loginUser } from "../../actions/authActions";
import classnames from "classnames";
class Login extends Component {
constructor() {
super();
this.state = {
email: "",
password: "",
errors: {}
};
}
componentDidMount() {
// If logged in and user navigates to Login page, should redirect them to dashboard
if (this.props.auth.isAuthenticated) {
this.props.history.push("/dashboard");
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.auth.isAuthenticated) {
this.props.history.push("/dashboard");
}
if (nextProps.errors) {
this.setState({
errors: nextProps.errors
});
}
}
onChange = e => {
this.setState({ [e.target.id]: e.target.value });
};
onSubmit = e => {
e.preventDefault();
const userData = {
email: this.state.email,
password: this.state.password
};
this.props.loginUser(userData);
};
render() {
const { errors } = this.state;
return (
<div className="container">
<div style={{ marginTop: "4rem" }} className="row">
<div className="col s8 offset-s2">
<div className="col s12" style={{ paddingLeft: "11.250px" }}>
<h4>
<b>Login</b>
</h4>
</div>
<form noValidate onSubmit={this.onSubmit}>
<div className="input-field col s12">
<input
onChange={this.onChange}
value={this.state.email}
error={errors.email}
id="email"
type="email"
className={classnames("", {
invalid: errors.email || errors.emailnotfound
})}
/>
similar fields for email, password and button...
const mapStateToProps = state => ({
auth: state.auth,
errors: state.errors
});
export default connect(
mapStateToProps,
{ loginUser }
)(Login);
This is the reducer:
import { SET_CURRENT_USER, USER_LOADING } from "../actions/types";
const isEmpty = require("is-empty");
const initialState = {
isAuthenticated: false,
user: {},
loading: false
};
export default function(state = initialState, action) {
switch (action.type) {
case SET_CURRENT_USER:
return {
...state,
isAuthenticated: !isEmpty(action.payload),
user: action.payload
};
case USER_LOADING:
return {
...state,
loading: true
};
default:
return state;
}
}
I have made the following changes:
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [errors, setErrors] = useState([]);
useEffect(() => {
if (props.auth.isAuthenticated) {
props.history.push("/dashboard");
}
});
useEffect(() => {
if (props.auth.isAuthenticated) {
props.history.push("/dashboard");
}
}, [props.auth]);
I get the following error when I try to tun it:
TypeError: Cannot read property 'isAuthenticated' of undefined
useEffect(() => {
36 | // Update the document title using the browser API
> 37 | if (props.auth.isAuthenticated) {
| ^ 38 | props.history.push("/dashboard");
39 | }
40 | });
I think the reducer state is not being read here. Can someone please help me out?
value of the "auth" object is not filled at first time
And since the auth was null , not access the "isAuthenticated"
You can use the bottom hook you wrote
useEffect(() => {
if (props.auth.isAuthenticated) {
props.history.push("/dashboard");
}
}, [props.auth]);
or you can check null value
...
if ( props.auth && props.auth.isAuthenticated)
...
i suggest redux hooks because you want to work with functional components
you can try this
const state = useSelector(state => state.yourReducer)
useEffect(() => {
if (state.auth && state.auth.isAuthenticated) {
props.history.push("/dashboard");
}
}, [state]);

react form input values are not showing correctly

i am working on a project and creating a form to create tours. everything is working fine just the issue is form input values are exchanging
for ex -
actual output- { tourName: 'pune darshan', location: '999', price: 'pune' }
expected output :- { tourName: 'pune darshan', location: 'pune', price: '999' }
i dont know where i am going wrong i am stuck here since 6 hrs
here is what i have tried
form component
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { createTour } from "../../store/slices/tourSlice";
import "./createListing.scss";
const CreateListing = () => {
const [tour, setTour] = useState({
tourName: "",
price: "",
location: "",
});
const dispatch = useDispatch();
const handleInput = (event) => {
setTour((tour) => ({
...tour,
[event.target.name]: event.target.value,
}));
};
const handleSubmit = (event) => {
event.preventDefault();
dispatch(createTour(tour.tourName, tour.price, tour.location));
};
return (
<div>
<div className='form-controller'>
<form action='' method='post' onSubmit={handleSubmit}>
<div className='form-group'>
<input
type='text'
className='form-control'
name='tourName'
placeholder='Enter Tour Name'
onChange={handleInput}
required
/>
</div>
<div className='form-group'>
<input
type='text'
className='form-control'
name='location'
placeholder='Enter Tour Location'
onChange={handleInput}
required
/>
</div>
<div className='form-group'>
<input
type='number'
className='form-control'
name='price'
placeholder='Enter Tour Cost'
onChange={handleInput}
required
/>
</div>
<div className='text-center'>
<button type='submit theme-btn'>Create Tour</button>
</div>
</form>
</div>
</div>
);
};
export default CreateListing;
here is the redux toolkit file
import { createSlice } from "#reduxjs/toolkit";
import axios from "axios";
import { history } from "./../../helpers/history";
export const initialState = {
tourName: "",
location: "",
price: "",
error: "",
loading: false,
};
const tourSlice = createSlice({
name: "tour",
initialState,
reducers: {
tourCreateRequest: (State, action) => {
return {
loading: true,
};
},
tourCreateSuccess: (state, action) => {
return { loading: false, tourInfo: action.payload };
},
tourCreateFail: (state, action) => {
return {
loading: false,
error: action.payload,
};
},
},
});
const {
tourCreateFail,
tourCreateRequest,
tourCreateSuccess,
} = tourSlice.actions;
export default tourSlice.reducer;
export const createTour = (tourName, location, price) => async (dispatch) => {
try {
dispatch(tourCreateRequest);
const tourData = {
tourName,
location,
price,
};
const res = await axios.post(
"http://localhost:3000/api/v1/tours",
tourData
);
if (res) {
dispatch(tourCreateSuccess);
// history.push("/dashboard");
} else {
dispatch(
tourCreateFail(
error.response && error.response.data.message
? error.response.data.message
: error.message
)
);
console.log("error");
}
} catch (error) {
dispatch(
tourCreateFail(
error.response && error.response.data.message
? error.response.data.message
: error.message
)
);
}
};
here is the model file
const mongoose = require("mongoose");
const tourSchema = mongoose.Schema(
{
tourName: { type: String },
rating: { type: String, default: 4.5 },
location: { type: String },
price: { type: String, default: 999 },
},
{ timestamps: {} }
);
const Tour = mongoose.model("Tour", tourSchema);
module.exports = Tour;
here is controller code
const createTours = async (req, res, next) => {
const { tourName, price, location } = req.body;
console.log(req.body);
try {
const newTour = new Tour({
tourName,
price,
location,
});
newTour.save();
res.status(200).json({
status: "success",
newTour,
});
} catch (error) {
res.status(404).json({
status: "failed",
error: error,
});
}
};
You pass the parameters in the createTour function in the wrong order.
You should update the dispatch line:
dispatch(createTour(tour.tourName, tour.location, tour.price));

React Type Error when implementing authentication with Python

I'm trying to implement authentication with Python and React, and I have this error message on the front-end.
TypeError: Cannot read property 'loading' of undefined
And this is my SignIn.js
import React, { Component } from "react";
import { Button, Checkbox, Form, Icon, Input } from "antd";
import { Link, Redirect } from "react-router-dom";
import { connect } from "react-redux";
import { authLogin } from "../store/actions/auth";
class SignIn extends React.Component {
state = {
username: "",
password: ""
};
handleChange = e => {
this.setState({ [e.target.name]: e.target.value });
};
handleSubmit = e => {
e.preventDefault();
const { username, password } = this.state;
this.props.login(username, password);
};
render() {
const { getFieldDecorator } = this.props.form;
const { error, loading, token } = this.props;
const { username, password } = this.state;
if (token) {
return <Redirect to="/" />;
}
return (
<div className="gx-login-container">
<div className="gx-login-content">
<div className="gx-login-header gx-text-center">
<h1 className="gx-login-title">Sign In</h1>
</div>
{error && <p>{this.props.error.message}</p>}
<React.Fragment>
<Form onSubmit={this.handleSubmit} className="gx-login-form gx-form-row0">
{getFieldDecorator('email', {
rules: [{ required: true, message: 'Please input your email!' }],
})(
<Button type="primary" htmlType="submit" loading={loading} disabled={loading}>
Log in
</Button>
</Form>
</React.Fragment>
</div>
</div>
);
}
}
const mapStateToProps = state => {
return {
loading: state.auth.loading,
error: state.auth.error,
token: state.auth.token
};
};
const mapDispatchToProps = dispatch => {
return {
login: (username, password) => dispatch(authLogin(username, password))
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(SignIn);
I have deleted the input part because I don't think that there is some problem. If someone think that the input part is the problem I will gladly post it.
Here is my reducers/auth.js
import * as actionTypes from "../actions/actionTypes";
import { updateObject } from "../utility";
const initialState = {
token: null,
error: null,
loading: false
};
const authStart = (state, action) => {
return updateObject(state, {
error: null,
loading: true
});
};
const authSuccess = (state, action) => {
return updateObject(state, {
token: action.token,
error: null,
loading: false
});
};
const authFail = (state, action) => {
return updateObject(state, {
error: action.error,
loading: false
});
};
const authLogout = (state, action) => {
return updateObject(state, {
token: null
});
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.AUTH_START:
return authStart(state, action);
case actionTypes.AUTH_SUCCESS:
return authSuccess(state, action);
case actionTypes.AUTH_FAIL:
return authFail(state, action);
case actionTypes.AUTH_LOGOUT:
return authLogout(state, action);
default:
return state;
}
};
export default reducer;
The errors says that it cannot find the property loading in an undefined object. Maybe your state.auth is null or undefined. Try logging the state.auth to check if it has a value.
I think I got your issue. You have initial state as,
const initialState = {
token: null,
error: null,
loading: false
};
and you are trying to get the state in mapStateToProps as,
const mapStateToProps = state => {
return {
loading: state.auth.loading,
error: state.auth.error,
token: state.auth.token
};
};
Here you are trying to access state using state.auth.loading, but you don't have auth object in your initial state and you get undefined error. You can fix this like,
const mapStateToProps = state => {
return {
loading: state.loading,
error: state.error,
token: state.token
};
};
Note: If your updateObject function returning state with auth object then you need to correct that. Your initial state and returned state should be of same pattern.

Can't upload file to firebase storage

I'am trying to upload files to firebase in react, But the file upload progress reaches 100% then suddenly it shows me an unknown error like
{
"error": {
"code": 400,
"message": "Bad Request. Could not create object",
"status": "CREATE_OBJECT"
}
}
this is the code I'm using to upload the file, This is the actual component where file uploading is done, The user opens a modal to select a file and then after selecting and pressing send in the modal the file uploading starts in the below component.
import React, { Component } from "react";
import { Segment, Button, Input, ButtonGroup } from "semantic-ui-react";
import firebase from "../../firebase";
import FileModal from "./FileModal";
import uuidv4 from "uuid/v4";
class MessageForm extends Component {
state = {
storageRef: firebase.storage().ref(),
message: "",
channel: this.props.currentChannel,
user: this.props.currentUser,
loading: false,
errors: [],
modal: false,
uploadState: "",
uploadTask: null,
percentUploaded: 0
};
uploadFile = (file, metadata) => {
const pathToUpload = this.state.channel.id;
const ref = this.props.messagesRef;
const filePath = `chat/public/${uuidv4}.jpg`;
this.setState(
{
uploadState: "uploading",
uploadTask: this.state.storageRef.child(filePath).put(file, metadata)
},
() => {
this.state.uploadTask.on(
"state_changed",
snap => {
const percentUploaded = Math.round(
(snap.bytesTransferred / snap.totalBytes) * 100
);
this.setState({ percentUploaded });
},
err => {
console.error(err);
this.setState({
errors: [...this.state.errors, err],
uploadState: "error",
uploadTask: null
});
},
() => {
console.log(this.state.uploadTask);
this.state.uploadTask.snapshot.ref
.getDownloadURL()
.then(downloadUrl => {
this.sendFileMessage(downloadUrl, ref, pathToUpload);
})
.catch(err => {
console.error(err);
this.setState({
errors: [...this.state.errors, err],
uploadState: "error",
uploadTask: null
});
});
}
);
}
);
};
sendFileMessage = (fileUrl, ref, pathToUpload) => {
ref
.child(pathToUpload)
.push()
.set(this.createMessage(fileUrl))
.then(() => {
this.setState({
uploadState: "done"
}).catch(err => {
console.error(err);
this.setState({
errors: [...this.state.errors, err]
});
});
});
};
openModal = () => {
this.setState({
modal: true
});
};
closeModal = () => {
this.setState({
modal: false
});
};
handleChange = e => {
this.setState({
[e.target.name]: e.target.value
});
};
createMessage = (fileUrl = null) => {
const message = {
timestamp: firebase.database.ServerValue.TIMESTAMP,
user: {
id: this.state.user.uid,
name: this.state.user.displayName,
avatar: this.state.user.photoURL
}
};
if (fileUrl != null) {
message["image"] = fileUrl;
} else {
message["content"] = this.state.message.trim();
}
return message;
};
sendMessage = () => {
const { messagesRef } = this.props;
const { message, channel } = this.state;
if (message) {
this.setState({
loading: true
});
messagesRef
.child(channel.id)
.push()
.set(this.createMessage())
.then(() => {
this.setState({
loading: false,
message: "",
errors: []
});
})
.catch(err => {
console.error(err);
this.setState({
loading: false,
errors: [...this.state.errors, err]
});
});
} else {
this.setState({
errors: [...this.state.errors, { message: "Add a message" }]
});
}
};
render() {
const { errors, message, loading, modal } = this.state;
return (
<Segment className="message__form">
<Input
fluid
name="message"
style={{ marginBottom: "0.7em" }}
icon="add"
iconPosition="left"
placeholder="Write your message"
onChange={this.handleChange}
className={
errors.some(error => error.message.includes("message"))
? "error"
: ""
}
value={message}
/>
<ButtonGroup icon widths="2">
<Button
onClick={this.sendMessage}
disabled={loading}
color="orange"
content="Add reply"
labelPosition="left"
icon="edit"
/>
<Button
color="violet"
content="Upload Media"
labelPosition="right"
icon="cloud upload"
onClick={this.openModal}
/>
<FileModal
modal={modal}
closeModal={this.closeModal}
uploadFile={this.uploadFile}
/>
</ButtonGroup>
</Segment>
);
}
}
export default MessageForm;
Just a guess, but I suspect that your error might be related to the way you are storing the uploadTask in the component's state... and it makes me pretty uncomfortable - it seems to violate one of the core principles of using component state in React.
As you've probably heard already state should only be mutated via the setState command... and the problem with your approach is that the uploadTask portion of the state will be mutated during the upload execution. In fact, your code counts on it - you've written it so that as the uploadTask is updated, its percentage gets displayed on screen.
Overall, you've got the right idea - just take that uploadTask: this.state.storageRef.child(filePath).put(file, metadata) assignment out of your state... something like this:
uploadFile = (file, metadata) => {
const pathToUpload = this.state.channel.id;
const ref = this.props.messagesRef;
const filePath = `chat/public/${uuidv4}.jpg`;
this.setState(
{
uploadState: "uploading",
},
() => {
let uploadTask = this.state.storageRef.child(filePath).put(file, metadata);
uploadTask.on(
"state_changed",
snap => {
const percentUploaded = Math.round(
(snap.bytesTransferred / snap.totalBytes) * 100
);
this.setState({ percentUploaded });
},
err => {
console.error(err);
this.setState({
errors: [...errors, err],
uploadState: "error",
});
},
() => {
console.log(uploadTask);
uploadTask.snapshot.ref
.getDownloadURL()
.then(downloadUrl => {
this.sendFileMessage(downloadUrl, ref, pathToUpload);
})
.catch(err => {
console.error(err);
this.setState({
errors: [...this.state.errors, err],
uploadState: "error",
});
});
}
);
}
);
};
(Untested code, conceptual only)

Categories