submit form with react redux - reactjs

i implement a single form for each sign up/ login with redux i test the apis with postman before trying to submit data with forms so the login form works fine but when i try to submit the register form nothing happened , just i got the console log message in the navigator console ..
here is my code :
form.js
import React, { useEffect } from "react";
import { Link, useNavigate } from "react-router-dom";
import { useSelector, useDispatch } from "react-redux";
import PropTypes from "prop-types";
import { useForm } from "react-hook-form";
import { login } from "../../redux/index";
import loginSvg from "../../images/Login-amico.svg";
import signUpSvg from "../../images/signup.svg";
function Form({ page }) {
const dispatch = useDispatch();
const navigate = useNavigate();
const userLogin = useSelector((state) => state.userLogin);
const { error: loginError, loading: loginLoading, userInfo } = userLogin;
const { register, handleSubmit } = useForm();
const onSubmit = (data) => {
if (page !== "signUp") {
dispatch(login(data.email, data.password));
} else {
console.log("smthg else");
}
};
useEffect(() => {
if (userInfo) {
navigate("/app/dashboard", { replace: true });
}
}, [navigate, userInfo]);
return (
<form onSubmit={handleSubmit(onSubmit)} >
{page === "signUp" ? (
<div className="mb-3">
<label>User Name</label>
<input
type="text"
className="form-control"
placeholder="user name"
{...register("userName", {
required: true,
className:
"form-control form-control-lg",
type: "text",
placeholder: "User Name",
})}
/>
</div>
) : undefined}
<div className="mb-3">
<label>Email address</label>
<input
type="email"
className="form-control"
placeholder="Enter email"
{...register("email", {
required: true,
className: "form-control",
type: "email",
placeholder: "Email address",
})}
/>
</div>
<div className="mb-3">
<label>Password</label>
<input
type="password"
className="form-control"
placeholder="Enter password"
{...register("password", {
required: true,
className: "form-control",
type: "password",
placeholder: "Password",
})}
/>
</div>
<div className="d-grid">
<button type="submit" className="btn btn-primary">
Submit
</button>
</div>
<p className="forgot-password text-right">
Already registered sign in?
</p>
</form>
);
}
Form.propTypes = {
page: PropTypes.string.isRequired,
};
export default Form;
how can i solve this issue ?

Unfortunately I can't comment yet but I have a suggestion. I'm not very familiar with the useForm hook but I see in your onSubmit function that if page is signUp you will execute only a console.log message. Maybe you should add some dispatch to this condition as well.

Related

Form values coming as blank on submit

Not able to pick the values of email and token from the below form. It is displaying as blank during on click on submit. Can anyone please advise where I am doing wrong ?
https://codesandbox.io/s/dazzling-kirch-1gqq4?file=/src/App.js
import React, { useRef, useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import Axios from "axios";
const CreateLink = () => {
const [email, setEmail] = useState("");
const [token, setToken] = useState("");
const {
handleSubmit,
register,
formState: { errors }
} = useForm();
const onSubmit = (e) => {
e.preventDefault();
alert(`Sending Email ${email}`);
const fetchData = async () => {
try {
const res = await Axios.post(
"http://localhost:8000/service/createlink",
email,
token
);
if (res.data.success) {
console.log("Link token created:" + res.data.success);
}
} catch (e) {
console.log(e);
}
};
fetchData();
};
return (
<div className="App">
<h1>Create Link</h1>
<form onSubmit={handleSubmit(onSubmit)} className="linkForm inputForm">
<div className="inputField">
<input
name="email"
placeholder="email"
type="text"
value={email}
onChange={(e) => setEmail(e.target.value)}
{...register("email", {
required: "Email is required",
pattern: {
value: /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i,
message: "Invalid email address"
}
})}
/>
<span className="loginErrorTextFormat">
{errors.email && <p>{errors.email.message}</p>}
</span>
</div>
<div className="inputField">
<input
placeholder="token"
name="token"
type="text"
value={token}
onChange={(e) => setToken(e.target.value)}
{...register("token", {
required: "Token is required"
})}
/>
<span className="loginErrorTextFormat">
{errors.token && <p>Input is not valid</p>}
</span>
</div>
<input type="submit" />
</form>
</div>
);
};
export default CreateLink;
Because register return an object has property onChange. So it override your onChange, you just need move onChange after register
{...register("token", {
required: "Token is required"
})}
onChange={(e) => setToken(e.target.value)}

Open a new component when the register button is clicked

Open as a new page and the user goes inside the page when the button is clicked. How can I do it in react functional component? I want to navigate from Register to another MyProfile component when button is clicked. The code for Register component is as below:
import React, { useState } from 'react'
import './Register.css'
import axios from 'axios'
import { useHistory } from 'react-router-dom'
function Register() {
const history = useHistory();
const [data, setData] = useState({
name: "",
email: "",
password: "",
confirmpassword: "",
});
const register = (e) => {
e.preventDefault();
history.push("/myprofile")
}
const InputEvent = (event) => {
const { name, value } = event.target;
setData((preVal) => {
return {
...preVal,
[name]: value,
};
});
};
const formSubmit = (e) => {
e.preventDefault();
};
return (
<div className="signup__container">
<div className="signup__containerInfo">
<h1 className="h1__swari">swari</h1>
<hr />
</div>
<form4 onSubmit={formSubmit}>
<h5 className="h5__form"> Name</h5>
<input type="text" placeholder="Full Name" name="name" value={data.name} onChange={InputEvent} />
<h5 className="h5__form"> Email-Address </h5>
<input type="Email" placeholder="Email" name="email" value={data.email} onChange={InputEvent} />
<h5 className="h5__form"> Password </h5>
<input type="Password" placeholder="Password" name="password" value={data.password} onChange={InputEvent} />
<h5 className="h5__form"> Confirm Password </h5>
<input type="Password" placeholder="Confirm Password" name="confirmpassword" value={data.confirmpassword} onChange={InputEvent} />
<p>
<button onClick={register} type="submit" className="signup__registerButton" >Register Now</button>
</p>
</form4>
</div>
)
}
export default Register
Make a separate component of History.js
import { createBrowserHistory } from 'history';
export default createBrowserHistory();
Then in Register component.
import history from "../Shared/history";
const formSubmit = e => {
e.preventDefault();
history.push("/SomeComponent", {
someData: true,
otherData:{},
});
};
In Routes:
<Route path="/SomeComponent" component={Component} />

React Hook Form with AntD Styling

I'm trying to figure out how to use react-hook-form with antd front end.
I have made this form and it seems to be working (it's part 1 of a multipart form wizard) except that the error messages do not display.
Can anyone see what I've done wrong in merging these two form systems?
I'm not getting any errors, but I think I have asked for both form fields to be required but if I press submit without completing them the error messages are not displayed.
import React from "react";
import useForm from "react-hook-form";
import { BrowserRouter as Router, Route } from "react-router-dom";
import { StateMachineProvider, createStore } from "little-state-machine";
import { withRouter } from "react-router-dom";
import { useStateMachine } from "little-state-machine";
import updateAction from "./updateAction";
import { Button, Form, Input, Divider, Layout, Typography, Skeleton, Switch, Card, Icon, Avatar } from 'antd';
const { Content } = Layout
const { Text, Paragraph } = Typography;
const { Meta } = Card;
createStore({
data: {}
});
const General = props => {
const { register, handleSubmit, errors } = useForm();
const { action } = useStateMachine(updateAction);
const onSubit = data => {
action(data);
props.history.push("./ProposalMethod");
};
return (
<div>
<Content
style={{
background: '#fff',
padding: 24,
margin: "auto",
minHeight: 280,
width: '70%'
}}
>
<Form onSubmit={handleSubmit(onSubit)}>
<h2>Part 1: General</h2>
<Form.Item label="Title" >
<Input
name="title"
placeholder="Add a title"
ref={register({ required: true })}
/>
{errors.title && 'A title is required.'}
</Form.Item>
<Form.Item label="Subtitle" >
<Input
name="subtitle"
placeholder="Add a subtitle"
ref={register({ required: true })}
/>
{errors.subtitle && 'A subtitle is required.'}
</Form.Item>
<Form.Item>
<Button type="secondary" htmlType="submit">
Next
</Button>
</Form.Item>
</Form>
</Content>
</div>
);
};
export default withRouter(General);
react-hook-form author here. Antd Input component doesn't really expose inner ref, so you will have to register during useEffect, and update value during onChange, eg:
const { register, setValue } = useForm();
useEffect(() => {
register({ name: 'yourField' }, { required: true });
}, [])
<Input name="yourField" onChange={(e) => setValue('yourField', e.target.value)}
i have built a wrapper component to make antd component integration easier: https://github.com/react-hook-form/react-hook-form-input
import React from 'react';
import useForm from 'react-hook-form';
import { RHFInput } from 'react-hook-form-input';
import Select from 'react-select';
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' },
];
function App() {
const { handleSubmit, register, setValue, reset } = useForm();
return (
<form onSubmit={handleSubmit(data => console.log(data))}>
<RHFInput
as={<Select options={options} />}
rules={{ required: true }}
name="reactSelect"
register={register}
setValue={setValue}
/>
<button
type="button"
onClick={() => {
reset({
reactSelect: '',
});
}}
>
Reset Form
</button>
<button>submit</button>
</form>
);
}
This is my working approach:
const Example = () => {
const { control, handleSubmit, errors } = useForm()
const onSubmit = data => console.log(data)
console.log(errors)
return (
<Form onSubmit={handleSubmit(onSubmit)}>
<Controller
name="email"
control={control}
rules={{ required: "Please enter your email address" }}
as={
<Form.Item
label="name"
validateStatus={errors.email && "error"}
help={errors.email && errors.email.message}
>
<Input />
</Form.Item>
}
/>
<Button htmlType="submit">Submit</Button>
</Form>
)
}
On writing such code:
<Input
name="subtitle"
placeholder="Add a subtitle"
ref={register({ required: true })}
/>
You assume that Input reference is bound to input, but that's not true.
In fact, you need to bind it to inputRef.input.
You can check it with the next code:
const App = () => {
const inputRef = useRef();
const inputRefHtml = useRef();
useEffect(() => {
console.log(inputRef.current);
console.log(inputRefHtml.current);
});
return (
<FlexBox>
<Input ref={inputRef} />
<input ref={inputRefHtml} />
</FlexBox>
);
};
# Logs
Input {props: Object, context: Object, refs: Object, updater: Object, saveClearableInput: function ()…}
<input></input>
Note that antd is a complete UI library (using 3rd party "helpers" should "turn a red light"), in particular, Form has a validator implemented, you can see a variety of examples in docs.
In Ant Design v4.x + react-hook-form v6.x. We can implement as normally
import { useForm, Controller, SubmitHandler } from 'react-hook-form';
import * as yup from 'yup';
import { yupResolver } from '#hookform/resolvers/yup';
import { useIntl } from 'react-intl';
import { Input, Button, Form } from 'antd';
const SignInSchema = yup.object().shape({
email: yup.string().email().required(),
password: yup.string().required('required').min(6, 'passwordMin'),
});
interface PropTypes {
defaultValues?: {
email: string;
password: string;
};
handleFormSubmit: SubmitHandler<{ email: string; password: string }>;
}
function SignInForm({ defaultValues, handleFormSubmit }: PropTypes) {
const intl = useIntl();
const { handleSubmit, control, errors } = useForm({
defaultValues,
resolver: yupResolver(SignInSchema),
});
return (
<Form onFinish={handleSubmit(handleFormSubmit)}>
<Form.Item
validateStatus={errors && errors['email'] ? 'error' : ''}
help={errors.email?.message}
>
<Controller
as={Input}
name="email"
autoComplete="email"
control={control}
placeholder={intl.formatMessage({ id: 'AUTH_INPUT_EMAIL' })}
/>
</Form.Item>
<Form.Item
validateStatus={errors && errors['password'] ? 'error' : ''}
help={errors.password?.message}
>
<Controller
as={Input}
name="password"
type="password"
control={control}
autoComplete="new-password"
defaultValue=""
placeholder={intl.formatMessage({ id: 'AUTH_INPUT_PASSWORD' })}
/>
</Form.Item>
<Button type="primary" htmlType="submit">
{intl.formatMessage({ id: 'SIGN_IN_SUBMIT_BUTTON' })}
</Button>
</Form>
);
}
export default SignInForm;
In case anyone is still interested in getting close to the default styles of the Form Inputs provided by Ant, here's how I got it to work:
import { Form, Button, Input } from 'antd';
import { useForm, Controller } from 'react-hook-form';
function MyForm() {
const { control, handleSubmit, errors, setValue } = useForm();
const emailError = errors.email && 'Enter your email address';
const onSubmit = data => { console.log(data) };
const EmailInput = (
<Form.Item>
<Input
type="email"
placeholder="Email"
onChange={e => setValue('email', e.target.value, true)}
onBlur={e => setValue('email', e.target.value, true)}
/>
</Form.Item>
);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
as={EmailInput}
name="email"
control={control}
defaultValue=""
rules={{
required: true
}}
validateStatus={emailError ? 'error' : ''}
help={emailError}
/>
<Button block type="primary" htmlType="submit">
Submit
</Button>
</form>
);
}
Codesandbox sample

I have a form component I am passing to App.js but I am getting this error

Getting this error when render the app.
SignIn(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
SignIn.js
import React, { useState, useRef } from "react";
import axios from "axios";
import swal from "sweetalert";
import "./SignUp.css";
import image from './img/signup.jpg'
const SignIn = () => {
const [creds, setCreds] = useState({ userName: "", password: "" });
const usernameFieldRef = useRef();
const passwordFieldRef = useRef();
const changeHandler = e => {
setCreds({ ...creds, [e.target.name]: e.target.value });
};
const handleSubmit = event => {
event.preventDefault();
console.log('login');
const username = usernameFieldRef.current.value;
const password = passwordFieldRef.current.value;
axios
.post("https://foodiefun-api.herokuapp.com/api/auth/login", {
username,
password
})
.then(res => {
console.log("DATA", res.data)
localStorage.setItem('token', res.data.token)
swal(
"Welcome to Foodie Fun!",
"You are now signed in, enjoy your stay!",
"success"
);
})
.catch(err => {
console.log('LOGIN FAILED', err.response); // There was an error creating the data and logs to console
});
return (
<div class="page-content">
<div class="form-v6-content">
<div class="form-left">
<img className="form-image" style={{ linearGradient: "red, blue", opacity: ".6" }} src={image} alt="form" />
</div>
<form
onSubmit={handleSubmit}
class="form-detail"
method="post">
<h2>Register Form</h2>
<div class="form-row">
<input
ref={usernameFieldRef}
onChange={changeHandler}
// value={creds.userName}
type="text"
name="id"
id="user-name"
class="input-text"
placeholder="Enter Desired User Name"
required />
</div>
<div class="form-row">
<input
ref={passwordFieldRef}
onChange={changeHandler}
value={creds.password}
type="password"
name="password"
id="password"
class="input-text"
placeholder="Password"
required />
</div>
<div class="form-row">
<input
type="password"
name="comfirm-password"
id="comfirm-password"
class="input-text"
placeholder="Comfirm Password"
required />
</div>
<div class="form-row-last">
<input type="submit"
name="register"
class="register"
value="Register" />
</div>
</form>
</div>
</div>
)
}
}
export default SignIn
App.js
import React, { useState } from "react";
import "./App.css";
import Form from "./Components/SignIn";
import ReviewForm from "./Components/ReviewForm/ReviewForm";
import UserInfo from "./Components/userInfo";
import mockarray from "./Components/mockarray";
import Navbar from "./Components/Navbar";
import RecipeApp from "./recipes/RecipeApp";
import SignUp from "./Components/SignUp";
import SignIn from "./Components/SignIn";
const App = () => {
const [reviews, setReviews] = useState([]);
const addReview = restaurant => {
setReviews([...reviews, { ...restaurant, id: Date.now() }]);
};
return (
<div className="App">
<Navbar />
<SignUp />
<SignIn />
{/* <RecipeApp /> */}
{/* <ReviewForm /> */}
{/* <ReviewForm addReview={addReview} /> */}
{console.log(reviews)}
{/* <UserInfo data = {mockarray} /> */}
</div>
);
};
export default App;
Two forms should be rendering on the screen but when I comment in the SignIn page I get this error. A couple of people have looked at it and they both say everything is right. I'm stumped!
Your return statement is inside the handleSubmit function.
You need to return your state outside handleSubmit function :
import React, { useState, useRef } from "react";
import axios from "axios";
import swal from "sweetalert";
import "./SignUp.css";
import image from './img/signup.jpg'
const SignIn = () => {
const [creds, setCreds] = useState({ userName: "", password: "" });
const usernameFieldRef = useRef();
const passwordFieldRef = useRef();
const changeHandler = e => {
setCreds({ ...creds, [e.target.name]: e.target.value });
};
const handleSubmit = event => {
event.preventDefault();
console.log('login');
const username = usernameFieldRef.current.value;
const password = passwordFieldRef.current.value;
axios
.post("https://foodiefun-api.herokuapp.com/api/auth/login", {
username,
password
})
.then(res => {
console.log("DATA", res.data)
localStorage.setItem('token', res.data.token)
swal(
"Welcome to Foodie Fun!",
"You are now signed in, enjoy your stay!",
"success"
);
})
.catch(err => {
console.log('LOGIN FAILED', err.response); // There was an error creating the data and logs to console
});
}
return (
<div class="page-content">
<div class="form-v6-content">
<div class="form-left">
<img className="form-image" style={{ linearGradient: "red, blue", opacity: ".6" }} src={image} alt="form" />
</div>
<form
onSubmit={handleSubmit}
class="form-detail"
method="post">
<h2>Register Form</h2>
<div class="form-row">
<input
ref={usernameFieldRef}
onChange={changeHandler}
// value={creds.userName}
type="text"
name="id"
id="user-name"
class="input-text"
placeholder="Enter Desired User Name"
required />
</div>
<div class="form-row">
<input
ref={passwordFieldRef}
onChange={changeHandler}
value={creds.password}
type="password"
name="password"
id="password"
class="input-text"
placeholder="Password"
required />
</div>
<div class="form-row">
<input
type="password"
name="comfirm-password"
id="comfirm-password"
class="input-text"
placeholder="Comfirm Password"
required />
</div>
<div class="form-row-last">
<input type="submit"
name="register"
class="register"
value="Register" />
</div>
</form>
</div>
</div>
)
}
export default SignIn
Working code : https://codesandbox.io/s/divine-field-kkfd6
Not sure if it's a typo on the code of your question or in your acctual code but the return is inside handleSubmit.
import React, { useState, useRef } from 'react'
import axios from 'axios'
import swal from 'sweetalert'
import './SignUp.css'
import image from './img/signup.jpg'
const SignIn = () => {
const [creds, setCreds] = useState({ userName: '', password: '' })
const usernameFieldRef = useRef()
const passwordFieldRef = useRef()
const changeHandler = e => {
setCreds({ ...creds, [e.target.name]: e.target.value })
}
const handleSubmit = event => {
event.preventDefault()
console.log('login')
const username = usernameFieldRef.current.value
const password = passwordFieldRef.current.value
axios
.post('https://foodiefun-api.herokuapp.com/api/auth/login', {
username,
password
})
.then(res => {
console.log('DATA', res.data)
localStorage.setItem('token', res.data.token)
swal(
'Welcome to Foodie Fun!',
'You are now signed in, enjoy your stay!',
'success'
)
})
.catch(err => {
console.log('LOGIN FAILED', err.response) // There was an error creating the data and logs to console
})
}
return (
<div className="page-content">
<div className="form-v6-content">
<div className="form-left">
<img
className="form-image"
style={{ linearGradient: 'red, blue', opacity: '.6' }}
src={image}
alt="form"
/>
</div>
<form onSubmit={handleSubmit} className="form-detail" method="post">
<h2>Register Form</h2>
<div className="form-row">
<input
ref={usernameFieldRef}
onChange={changeHandler}
// value={creds.userName}
type="text"
name="id"
id="user-name"
className="input-text"
placeholder="Enter Desired User Name"
required
/>
</div>
<div className="form-row">
<input
ref={passwordFieldRef}
onChange={changeHandler}
value={creds.password}
type="password"
name="password"
id="password"
className="input-text"
placeholder="Password"
required
/>
</div>
<div className="form-row">
<input
type="password"
name="comfirm-password"
id="comfirm-password"
className="input-text"
placeholder="Comfirm Password"
required
/>
</div>
<div className="form-row-last">
<input
type="submit"
name="register"
className="register"
value="Register"
/>
</div>
</form>
</div>
</div>
)
}
export default SignIn

Where to put proptypes validation?

Im my react application, Im in the refactoring phase. I want to separate redux part from the components. Im confused about where to put proptypes validation? Should the redux props be validated in the container file, and component props should be validated on the component? Or should the both prop types be handled on the component? Or in container? Here is my code.
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import PropTypes from 'prop-types';
import {bindActionCreators} from 'redux';
import { connect } from 'react-redux';
import { registerUser } from '../../../actions';
import { TextFieldGroup } from '../../../components/UI';
class RegisterScreen extends Component {
state = {
name: '',
email: '',
password: '',
password2: '',
errors: {}
};
componentDidMount() {
if (this.props.auth.isAuthenticated) {
this.props.history.push('./dashboard');
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.errors) {
this.setState({ errors: nextProps.errors });
}
}
onChange = e => {
this.setState({
[e.target.name]: e.target.value
});
}
onSubmit = e => {
e.preventDefault();
const newUser = {
name: this.state.name,
email: this.state.email,
password: this.state.password,
password2: this.state.password2
}
this.props.registerUser(newUser, this.props.history);
}
render() {
const { errors } = this.state;
return (
<div className='register'>
<div className='container'>
<div className='row'>
<div className='col-md-8 m-auto'>
<h1 className='display-4 text-center'>Sign Up</h1>
<p className='lead text-center'>
Create Your Developer Connector Account
</p>
<form noValidate onSubmit={this.onSubmit}>
<TextFieldGroup
className={errors.email}
placeholder="* Full Name"
name="name"
value={this.state.name}
onChange={this.onChange}
error={errors.name}
/>
<TextFieldGroup
type='email'
className={errors.email}
placeholder="* Email Address"
name="email"
value={this.state.email}
onChange={this.onChange}
error={errors.email}
info='This site uses Gravatar so if you want a profile image, use a Gravatar email'
/>
<TextFieldGroup
type='password'
className={errors.password}
placeholder="* Password"
name="password"
value={this.state.password}
onChange={this.onChange}
error={errors.password}
/>
<TextFieldGroup
type='password'
className={errors.password2}
placeholder="* Confirm Password"
name="password2"
value={this.state.password2}
onChange={this.onChange}
error={errors.password2}
/>
<input type='submit' className='btn btn-info btn-block mt-4' />
</form>
</div>
</div>
</div>
</div>
);
}
}
RegisterScreen.propTypes = {
registerUser: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired
}
const mapStateToProps = state => ({
auth: state.auth,
errors: state.errors
});
const mapDispatchToProps = dispatch => ({
registerUser: bindActionCreators(registerUser, dispatch)
});
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(RegisterScreen));
You should define the PropTypes in the component as you have in the example there. When deciding when to use PropTypes for static typing, you should always guard against incoming props that can directly affect the desired result of the component when rendered. By that I mean, if the component requires "auth" or "errors" props, then you should define the PropTypes for the component receiving those properties, whether through HoC in this case redux or any other mode of passing data down.

Resources