How to submit a form with react and redux? - reactjs

I am trying to submit a POST form with a simple html form and I also use redux with a userAction which allows to store the user and the token returned by the express js API, but the submission does not work, when I submit the form, nothing happens I can't even get into the fetch function of the action.
import '../styles/ConnexionModal.css';
import { userLogin } from '../redux/userActions';
import { useState } from 'react';
// import { useSelector } from 'react-redux';
const ConnexionModal = ({ showModal, hideModal }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
// const message = useSelector(state => state.userReducer.message);
// console.log(message);
const handleChangeEmail = (event) => {
setEmail(event.target.value);
}
const handleChangePassword = (event) => {
setPassword(event.target.value);
}
const handleSubmit = (e) => {
e.preventDefault();
userLogin(email, password);
}
return (
showModal && (
<div className="modalBg">
<div className="modalContainer">
<div className="modalHeader">
<h1 className="modalTitle">Connexion</h1>
</div>
<div className="modalBody">
<form method="POST" onSubmit={handleSubmit}>
<div className="formGroup">
<label htmlFor="email" className="info">Email</label>
<input className="field" name="email" id="email" type="email" value={email} onChange={handleChangeEmail} autoFocus required />
</div>
<div className="formGroup">
<label htmlFor="password" className="info">Mot de passe</label>
<input className="field" name="password" id="password" type="password" value={password} onChange={handleChangePassword} required />
</div>
<div className="formGroup">
<label htmlFor="connexion" className="submitButton">Se connecter</label>
<input className="field submit" id="connexion" name="submit" type="submit" />
</div>
</form>
</div>
<div className="close">
<button onClick={() => hideModal()} className="closeButton">Fermer</button>
</div>
</div>
</div>
)
)
}
export default ConnexionModal;
export const LOGIN = "LOGIN";
export const ERROR = "ERROR";
export const userLogin = (email, password) => {
return async dispatch => {
console.log('test');
fetch('http://192.168.1.36:4000/api/users/login', {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
email: email,
password: password
})
})
.then((res) => res.json())
.then(async (res) => {
if(!res.error) {
localStorage.setItem('auth', res.token);
dispatch({ type: LOGIN, user: res.user, token: res.token });
} else {
dispatch({ type: ERROR, message: res.error });
}
})
}
}
The console.log ('test') does not display anything which means I am not even accessing the userLogin function in my component.

I assume you are also using redux-thunk.
You should always pass the action inside your container to a redux dispatch function. E.g.
import '../styles/ConnexionModal.css';
import { userLogin } from '../redux/userActions';
import { useState } from 'react';
import { useDispatch } from 'react-redux';
const ConnexionModal = ({ showModal, hideModal }) => {
const dispatch = useDispatch();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleChangeEmail = (event) => {
setEmail(event.target.value);
}
const handleChangePassword = (event) => {
setPassword(event.target.value);
}
const handleSubmit = (e) => {
e.preventDefault();
dispatch(userLogin(email, password));
}
...

Related

How can I send React post requet with file data to an API?

Here I want to register user with image so I want to pass both image and name in my formdata.
I am able to upload the file using some guideline (I am not good with react) but I am not able to pass the input name with my formdata. which procedure to follow?
import axios from "axios";
import React, { useState, useEffect } from 'react'
import { LinkContainer } from 'react-router-bootstrap'
import { Table, Button, Row, Col } from 'react-bootstrap'
import { useDispatch, useSelector } from 'react-redux'
const UPLOAD_ENDPOINT = "http://127.0.0.1:8000/api/orders/vendor/register/";
function VendorRegistration() {
const [file, setFile] = useState(null);
const [name, setName] = useState("");
const { userInfo } = useSelector((state) => state.userLogin);
const handleSubmit = async (event) => {
event.preventDefault();
const formData = new FormData();
formData.append("avatar", file);
formData.append("name", name);
const resp = await axios.post(UPLOAD_ENDPOINT, formData, {
headers: {
"content-type": "multipart/form-data",
Authorization: `Bearer ${userInfo.token}`,
},
});
console.log(resp.status)
};
return (
<form onSubmit={handleSubmit}>
<h1>React File Upload</h1>
<input type="file" onChange={(e) => setFile(e.target.files[0])} />
<input type="text" onChange={(e) => setName(e.target.value)} value={name} />
<button type="submit" disabled={!(file && name)}>
Upload File
</button>
{resp.status == 200(
<h1>ok</h1>
)
}
</form>
);
}
export default VendorRegistration;
You'll just want to bind the other input to state as per usual, and then add that value to the form data.
I added rudimentary validation that prevents clicking the submit button unless both fields are filled in, too.
EDIT: I also added status responses, as per comments.
import React from "react";
import axios from "axios";
const UPLOAD_ENDPOINT = "http://127.0.0.1:8000/api/orders/vendor/register/";
function VendorRegistration() {
const [file, setFile] = useState(null);
const [name, setName] = useState("");
const [status, setStatus] = useState("");
const { userInfo } = useSelector((state) => state.userLogin);
const handleSubmit = async (event) => {
setStatus(""); // Reset status
event.preventDefault();
const formData = new FormData();
formData.append("avatar", file);
formData.append("name", name);
const resp = await axios.post(UPLOAD_ENDPOINT, formData, {
headers: {
"content-type": "multipart/form-data",
Authorization: `Bearer ${userInfo.token}`,
},
});
setStatus(resp.status === 200 ? "Thank you!" : "Error.");
};
return (
<form onSubmit={handleSubmit}>
<h1>React File Upload</h1>
<input type="file" onChange={(e) => setFile(e.target.files[0])} />
<input type="text" onChange={(e) => setName(e.target.value)} value={name} />
<button type="submit" disabled={!(file && name)}>
Upload File
</button>
{status ? <h1>{status}</h1> : null}
</form>
);
}
export default VendorRegistration;

Got Received number of calls: 0 error for login functionality

I am writing unit test case for login.
I am unsure about how to test handle submit as it contains one of the service call in the form of getToken() method, it would be greate if someone can guide me through how to handle this situation.
export const getToken = (credentials) => {
const token = 'abccss';
if (
credentials.username === 'test#test.com' &&
credentials.password === '123'
) {
return token;
} else {
return null;
}
};
The above code fetches user name and password and sends it to login in handleSubmit() function
//all imports(loginservice,auth etc etc)
import './Login.scss';
const Login = () => {
const [email, setEmail] = useState('');
const [pwd, setPwd] = useState('');
const authCon = useContext(AuthContext);
const handleSubmit = (e) => {
e.preventDefault();
const token = getToken({ username: email, password: pwd });
if (token) {
authCon.login(token);
window.location.href = '/dashboard';
}
};
return (
<div className="div-login">
<div className="div-login-logo">
<img src={logo} alt="Logo"></img>
</div>
<div>
<form onSubmit={handleSubmit}>
<input
className="credentials-input"
type="email"
value={email}
placeholder="Email Address"
required
onChange={(e) => setEmail(e.target.value)}
/>
<input
className="credentials-input"
type="password"
value={pwd}
placeholder="Password"
required
onChange={(e) => setPwd(e.target.value)}
/>
<button className="login-button" type="submit">
Log In
</button>
</form>
</div>
</div>
);
};
export default Login;
Test Code
test('Submit shoud work successfully', () => {
const mockLogin = jest.fn();
const { getByRole } = render(<Login handleSubmit={mockLogin} />);
const login_button = getByRole('button');
fireEvent.submit(login_button);
expect(mockLogin).toHaveBeenCalledTimes(1);
});
expect(jest.fn()).toHaveBeenCalledTimes(expected)
Expected number of calls: 1
Received number of calls: 0
As I am new to React, help will be appreciated.
The actual issue is handleSubmit is not a props of Login component.
Also you can't test the internal methods of a component using React testing Library, you have to move the handleSubmit method to either parent component or a common file and pass it to the login component or import it so that you can mock the method and perform the test.
Move the getToken and handleSubmit to a common file like below,
common.ts
export const getToken = (credentials:any) => {
const token = 'abccss';
if (
credentials.username === 'test#test.com' &&
credentials.password === '123'
) {
return token;
} else {
return null;
}
};
export const handleSubmit = (e:any, email:string, pwd: string) => {
e.preventDefault();
const token = getToken({ username: email, password: pwd });
if (token) {
// authCon.login(token);
window.location.href = '/dashboard';
}
};
Modify Login.ts as like below ( see below handleSubmit is not internal and its imported from common.ts file so we that we can mock it)
import React, { useContext, useState } from 'react';
import { getToken, handleSubmit } from './common';
const Login = () => {
const [email, setEmail] = useState('');
const [pwd, setPwd] = useState('');
// const authCon = useContext(AuthContext);
return (
<div className="div-login">
<div className="div-login-logo">
{/* <img src={logo} alt="Logo"></img> */}
</div>
<div>
<form onSubmit={(e) => handleSubmit(e, email, pwd)}>
<input
className="credentials-input"
type="email"
value={email}
placeholder="Email Address"
required
onChange={(e) => setEmail(e.target.value)}
/>
<input
className="credentials-input"
type="password"
value={pwd}
placeholder="Password"
required
onChange={(e) => setPwd(e.target.value)}
/>
<button className="login-button" type="submit">
Log In
</button>
</form>
</div>
</div>
);
};
export default Login;
And finally Login.test.tsx shown below
import { fireEvent, render, screen } from '#testing-library/react';
import Login from './Login';
import * as CommonModule from './common';
jest.mock('./common');
test('Submit shoud work successfully', () => {
const mockLogin = jest.spyOn(CommonModule,'handleSubmit').mockImplementation();
const { getByRole } = render(<Login />);
const login_button = getByRole('button');
fireEvent.submit(login_button);
expect(mockLogin).toHaveBeenCalledTimes(1);
});
Test Result :

Update state after axios POST request instead of page refresh

I have a form with input fields 'title' and 'body', they are being added to MongoDB after submitting the form, but the new post item shows up only after I refresh the page (that of course is not how it should work), but I want to know what I am doing wrong and how to fix the handleSumbit function?
Here is my code:
import { useState, useEffect } from "react";
import axios from "axios";
import "./App.css";
function App() {
const [title, setTitle] = useState("");
const [body, setBody] = useState("");
const [posts, setPosts] = useState([]);
const url = "http://localhost:8005/";
useEffect(() => {
const fetchPosts = async () => {
const response = await axios(`${url}posts`);
setPosts(response.data);
};
fetchPosts();
}, []);
const handleTitleChange = (event) => {
setTitle(event.target.value);
};
const handleBodyChange = (event) => {
setBody(event.target.value);
};
const handleSubmit = (event) => {
event.preventDefault();
axios.post(`${url}posts`, { title: title, body: body })
.then((res) => {
console.log(res.data);
});
alert("Post added!");
setTitle("");
setBody("");
};
console.log(posts);
return (
<div className="App">
{posts.map((post) => (
<div className="post" key={post.id}>
<h4>{post.title}</h4>
<p>{post.body}</p>
</div>
))}
<form onSubmit={handleSubmit}>
<label>
Mew post:
<input
type="text"
name="title"
placeholder="Add title"
onChange={handleTitleChange}
/>
<input
type="text"
name="body"
placeholder="Add body"
onChange={handleBodyChange}
/>
</label>
<button type="submit">Add</button>
</form>
</div>
);
}
export default App;
The problem is that you didn't update the posts state after you sent the axios post request. Edit the below code block:
const handleSubmit = (event) => {
event.preventDefault();
axios.post(`${url}posts`, { title: title, body: body })
.then((res) => {
console.log(res.data);
// save the new post to posts
setPosts([...posts, res.data])
});
alert("Post added!");
setTitle("");
setBody("");
};

How to POST request using axios with React Hooks?

This is my Signupcomponent
const SignupComponent = () => {
const [values, setValues] = useState({
username: 'silvio1',
name: 'Silvioo',
email: 'berlusconi#gmail.com',
password: '123ooo007',
});
const [loading, setLoading] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
const { username, name, email, password } = values;
const user = {username, name, email, password};
await axios.post('${API)/signup', user);
};
const handleChange = name => e => {
setValues({ ...values, [name]: e.target.value });
};
const showLoading = () => (loading ? <div className="alert alert-info">Loading...</div> : '');
const signupForm = () => {
return (
<form onSubmit={handleSubmit}>
<div className="form-group">
<input
value={values.username}
onChange={handleChange('username')}
type="text"
className="form-control"
placeholder="Type your username"
/>
</div>
<div className="form-group">
<input
value={values.name}
onChange={handleChange('name')}
type="text"
className="form-control"
placeholder="Type your name"
/>
</div>
<div className="form-group">
<input
value={values.email}
onChange={handleChange('email')}
type="email"
className="form-control"
placeholder="Type your email"
/>
</div>
<div className="form-group">
<input
value={values.password}
onChange={handleChange('password')}
type="password"
className="form-control"
placeholder="Type your password"
/>
</div>
<div>
<button className="btn btn-primary">Signup</button>
</div>
</form>
);
};
return <React.Fragment>
{showLoading()}
{signupForm()}
</React.Fragment>;
};
export default SignupComponent;
EDIT
I changed my code(zhulien's accepted answer).
Signup page appears,I try to sign up user.
I got error
Unhandled Runtime Error
Error: Request failed with status code 404
Call Stack
createError
node_modules/axios/lib/core/createError.js (16:0)
settle
node_modules/axios/lib/core/settle.js (17:0)
XMLHttpRequest.handleLoad
node_modules/axios/lib/adapters/xhr.js (62:0)
Frontend folder
components
config.js
next.config.js
node_modules
package.json
package-lock.json
pages
My pages folder
_document.js
index.js
signin.js
signup.js
signup.js imports the code above
import Link from 'next/link';
import Layout from '../components/Layout';
import SignupComponent from '../components/frontauth/SignupComponent';
const Signup = () => {
return (
<Layout>
<h2>Signup page</h2>
<SignupComponent />
</Layout>
);
};
My next.config.js
{
APP_NAME: 'BLOG FRONTEND',
APP_DEVELOPMENT: 'http://localhost:3000',
PRODUCTION: false
}
And config.js
const { publicRuntimeConfig } = getConfig();
console.log(publicRuntimeConfig);
export const API = publicRuntimeConfig.PRODUCTION
? 'https://cryptoblog.com'
: 'http://localhost:3000';
export const APP_NAME = publicRuntimeConfig.APP_NAME;
I am new to React and React Hooks. How to solve this problem?
First of all, you're trying to access {username}(which doesn't exist) instead of the state property which is values.username. Furthermore, don't use hooks in event handlers, they should be used in the top level body of the component or in custom hooks only. Checkout this: React hooks rules.
So:
In your form you have to use the state(values) properties.
Extract useEffect hook in the main body flow of the component or BETTER remove it altogether as you're not using it properly currently. You're better of with just the simple event handler for form submit which should post the data somewhere without setting any state.
Your code could look something like:
import axios from 'axios';
import React, { useEffect, useState } from 'react';
import { API } from '../../config';
const SignupComponent = () => {
const [values, setValues] = useState({
username: 'silvio1',
name: 'Silvioo',
email: 'berlusconi#gmail.com',
password: '123ooo007',
});
const [loading, setLoading] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
const { username, name, email, password } = values;
const user = {username, name, email, password};
await axios.post('${API)/signup', user);
};
const handleChange = name => e => {
setValues({ ...values, [name]: e.target.value });
};
const showLoading = () => (loading ? <div className="alert alert-info">Loading...</div> : '');
const signupForm = () => {
return (
<form onSubmit={handleSubmit}>
<div className="form-group">
<input
value={values.username}
onChange={handleChange('username')}
type="text"
className="form-control"
placeholder="Type your username"
/>
</div>
this is how it should be:
useEffect(() => {
postUser();
}, []);
not inside the function the way you have done it:
const handleSubmit = e => {
e.preventDefault();
setValues({...values});
const { username, name, email, password } = values;
const user = {username, name, email, password};
async function postUser () {
const result = await axios.post('${API)/signup', user);
};
useEffect(() => {
postUser();
}, []);
};
UseEffects aren't meant to be placed inside your functions.Just place them inside your functional component,with some value(or no value) inside your dependency array of the useEffect.These values present inside the array will trigger the useEffect whenever they get changed.

Reset Password in React

i am creating a mern app. i got stuck in forgot password. i'm able to send a mail for forgot password but when i try to set new password it is not changing password but in postman i was able to change the password but when it comes to react i was not. I know the problem is that i was not able to get token as params .
work in postman but not in when i try in react.
Resetpassword component
import React, { Fragment, useState } from 'react';
import { connect } from 'react-redux';
import { Link, Redirect } from 'react-router-dom';
import { setAlert } from '../../actions/alert';
import { reset } from '../../actions/auth';
import PropTypes from 'prop-types';
const Reset = ({ setAlert, reset }) => {
const [formData, setFormData] = useState({
password: '',
password2: ''
});
const { password, password2 } = formData;
const onChange = e =>
setFormData({ ...formData, [e.target.name]: e.target.value });
const onSubmit = async => {
const token = props.match.params.token;
console.log(token);
if (password !== password2) {
setAlert('password does not matched', 'danger');
} else {
reset({ password, token });
}
};
return (
<Fragment>
<section className='container'>
<h1 className='large text-primary'>RESET PASSWORD</h1>
<p className='lead'>
<i className='fas fa-user' /> Create Your NEW PASSWORD
</p>
<form
className='form'
onSubmit={e => onSubmit(e)}
action='create-profile.html'
>
<div className='form-group'>
<input
type='password'
placeholder='Password'
name='password'
value={password}
onChange={e => onChange(e)}
/>
</div>
<div className='form-group'>
<input
type='password'
placeholder='Confirm Password'
name='password2'
value={password2}
onChange={e => onChange(e)}
/>
</div>
<input type='submit' className='btn btn-primary' value='Register' />
</form>
<p className='my-1'>
Already have an account? <Link to='/login'>Sign In</Link>
</p>
</section>
</Fragment>
);
};
Reset.propTypes = {
setAlert: PropTypes.func.isRequired,
reset: PropTypes.func.isRequired
};
export default connect(
null,
{ setAlert, reset }
)(Reset);
resetaction.JS
export const reset = ({ password, token }) => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
const body = JSON.stringify({ password, token });
try {
const res = await axios.put(
`http://localhost:3000/api/auth/reset/${token}`,
body,
config
);
dispatch({
type: RESET_PASSWORD,
payload: res.data
});
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach(error => dispatch(setAlert(error.msg, 'danger')));
}
}
};
By only seeing this snippet I assume your problems are following lines:
const Reset = ({ setAlert, reset }) => {
//...
const token = props.match.params.token;
You destructed the whole props argument (into { setAlert, reset }), so in your case props is undefined. You should adapt your code to this:
const Reset = ({ setAlert, reset, match }) => {
//...
const token = match.params.tok

Resources