How to modify React component to use hooks? - reactjs

Below is a login component without hooks. This component has two input fields and a submit button. How can we modify this component to use hooks and convert this component into a functional component that can use states?
import React from 'react';
import { userService } from '../services/user.services';
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
username: '',
password: '',
submitted: false,
loading: false,
error: ''
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e) {
e.preventDefault();
this.setState({ submitted: true });
const data = this.state;
userService.login(data.username, data.password)
.then(
user => {
const { from } = this.props.location.state || { from: { pathname: "/" } };
this.props.history.push(from);
}
);
}
handleChange(e) {
const { name, value } = e.target;
this.setState({ [name]: value });
}
render() {
const { ...data } = this.state;
return (
<div className="login-box">
<h1>Travel With Us</h1>
<form onSubmit={this.handleSubmit}>
<div className="text-box">
<i className="fa fa-user"></i>
<input type="text" name="username" defaultValue={data.username} onChange={this.handleChange} placeholder="Username" />
</div>
<div className="text-box">
<i className="fa fa-lock" />
<input type="password" name="password" defaultValue={data.password} onChange={this.handleChange} placeholder="Passward" />
</div>
<button className="btn" value="login">Sign in</button>
</form>
</div>
);
}
}
export default Login;

Below is the sample code which I have converted to use hooks.
import React, { useState } from 'react';
import { userService } from '../services/user.services';
const LoginHooks = (props) => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [submitted, setSubmit] = useState(false);
let handleSubmit = function (e) {
e.preventDefault();
console.log(submitted);
setSubmit(true);
userService.login(username, password)
.then(
user => console.log(user)
);
};
return (
<div className="login-box">
<h1>Login</h1>
<form onSubmit={handleSubmit}>
<div className="text-box">
<i className="fa fa-user"></i>
<input type="text" name="username" defaultValue={username} onChange={({target}) => setUsername(target.value)} placeholder="Username" />
</div>
<div className="text-box">
<i className="fa fa-lock" />
<input type="password" name="password" defaultValue={password} onChange={({target}) => setPassword(target.value)} placeholder="Passward" />
</div>
<button className="btn" value="login">Sign in</button>
</form>
</div>
);
}
export default LoginHooks;

Related

Login is not a function after logging out then trying to log back in

I am building a react project where I want to implement a log-in/ log out screen.
I am able to succesfully log in but once I log out, I cannot log back in with the error "TypeError
Login is not a function" being displayed. It seems to be coming from LogInForm.js, from the Login(details); line. I cannot spot my mistake here so I would greatly appreciate any feedback. Cheers.
LogInForm.js
import React, { useState } from "react";
function LogInForm({ Login, error }) {
const [details, setDetails] = useState({ email: "", password: "" });
const submitHandler = (e) => {
e.preventDefault();
Login(details);
};
return (
<form onSubmit={submitHandler}>
<div className="form-inner">
<h2>Login</h2>
{/* ERROR!*/}
<div className="form-group">
<label htmlFor="email">Email:</label>
<input
type="email"
name="email"
id="email"
onChange={(e) => setDetails({ ...details, email: e.target.value })}
value={details.email}
/>
</div>
<div className="form-group">
<label htmlFor="password">Password:</label>
<input
type="password"
name="password"
id="password"
onChange={(e) =>
setDetails({ ...details, password: e.target.value })
}
value={details.password}
/>
</div>
<input type="submit" value="Log In" />
</div>
</form>
);
}
export default LogInForm;
LogIn.js
import React, { useState } from "react";
import App from "./App";
import LogInForm from "./LogInForm";
function LogIn() {
const adminUser = {
email: "admin#admin.com",
password: "test123"
};
const [user, setUser] = useState({ name: "", email: "" });
const [error, setError] = useState("");
const Login = (details) => {
if (
details.email == adminUser.email &&
details.password == adminUser.password
) {
console.log("Logged in!");
setUser({ email: details.email, password: details.password });
} else {
console.log("details dont match");
}
};
const Logout = () => {
console.log("Logout");
};
return (
<div className="Display">
{user.email != "" ? (
<App email={user.email} password={user.password} />
) : (
<LogInForm Login={Login} error={error} />
)}
</div>
);
}
export default LogIn;
index.js
import React from "react";
import ReactDOM from "react-dom";
import LogIn from "./LogIn";
ReactDOM.render(<LogIn />, document.getElementById("root"));
App.js
import React from "react";
import LogIn from "./LogIn";
import LogInForm from "./LogInForm";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
email: this.props.email,
password: this.props.password,
};
this.logOut = this.logOut.bind(this);
}
logOut() {
console.log("Logged out");
this.setState({ email: "", password: "" });
}
componentDidMount() {
console.log("Email:" + this.state.email);
console.log("Password:" + this.state.password);
}
render() {
if (this.state.email == "") {
return <LogInForm />;
} else {
{
console.log(this.state);
}
return (
<div>Hello</div>,
<button onClick={this.logOut}>Log Out</button>
);
}
}
}
export default App;
The issue is haven't passed Login function in App file. This could be done in a more simple way, like this.
function LogInForm({ handleLogin, user, setUser }) {
return (
<form onSubmit={handleLogin}>
<div className="form-inner">
<h2>Login</h2>
{/* ERROR!*/}
<div className="form-group">
<label htmlFor="email">Email:</label>
<input
type="email"
name="email"
id="email"
onChange={(e) => setUser({ ...user, email: e.target.value })}
value={user.email}
/>
</div>
<div className="form-group">
<label htmlFor="password">Password:</label>
<input
type="password"
name="password"
id="password"
onChange={(e) => setUser({ ...user, password: e.target.value })}
value={user.password}
/>
</div>
<input type="submit" value="Log In" />
</div>
</form>
);
}
const adminUser = {
email: "admin#admin.com",
password: "test123"
};
export default function App() {
const [user, setUser] = useState({ name: "", email: "" });
const [isAuthenticated, setIsAuthenticated] = useState(false);
const handleLogin = () => {
if (
user.email === adminUser.email &&
user.password === adminUser.password
) {
console.log("Logged in!");
setIsAuthenticated(true);
} else {
console.log("details dont match");
}
};
const handleLogout = () => {
setIsAuthenticated(false);
console.log("Logout");
};
return (
<div className="Display">
{isAuthenticated ? (
<button onClick={handleLogout}>logout</button>
) : (
<LogInForm
handleLogin={handleLogin}
user={user}
setUser={setUser}
/>
)}
</div>
);
}

How to use ComponentWillReceieveProp in React Hooks

I am trying to convert a class component into a functional component with hooks, but i am having a challenge on how to use an equivalent lifecycle of ComponentWillReceiveProps.
Log in form : Class Component:
class Login extends Component {
constructor() {
super();
this.state = {
emial:"",
password: "",
errors: {}
};
}
componentDidMount() {
window.history.pushState(null, document.title, window.location.href);
window.addEventListener('popstate', function (event){
window.history.pushState(null, document.title, window.location.href);
});
}
componentWillReceiveProps(nextProps) {// want to replace with hook equivalent
if (nextProps.auth.isAuthenticated) {
this.props.history.push("/recruitcentral"); // push user to dashboard when they login
}
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.props.username,
password: this.state.password
};
this.props.loginUser(userData);
};
render() {
const { errors } = this.state;
return (
<>
<Row>
<Col>
<div>
<h1>Login</h1>
<div className={styles.formWrapper}>
<form onSubmit={this.onSubmit} noValidate>
<div className="email">
<label htmlFor="email"></label>
<input
onChange={this.onChange}
value={this.state.email}
error={errors.email}
id="email"
type="email"
className={classnames("", {
invalid: errors.email || errors.emailnotfound
})}
/>
<div className="Input__line" />
<label htmlFor="email" >Enter your email</label>
<span className="red-text">
{errors.email}
{errors.emailnotfound}
</span>
</div>
<div className="input-field col s12">
<input
onChange={this.onChange}
value={this.state.password}
error={errors.password}
id="password"
type="password"
className={classnames("", {
invalid: errors.password || errors.passwordincorrect
})}
/>
<div className="Input__line" />
<label htmlFor="password">Password</label>
<span className="red-text">
{errors.password}
{errors.passwordincorrect}
</span>
</div>
<div className={styles.createAccount}>
<button type="submit">Log in</button>
</div>
<div className={styles.createAccount}>
<button onClick={()=> this.props.history.push("/register")}type="submit">Create Account</button>
</div>
</form>
</div>
</div>
</Col>
</Row>
</>
Login Form :Functional Component
function Login(props) {
const [inputs, setInputs] = useState({
email:'',
password: '',
errors: {}
});
const [submitted, setSubmitted] = useState(false);
const { username, password } = inputs;
const auth =useSelector(state=>state.auth)
const dispatch = useDispatch();
const location = useLocation();
// reset login status
useEffect(() => {
dispatch(logoutUser());
}, []);
useEffect(() => {
// code to perform same function as componentWillReceiveProps here
}
}, [])
I know useEffect() will be used, but i really dont know how to implement it
componentWillRecieveProps is for all practical purposes deprecated with the recommendation being to use the componentDidUpdate lifecycle method. useEffect hook with appropriate dependency is the functional component equivalent.
For the first side-effect, navigating to "/recruitcentral" when user is authenticated.
useEffect(() => {
if (auth.isAuthenticated) {
props.history.push("/recruitcentral");
}
}, [auth]); // <-- Note: may complain about missing dependency like `history`, you can add these

the state inside hooks are not updated for first time on form submit in react

I was trying to implement contactUS form in react using hooks.Contact us form is placed inside hooks.When I first submit the form the state in hooks are not updated ,when I click 2nd time states are set .and I am returning state to class component there api call are made.
//contactushook.js
import React, { useState } from 'react';
const ContactUshook = ({ parentCallBack }) => {
const [data, setData] = useState([]);
const handleSubmit = (event) => {
event.preventDefault();
setData({ name: document.getElementById('name').value, email: document.getElementById('email').value, message: document.getElementById('message').value });
console.log(data);
parentCallBack(data);
}
return <React.Fragment>
<div className="form-holder">
<form onSubmit={handleSubmit}>
<div>
<input id="name" type="text" placeholder="enter the name"></input>
</div>
<div>
<input id="email" type="email" placeholder="enter the email"></input>
</div>
<div>
<textarea id="message" placeholder="Type message here"></textarea>
</div>
<button type="submit" >Submit</button>
</form>
</div>
</React.Fragment >
}
export default ContactUshook;
//contactus.js
import React, { Component } from 'react';
import ContactUshook from './hooks/contactushook';
import '../contactUs/contactus.css';
class ContactComponent extends Component {
onSubmit = (data) => {
console.log('in onsubmit');
console.log(data);
}
render() {
return (
<div>
<h4>hook</h4>
<ContactUshook parentCallBack={this.onSubmit}></ContactUshook>
</div>
);
}
}
export default ContactComponent;
Stop using document queries and start using state instead!
Your ContactUshook component should look like this:
const ContactUshook = ({ parentCallBack }) => {
const [data, setData] = useState({ name: '', email: '', message: '' });
const handleSubmit = () => {
event.preventDefault();
parentCallBack(data);
}
const handleChange = (event, field) => {
const newData = { ...data };
newData[field] = event.target.value;
setData(newData);
}
return (
<div className="form-holder">
<form onSubmit={handleSubmit}>
<div>
<input
id="name"
type="text"
value={data.name}
placeholder="enter the name"
onChange={(e) => handleChange(e,'name')} />
</div>
<div>
<input
id="email"
type="email"
value={data.email}
placeholder="enter the email"
onChange={(e) => handleChange(e,'email')} />
</div>
<div>
<textarea
id="message"
value={data.message}
placeholder="Type message here"
onChange={(e) => handleChange(e,'message')} />
</div>
<button type="submit" >Submit</button>
</form>
</div>
);
}

React form with <Popup> alert message instead of alert()

I am trying to create a contact form in React. It sends the email. Everything is working fine. What I would like to achieve is to replace the alert('') with a better looking < Popup> message using react Popup package.
Here is the code:
import React, { Component } from 'react';
import '../css/Contact.css';
import axios from 'axios';
import Popup from "reactjs-popup";
class Contact extends Component {
constructor(props) {
super(props);
this.state = {
name: '',
email: '',
message: '',
isValid: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.validationCheck = this.validationCheck.bind(this);
}
handleChange = (event) => {
const name = event.target.id;
const value = event.target.value;
this.setState ({ [name] : value});
}
validationCheck () {
if((this.state.name.length)>3 &&
(this.state.message.length)>5 &&
(this.state.email.length)>7 &&
(this.state.email.includes("#")) &&
(this.state.email.includes(".")) &&
((this.state.name || this.state.message || this.state.message)!=0)
)
{
this.setState({ isValid: true});
this.forceUpdate();
};
}
async handleSubmit (event) {
this.validationCheck();
this.forceUpdate();
if ((this.state.isValid) === true) {
event.preventDefault();
const { name, email, message } = this.state;
const form = await axios.post('/api/form', {
name,
email,
message
});
alert('thank you for your message');
event.target.reset();
}
else {
event.preventDefault();
alert('you might have entered something wrong');
}
}
render() {
return (
<div className="contact">
<h1>Contact Me</h1>
<form id="form" onSubmit={this.handleSubmit}>
<div>
<label htmlFor="Name" className="badge badge-secondary">Name</label>
<input
onChange={this.handleChange} id="name" className="form-control" placeholder="Enter your name here."/>
</div>
<div>
<label htmlFor="Email" className="badge badge-secondary">Email address</label>
<input
onChange={this.handleChange}
id="email"
className="form-control"
placeholder="Enter your e-mail address here." />
</div>
<div className="">
<label htmlFor="Message" className="badge badge-secondary">Message</label>
<textarea
onChange={this.handleChange} rows="4"
className="form-control" id="message" placeholder="Enter your message here."></textarea>
</div>
<button
type="submit"
form="form"
className="btn btn-secondary btn-lg button"
id="submit"
value="Submit"> Send! </button>
</form>
<hr />
<button type="button" className="btn btn-outline-info" onClick={this.props.scrollToTop}>
Scroll me back to the top!</button>
</div>
);
}
}
export default Contact;
I have tried to replace 'alert('thank you for your message');' with return(< Popup>); but that is clearly not a way forward.
Here's a way how you can exactly use a reactjs-popup:
import React from "react";
import Popup from "reactjs-popup";
export default () => (
<Popup trigger={<button> Trigger</button>} position="right center">
<div>Popup content here !!</div>
</Popup>
);
All you need to do is put a trigger for it and it'll work
For help: https://react-popup.netlify.com/

Updating properties after a callback in react

I have the following code within Typescript React:
I am trying to set loggingIn = "true" when I get the call back from the component. This would allow the component to show an indicator that its logging in.
What is the best way to approach this?
Thank you in advance,
Marty
Login.tsx
import * as React from "react";
import * as ReactDOM from "react-dom";
import { LoginPanel } from "../Shared/LoginPanel.tsx";
let loggingIn: string = "false";
ReactDOM.render(
<LoginPanel loggingIn={ loggingIn } onLogin={
function (email:string, password:string) {
this.loggingIn = "true";
alert("Logging in with e-mail" + email + " and password " + password);
}.bind(this)
} />,
document.getElementById("loginpanel")
)
LoginPanel.tsx
import * as React from "react";
export interface Properties { loggingIn:string; onLogin: (email: string, password: string) => void; }
export class LoginPanel extends React.Component<Properties, {}> {
email: HTMLInputElement = null;
password: HTMLInputElement = null;
submit = (e: any) => {
e.preventDefault();
this.props.onLogin(this.email.value,this.password.value);
};
render() {
return <div className="row">
<div className="col-xs-3">
<h3>Log in with your email account</h3>
<form onSubmit={this.submit}>
<div className="form-group">
{ this.props.loggingIn }
<label htmlFor="email" className="sr-only">Email</label>
<input type="email" ref={(input) => { this.email = input; } } className="form-control" placeholder="somebody#example.com" />
</div>
<div className="form-group">
<label htmlFor="key" className="sr-only">Password</label>
<input type="password" ref={(input) => { this.password = input; } } className="form-control" placeholder="Password" />
</div>
<input type="submit" id="btn-login" className="btn btn-custom btn-lg btn-block" value="Log in" />
</form>
Forgot your password?
<hr />
</div>
</div>
}
}
Your Login.tsx should look like this:
let loggingIn: string = "false";
function onLogin(email: string, password: string) {
loggingIn = "true";
console.log(`logged in. email: ${ email }, password: ${ password }`);
}
ReactDOM.render(
<LoginPanel loggingIn={ loggingIn } onLogin={ onLogin } />, document.getElementById("loginpanel")
)
And the LoginPanel.tsx:
export interface Properties {
loggingIn: string;
onLogin: (email: string, password: string) => void;
}
interface State {
loggingIn: string;
}
export class LoginPanel extends React.Component<Properties, State> {
email: HTMLInputElement = null;
password: HTMLInputElement = null;
constructor(props: Properties) {
super(props);
this.state = {
loggingIn: props.loggingIn
};
}
submit = (e: React.FormEvent) => {
e.preventDefault();
this.props.onLogin(this.email.value, this.password.value);
};
render() {
return <div className="row">
<div className="col-xs-3">
<h3>Log in with your email account</h3>
<form onSubmit={ this.submit }>
<div className="form-group">
{ this.state.loggingIn }
<label htmlFor="email" className="sr-only">Email</label>
<input type="email" ref={(input) => { this.email = input; } } className="form-control" placeholder="somebody#example.com" />
</div>
<div className="form-group">
<label htmlFor="key" className="sr-only">Password</label>
<input type="password" ref={(input) => { this.password = input; } } className="form-control" placeholder="Password" />
</div>
<input type="submit" id="btn-login" className="btn btn-custom btn-lg btn-block" value="Log in" />
</form>
Forgot your password?
<hr />
</div>
</div>
}
}

Resources