I have this two codes
import React, { useState, useEffect, createContext } from "react";
import { useNavigate } from "react-router-dom";
import { api, createSession } from "./Services/api"
export const AuthContext = createContext();
export const AuthProvider = ({children}) => {
const navigate = useNavigate();
const [user, setUser] = useState();
const [loading, setLoading] = useState(true)
useEffect(() => {
const recoveredUser = localStorage.getItem("user")
if(recoveredUser){
setUser(JSON.parse(recoveredUser))
}
setLoading(false);
}, []);
const refreshPage = ()=>{
window.location.reload();
}
const login = async (username, pass) => {
const response = await createSession(username, pass);
const loggedUser = response.data.user;
const token = response.data.jwt;
localStorage.setItem("user", JSON.stringify(loggedUser))
localStorage.setItem("token", token)
api.defaults.headers.Authorization = {
"identifier": username,
"password": pass
}
setUser(loggedUser)
navigate("/");
};
const logout = () => {
localStorage.clear();
api.defaults.headers.Authorization = null;
setUser(null);
navigate("/login")
refreshPage();
};
return(
<AuthContext.Provider value={{authenticated: !!user, user, login, logout, loading}}> {children} </AuthContext.Provider>
)
}
and this
import axios from "axios";
export const api = axios.create({
baseURL: "https://communicationadmin.grupostra.com",
});
export const createSession = async(identifier, password) => {
return api.post('/auth/local', {identifier, password});
}
and this is my login screen
import Input from '../components/Input';
import styles from './Login.module.css';
import { useState, useContext } from 'react';
import { AuthContext } from '../Auth'
import logo from '../img/grupostra_horizontal.png'
import openEye from '../img/eye.png'
import closedEye from '../img/closedeye.png'
function Login(){
const { login } = useContext(AuthContext);
const [eyeImg, setEyeImg] = useState(openEye);
function handdleClick(){
if(eyeImg === openEye){
setEyeImg(closedEye);
document.querySelector('input:nth-child(4)').type = "";
} else {
setEyeImg(openEye)
document.querySelector('input:nth-child(4)').type = "password";
}
}
function handleSubmit(e){
e.preventDefault();
login(user, pass);
}
const [user, setUser] = useState('');
const [pass, setPass] = useState('');
return(
<div className={styles.container}>
<div className={styles.form_container}>
<img src={logo} alt="group" srcset="" />
<p>Welcome</p>
<form onSubmit={handleSubmit}>
<label htmlFor="username">Usuário</label>
<Input type="text" name="username" id="username" placeholder="User" setProps={setUser} value={user}/>
<label htmlFor="password">Senha</label>
<Input type="password" name="password" id="password" placeholder="Pass" setProps={setPass} value={pass}/>
<span onClick={handdleClick}><img src={eyeImg} className={styles.eyeImg} /></span>
<Input className="btn-hover" type="submit" name="submit" id="submit" value="Enter" />
</form>
</div>
</div>
)
}
export default Login
My question is how can or where a write an alert to show to users when your credentials are wrong? I tried some things, but i don't have sucess . (I want to show in a login screen) ........................................................................................
If the server handle the "wrong credentials" as an HTTP Code 4XX or 5XX then you can just catch the exception an do something about it
async handleSubmit(e){
e.preventDefault();
await login(user, pass).catch(exp => alert("Something happened, maybe invalid credentials"))
}
can you check your api response when it is giving error .when we are creating login api's if any error is there we are adding exceptions handlers to show those messages .if that is the case use below code
*
import Input from '../components/Input';
import styles from './Login.module.css';
import { useState, useContext } from 'react';
import { AuthContext } from '../Auth'
import logo from '../img/grupostra_horizontal.png'
import openEye from '../img/eye.png'
import closedEye from '../img/closedeye.png'
function Login(){
const { login } = useContext(AuthContext);
const [eyeImg, setEyeImg] = useState(openEye);
const [error,seterror]=useState('')
function handdleClick(){
if(eyeImg === openEye){
setEyeImg(closedEye);
document.querySelector('input:nth-child(4)').type = "";
} else {
setEyeImg(openEye)
document.querySelector('input:nth-child(4)').type = "password";
}
}
async function handleSubmit(e){
try
{
e.preventDefault();
awiat login(user, pass);
} catch(error){
seterror(error.message.toString())
}
}
const [user, setUser] = useState('');
const [pass, setPass] = useState('');
return(
<div className={styles.container}>
<div className={styles.form_container}>
<img src={logo} alt="group" srcset="" />
<p>Welcome</p>
<p>{error}</P>
<form onSubmit={handleSubmit}>
<label htmlFor="username">Usuário</label>
<Input type="text" name="username" id="username" placeholder="User" setProps={setUser} value={user}/>
<label htmlFor="password">Senha</label>
<Input type="password" name="password" id="password" placeholder="Pass" setProps={setPass} value={pass}/>
<span onClick={handdleClick}><img src={eyeImg} className={styles.eyeImg} /></span>
<Input className="btn-hover" type="submit" name="submit" id="submit" value="Enter" />
</form>
</div>
</div>
)
}
export default Login
Related
I created a custom useAxios hook to make api calls.
I am trying to use this in my login component as below.
import { useContext, useState } from 'react';
import './login.scss';
import { useNavigate } from 'react-router-dom';
import { useAxios } from '../../api/use-axios';
import ApiConfig from '../../api/api-config';
import { AuthContext } from '../../context/AuthContext';
const LOGIN_AXIOS_CONFIG = ApiConfig.AUTH.LOGIN;
const Login = () => {
const [loginError, setLoginError] = useState('');
const [phone, setPhone] = useState('');
const [password, setPassword] = useState('');
const navigate = useNavigate();
const { dispatch } = useContext(AuthContext);
const handleLogin = (e) => {
e.preventDefault();
LOGIN_AXIOS_CONFIG.data = {
phone,
password,
};
const { response: loginData, error } = useAxios(LOGIN_AXIOS_CONFIG);
if (error) {
setLoginError(error);
}
if (loginData) {
dispatch({ type: 'LOGIN', payload: loginData });
navigate('/');
}
};
return (
<div className="login">
<div className="logo">
<h1>LOGO</h1>
<h3>LOGO DESCRIPTION</h3>
</div>
<form onSubmit={handleLogin}>
<input type="number" placeholder="phone" onChange={(e) => setPhone(e.target.value)} />
<input type="password" placeholder="password" onChange={(e) => setPassword(e.target.value)} />
<button type="submit">Submit</button>
{loginError && <span>{loginError}</span>}
</form>
</div>
);
};
export default Login;
use-axios.js
import { useState, useEffect } from 'react';
import axios from 'axios';
export const useAxios = (axiosParams) => {
const [response, setResponse] = useState(undefined);
const [error, setError] = useState('');
const [loading, setLoading] = useState(true);
const fetchData = async (params) => {
try {
const result = await axios.request({
...params,
method: params.method || 'GET',
headers: {
accept: 'application/json',
authorization:
'Bearer my-token',
},
});
console.log(result.data);
setResponse(result.data);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData(axiosParams);
}, [axiosParams]); // execute once only
return { response, error, loading };
};
Is react seeing use prefix and giving error?
How can I fix this?
You need to make a slight change to your custom hook useAxios since hooks can be invoked only in the body of the component and not conditionally. I add a param to that hook to handle the auto-fetch. In this case you don't want it to trigger automatically the fetch, but imperatively, so just return the fetcher function from the hook and use it imperatively. It would be better not to couple the hook btw and create two separate hooks, something like useAxiosOnMount, useAxiosOnAction. For simplicity I'll just edit your useAxios hook here:
const Login = () => {
const [loginError, setLoginError] = useState('');
const [phone, setPhone] = useState('');
const [password, setPassword] = useState('');
const navigate = useNavigate();
const { dispatch } = useContext(AuthContext);
const {fetch, loading, response, error} = useAxios(LOGIN_AXIOS_CONFIG, false)
useEffect(() => {
if (response) // do something
if (error) // do something else
},[response,error])
const handleLogin = (e) => {
e.preventDefault();
fetch()
};
return (
<div className="login">
<div className="logo">
<h1>LOGO</h1>
<h3>LOGO DESCRIPTION</h3>
</div>
<form onSubmit={handleLogin}>
<input type="number" placeholder="phone" onChange={(e) => setPhone(e.target.value)} />
<input type="password" placeholder="password" onChange={(e) => setPassword(e.target.value)} />
<button type="submit">Submit</button>
{loginError && <span>{loginError}</span>}
</form>
</div>
);
};
export default Login;
use-axios.js
export const useAxios = (axiosParams, isAuto) => {
const [response, setResponse] = useState(undefined);
const [error, setError] = useState('');
const [loading, setLoading] = useState(true);
const fetchData = async (params) => {
try {
const result = await axios.request({
...params,
method: params.method || 'GET',
headers: {
accept: 'application/json',
authorization:
'Bearer my-token',
},
});
console.log(result.data);
setResponse(result.data);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
useEffect(() => {
if (isAuto)
fetchData(axiosParams);
}, [axiosParams, isAuto]); // execute once only
return { fetch: () => fetchData(axiosParams), response, error, loading };
};
useAxios, as you designed it, is a hook. And a hook, should follow some rules, known as Rules of Hooks, and here is an overview:
It should only get called a the to level of a React component or at the top level of a hook, not in a function inside a hook or a component like you did, not inside any other block, like if-else...
An approche for your case may be something like this:
import { useContext, useState, useEffect } from 'react';
import './login.scss';
import { useNavigate } from 'react-router-dom';
import { useAxios } from '../../api/use-axios';
import ApiConfig from '../../api/api-config';
import { AuthContext } from '../../context/AuthContext';
const LOGIN_AXIOS_CONFIG = ApiConfig.AUTH.LOGIN;
const Login = () => {
const [loginAxioConfig, setLogicAxiosConfig]=useState(null);
const [loginError, setLoginError] = useState('');
const [phone, setPhone] = useState('');
const [password, setPassword] = useState('');
const { response: loginData, error } = useAxios(loginAxioConfig);
const navigate = useNavigate();
const { dispatch } = useContext(AuthContext);
const handleLogin = (e) => {
e.preventDefault();
setLogicAxiosConfig({...LOGIN_AXIOS_CONFIG, data = {
phone,
password,
} });
};
useEffect(()=>{
if (error) {
setLoginError(error);
}
},[error]);
useEffect(()=>{
if (loginData) {
dispatch({ type: 'LOGIN', payload: loginData });
navigate('/');
}
},[loginData])
return (
<div className="login">
<div className="logo">
<h1>LOGO</h1>
<h3>LOGO DESCRIPTION</h3>
</div>
<form onSubmit={handleLogin}>
<input type="number" placeholder="phone" onChange={(e) => setPhone(e.target.value)} />
<input type="password" placeholder="password" onChange={(e) => setPassword(e.target.value)} />
<button type="submit">Submit</button>
{loginError && <span>{loginError}</span>}
</form>
</div>
);
};
export default Login;
import { useState, useEffect } from 'react';
import axios from 'axios';
export const useAxios = (axiosParams) => {
const [response, setResponse] = useState(undefined);
const [error, setError] = useState('');
const [loading, setLoading] = useState(true);
const fetchData = async (params) => {
try {
const result = await axios.request({
...params,
method: params.method || 'GET',
headers: {
accept: 'application/json',
authorization:
'Bearer my-token',
},
});
console.log(result.data);
setResponse(result.data);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
useEffect(() => {
if(!axiosParams) return;
fetchData(axiosParams);
}, [axiosParams]);
return { response, error, loading };
};
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { Button } from 'react-bootstrap';
import NavBarManu from './NavBarManu'
const Login = () => {
const [name, setName] = useState("");
const [password, setPassword] = useState("");
function login() {
fetch("http://localhost:3000/login?q=" + name).then((data) => {
data.json().then((resp) => {
console.warn("resp", resp)
if (resp.length > 0) {
localStorage.setItem('login', JSON.stringify(resp))
//Facing error
console.warn(this.props.history.push('list'))
}
else {
alert("Please check username and password")
}
})
})
}
return (
<div>
<br /><h2>Please Login !</h2><br />
<input type="text"
placeholder="enter name"
name="user" onChange={(event) => setName(event.target.value)} /> <br /> <br />
<input
placeholder="enter password"
type="password" name="password" onChange={(event) => setPassword(event.target.value)} /> <br /> <br />
<button onClick={() => { login() }} >Login</button>
</div>
);
};
export default Login;
I am facing error in console.warn(this.props.history.push('list')).
The syntax is for class component. That's why it is showing error. I am facing some difficulty in implementing it in functional components.
After the user press login button i want the page to be directed towards "list" page.
Please someone can point out my error or If there is any other better way to approach this then do advice me .
You are creating functional component and using class component syntax. Your props are empty.
this key word is used in class components to reference to state.
It should be like this:
console.warn(history.push('list'))
You have to import:
import { useHistory } from "react-router-dom";
And in Login component body:
import { useHistory } from "react-router-dom";
const Login = () => {
const [name, setName] = useState("");
const [password, setPassword] = useState("");
const history = useHistory()
function login() {
fetch("http://localhost:3000/login?q=" + name).then((data) => {
data.json().then((resp) => {
console.warn("resp", resp)
if (resp.length > 0) {
localStorage.setItem('login', JSON.stringify(resp))
//Facing error
console.warn(history.push('list'))
}
else {
alert("Please check username and password")
}
})
})
}
return ...
I would to add geolocation.getCurrentPosition in my code below using react and redux and then send the coordinates (Lat and Long) to the Local Storage but i don't have any idea !
I deleted the HTML parts of the form (city, postalCode .. to avoid repetition
Anyone can help me with please
import React, { useState } from 'react';
import { Form, Button } from 'react-bootstrap';
import { useDispatch, useSelector } from 'react-redux';
import FormContainer from '../components/FormContainer';
import CheckoutSteps from '../components/CheckoutSteps';
import { saveShippingAddress } from '../actions/cartActions';
const ShippingScreen = ({ history }) => {
const cart = useSelector((state) => state.cart);
const { shippingAddress } = cart;
const [address, setAddress] = useState(shippingAddress.address);
const [city, setCity] = useState(shippingAddress.city);
const [postalCode, setPostalCode] = useState(shippingAddress.postalCode);
const [country, setCountry] = useState(shippingAddress.country);
const dispatch = useDispatch();
const submitHandler = (e) => {
e.preventDefault();
dispatch(
saveShippingAddress({ address, city, postalCode, country })
);
history.push('/payment');
};
return (
<FormContainer>
<CheckoutSteps step1 step2 />
<h1>Shipping</h1>
<Form onSubmit={submitHandler}>
<Form.Group controlId="address">
<Form.Label>Address</Form.Label>
<Form.Control
type="text"
placeholder="Enter address"
value={address}
required
onChange={(e) => setAddress(e.target.value)}
></Form.Control>
</Form.Group>
<Button type="submit" variant="primary">
Continue
</Button>
</Form>
</FormContainer>
);
};
export default ShippingScreen;
Currently, when a user signs up they are being created as a user in the firebase authentication. I am trying to add that newly created user directly into a firestore collection upon creation.
The following is the AuthContext.js
import React, { useContext, useState, useEffect } from "react";
import { auth } from "../firebase";
const AuthContext = React.createContext();
export function useAuth() {
return useContext(AuthContext);
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState();
const [loading, setLoading] = useState(true);
function signup (email, password) {
return auth.createUserWithEmailAndPassword(email, password);
}
function login(email, password) {
return auth.signInWithEmailAndPassword(email, password);
}
function logout() {
return auth.signOut();
}
function resetPassword(email) {
return auth.sendPasswordResetEmail(email);
}
function updateEmail(email) {
return currentUser.updateEmail(email);
}
function updatePassword(password) {
return currentUser.updatePassword(password);
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((user) => {
setCurrentUser(user);
setLoading(false);
});
return unsubscribe;
}, []);
const value = {
currentUser,
login,
signup,
logout,
resetPassword,
updateEmail,
updatePassword,
};
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
);
}
The following is Signup.js
import React, { useRef, useState } from 'react'
import { Form, Button, Card, Alert } from "react-bootstrap"
import { Link, useHistory } from "react-router-dom"
import { useAuth } from './contexts/AuthContext'
export default function Signup() {
const emailRef = useRef()
const passwordRef = useRef()
const passwordConfirmRef = useRef()
const { signup } = useAuth()
const [error, setError] = useState("")
const [loading, setLoading] = useState(false)
const history = useHistory()
async function handleSubmit(e){
e.preventDefault()
if (passwordRef.current.value !==
passwordConfirmRef.current.value) {
return setError('Passwords do not match')
}
try{
setError('')
setLoading(true)
await signup(emailRef.current.value,passwordRef.current.value)
history.push("/")
} catch {
setError('Failed to create an account')
}
setLoading(false)
}
return (
<>
<Card>
<Card.Body>
<h2 className="text-center mb-4">Sign Up</h2>
{error && <Alert variant="danger">{error}</Alert>}
<Form onSubmit={handleSubmit}>
<Form.Group id="email">
<Form.Label>Email</Form.Label>
<Form.Control type="email" ref={emailRef} required />
</Form.Group>
<Form.Group id="password">
<Form.Label>Password</Form.Label>
<Form.Control type="password" ref={passwordRef} required />
</Form.Group>
<Form.Group id="password-confirm">
<Form.Label>Password Confirmation</Form.Label>
<Form.Control type="password" ref={passwordConfirmRef} required />
</Form.Group>
<Button disabled={loading} className="w-100" type="submit">
Sign Up
</Button>
</Form>
<div className="w-100 text-center mt-2">
Already have an account? <Link to="/login">Log In</Link>
</div>
</Card.Body>
</Card>
</>
)
}
Thank you in advance, any help will be greatly appreciated.
The creation of the user in Firebase Auth will happen no matter what you do, but you can take advantage of that by creating a Cloud Function that triggers everytime a user is created in Firebase auth and then create a new document in Firestore representing that user. Here is a Cloud Function example that does just that:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
exports.createUser = functions.auth.user().onCreate((user) => {
const { uid } = user;
const userCollection = db.collection('users');
userCollection.doc(uid).set({
someData: "123"
});
});
Also in the documentation you can see how to deploy Cloud Functions, in case you are not familiar with it.
I submit a request to the server and then want to get the result without reloading the page (SPA principle), how can this be done using useEffect()?
I tried to do something like this:
useEffect (() => {
addProduct ();
})
but it's was a bad idea
import React, {useState, useEffect} from 'react';
import api from './api';
const HandleProduct = () => {
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const updateName = (e) =>{
setName(e.target.value);
}
const updateDescription = (e) =>{
setDescription(e.target.value);
}
const addProduct = () =>{
const product = {
name: name,
description: description
}
api.addProduct(product)
.then((req, res) =>{
console.log(res);
})
}
return (
<div>
<form onSubmit={addProduct}>
<input type="text" name="name" value={name} onChange={updateName}/>
<input type="text" name="description" value={description} onChange={updateDescription}/>
<button>Submit</button>
</form>
</div>
);
}
export default HandleProduct;
When the callback with response is called you've got the repsonse with all data sent from API. Let's assume you want to get ID. I will add new hook for storing ID, setting it after POST method is completed, and displaying it.
const [productId, setProductId] = useState(null);
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const updateName = (e) =>{
setName(e.target.value);
}
const updateDescription = (e) =>{
setDescription(e.target.value);
}
onSubmit() {
const product = {
name: name,
description: description
}
api.addProduct(product)
.then((req, res) =>{
setProudctId(JSON.parse(res).id);
})
}
return (
<div>
{productId && <span>Your productId: {productId} </span>}
<form onSubmit={addProduct}>
<input type="text" name="name" value={name} onChange={updateName}/>
<input type="text" name="description" value={description} onChange={updateDescription}/>
<button>Submit</button>
</form>
</div>
);
}
export default HandleProduct;
Your code seems legit, yet, given that is not working, I'll give you another option to do it.
In App.js
<Router >
<ProductsProvider>
<Route exact path="/products" component={ProductsList} props={...props} />
<Route exact path={'/products/add'} component={HandleProduct}
props={...props} />
</ProductsProvider>
</Router>
In HandleProduct.js
import React, {useState} from 'react';
import api from './api';
import { Redirect } from 'react-router'
const HandleProduct = ({history}) => {
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const updateName = (e) =>{
setName(e.target.value);
}
const updateDescription = (e) =>{
setDescription(e.target.value);
}
const addProduct = (e) =>{
e.preventDefault();
const product = {
name: name,
description: description
}
api.addProduct(product)
.then((req, res) =>{
history.push('/products');
})
}
return (
<div>
<form onSubmit={addProduct}>
<input type="text" name="name" value={name} onChange={updateName}/>
<input type="text" name="description" value={description} onChange={updateDescription}/>
<button>Submit</button>
</form>
</div>
);
}
import React, {useContext} from 'react';
import {ProductsContext} from './ProductsContext';
const ProductsList = () => {
const [data] = useContext(ProductsContext);
return (
<div>
{console.log(data)}
{data.products.map((product, index)=>(
<div key={index}>
<p>{product.name}</p>
<p><i>{product.description}</i></p>
</div>
))}
</div>
);
}
export default ProductsList;