i have created a login form where user need to input his email id and OTP. below is my code -
import { useState } from 'react';
import axios from '../api/axios';
const useLogin = () => {
const [user, setUser] = useState(false);
const auth = async (value, OTP) => {
let config = {
method: 'POST',
url: '/api/user/generateToken',
headers: {
Authorization: 'value'
},
data: {
username: value,
password: OTP
}
};
try {
const response = await axios(config);
if (response.data.Status === "Failure") {
throw response.data.Message;
} else {
setUser(true);
return { status: response.data.Status, isAuth: user }
}
} catch (err) {
setUser(false);
return { status: undefined, message: err, isAuth: user };
}
}
return { auth, user };
}
export default useLogin
Everything is working fine here only problem is when i'm calling this function in my component i'll receive isAuth always false. Below is my component code -
import React, { Fragment, useRef, useEffect, useState } from 'react';
import { useLocation, useHistory } from "react-router-dom";
import { css } from "#emotion/core";
import ScaleLoader from "react-spinners/ScaleLoader";
import '../css/login.css';
import '../css/common.css';
import logo from '../assets/engageLogo.png';
import * as Yup from 'yup';
import { useFormik } from 'formik';
import useLogin from './../hooks/useOTP';
const override = css`
display: block;
margin: 0 auto;
border-color: #fff;
`;
const OTP = () => {
const [loading, setLoading] = useState(false);
const [color] = useState("#ffffff");
const [APIResponse, setAPIResponse] = useState(false);
const [APIMessage, setAPIMessage] = useState('')
const login = useLogin();
const location = useLocation();
const history = useHistory();
const inputRef = useRef();
const readRef = useRef();
const buttonRef = useRef();
const schema = Yup.object({
otp: Yup.string().required("OTP is Required")
});
const handleChangeError = () => {
return setAPIResponse(false)
}
const {
handleSubmit,
handleChange,
handleBlur,
touched,
errors,
} = useFormik({
initialValues: {
otp: "",
},
validationSchema: schema,
onSubmit: (values) => {
console.log(JSON.stringify(values));
buttonRef.current.disabled = true;
setLoading(true);
const loginCall = login.auth(location.state.email, values.otp);
loginCall.then(response => {
if (response.status === undefined || response.status === null) {
setLoading(false);
buttonRef.current.disabled = false;
setAPIResponse(true)
setAPIMessage(response.message)
} else {
setLoading(false);
history.push({
pathname: '/dashboard',
state: { email: values.email }
});
}
})
},
});
useEffect(() => {
inputRef.current.focus();
readRef.current.value = location.state.email;
}, [location])
return <Fragment>
<div className="centered-form">
<div className="centered-form__box">
<div className="mb-3 text-center">
<img src={logo} className="img-fluid" width="150" alt="Logo" />
</div>
<form onSubmit={handleSubmit} noValidate>
<div className="mb-3">
<label htmlFor="readEmail" className="form-label">Email</label>
<input
type="text"
name="readEmail"
id="readEmail"
ref={readRef}
className="form-control" readOnly />
</div>
<div className="mb-3">
<label htmlFor="otp" className="form-label">OTP</label>
<input
type="text"
name="otp"
id="otp"
ref={inputRef}
onChange={(e) => { handleChange(e); handleChangeError(e) }}
onBlur={handleBlur}
className="form-control" placeholder="Enter OTP" required />
{touched.otp && errors.otp
? <div className="invalid-feedback">Please enter valid OTP</div>
: null}
{APIResponse
? <div className="invalid-feedback">{APIMessage}</div>
: null}
</div>
<div className="d-grid gap-2">
<button ref={buttonRef} className="btn btn-main">{loading ?
<ScaleLoader color={color} loading={loading} css={override} height={15} /> : <span>Login</span>}</button>
</div>
</form>
</div>
</div>
</Fragment>
}
export default OTP
in response of loginCall i'll always get isAuth: false.
I want to use isAuth for protecting my routes. Just to check whether user has logged in or not.
why setUser is not updating the value here.
thanks in advance...
That's because by the time you returning your isAuth value the new user value is not set yet. you need to know that React setState is asynchronous function.
just use the the boolean itself directly like this:
setUser(true);
return { status: response.data.Status, isAuth: true }
or in case of a rejection:
setUser(false);
return { status: undefined, message: err, isAuth: false };
Related
So I'm using the Firebase and React to make a chat.
And I need to notify the user when another user sends him a message in the chat.
Is there an easy way to do it.ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ
ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ
Input.js File with sending system.
import React, { useContext, useState } from "react";
import Img from "../../static/chat/add-img.png";
import Attach from "../../static/chat/attach.png";
import { AuthContext } from "./AuthContext";
import { ChatContext } from "./ChatContext";
import {
arrayUnion,
doc,
serverTimestamp,
Timestamp,
updateDoc,
} from "firebase/firestore";
import { db, storage } from "../../firebase";
import { v4 as uuid } from "uuid";
import { getDownloadURL, ref, uploadBytesResumable } from "firebase/storage";
const Input = () => {
const [text, setText] = useState("");
const [img, setImg] = useState(null);
const { currentUser } = useContext(AuthContext);
const { data } = useContext(ChatContext);
const handleSend = async () => {
if (img) {
const storageRef = ref(storage, uuid());
const uploadTask = uploadBytesResumable(storageRef, img);
uploadTask.on(
(error) => {
//TODO:Handle Error
},
() => {
getDownloadURL(uploadTask.snapshot.ref).then(async (downloadURL) => {
await updateDoc(doc(db, "chats", data.chatId), {
messages: arrayUnion({
id: uuid(),
text,
senderId: currentUser.uid,
date: Timestamp.now(),
img: downloadURL,
}),
});
});
}
);
} else {
await updateDoc(doc(db, "chats", data.chatId), {
messages: arrayUnion({
id: uuid(),
text,
senderId: currentUser.uid,
date: Timestamp.now(),
}),
});
}
await updateDoc(doc(db, "userChats", currentUser.uid), {
[data.chatId + ".lastMessage"]: {
text,
},
[data.chatId + ".date"]: serverTimestamp(),
});
await updateDoc(doc(db, "userChats", data.user.uid), {
[data.chatId + ".lastMessage"]: {
text,
},
[data.chatId + ".date"]: serverTimestamp(),
});
setText("");
setImg(null);
};
return (
<div className="input">
<input
type="text"
placeholder="Type something..."
onChange={(e) => setText(e.target.value)}
value={text}
/>
<div className="send">
<img src={Attach} alt="" />
<input
type="file"
style={{ display: "none" }}
id="file"
onChange={(e) => setImg(e.target.files[0])}
/>
<label htmlFor="file">
<img src={Img} alt="" />
</label>
<button onClick={handleSend}>Send</button>
</div>
</div>
);
};
export default Input;
Message.jsThe file that shows the messages.
import React, { useContext, useEffect, useRef } from "react";
import { AuthContext } from "./AuthContext";
import { ChatContext } from "./ChatContext";
const Message = ({ message }) => {
const { currentUser } = useContext(AuthContext);
const { data } = useContext(ChatContext);
const ref = useRef();
useEffect(() => {
ref.current?.scrollIntoView({ behavior: "smooth" });
}, [message]);
return (
<div
ref={ref}
className={`message ${message.senderId === currentUser.uid && "owner"}`}
>
<div className="messageInfo">
<img
src={
message.senderId === currentUser.uid
? currentUser.photoURL
: data.user.photoURL
}
alt=""
/>
<span>just now</span>
</div>
<div className="messageContent">
<p>{message.text}</p>
{message.img && <img src={message.img} alt="" />}
</div>
</div>
);
};
export default Message;
Can you help me Implement it.
I have 2 sibling components namely <AddTask/> and <TaskList/> which are children of <Home/> component. Currently, when I add a new task in my ToDo App, it will be added but I need to refresh the page in order for it to display the new task. How can I refresh the <TaskList/> component immediately after I click the Add button in <AddTask/> component?
Here is my <AddTask/> Component
const AddTask = () => {
const [task, setTask] = useState("");
const [isPending, setIsPending] = useState(false);
const handleClick = (e)=> {
e.preventDefault();
setIsPending(true);
const todo = {task};
fetch('http://localhost:8000/tasks', {
method: 'POST',
headers: { "Content-Type": "application/json" },
body: JSON.stringify(todo)
})
.then(()=>{
setIsPending(false);
})
};
return (
<form className="new-task" onSubmit={handleClick}>
<input className="input"
type="text"
required
value={task}
onChange= { (e)=> setTask(e.target.value) }
/>
<button className="add-task">Add</button>
</form>
);
}
export default AddTask;
This is the <Home/> Component
import TaskList from "./TaskList";
import useFetch from "./useFetch";
const Home = () => {
const { data: task, isPending, error} = useFetch('http://localhost:8000/tasks');
return (
<div className="home">
<AddTask />
{ error && <div>Failed to fetch data.</div> }
{ isPending && <div>Loading...</div> }
{ task && <TaskList task={task} /> }
</div>
);
}
export default Home;
In Home component, you need a tasks state so you can update that state in AddTask component
Home
import TaskList from "./TaskList";
import useFetch from "./useFetch";
import { useState, useEffect } from 'react'
const Home = () => {
const [tasks, setTasks] = useState(null);
const { data: task, isPending, error} = useFetch('http://localhost:8000/tasks');
useEffect(() => {
if (task) setTasks(task)
}, [task])
return (
<div className="home">
<AddTask setTasks={setTasks} />
{ error && <div>Failed to fetch data.</div> }
{ isPending && <div>Loading...</div> }
{ tasks && <TaskList task={tasks} /> }
</div>
);
}
export default Home;
AddTask
const AddTask = ({ setTasks }) => {
const [task, setTask] = useState("");
const [isPending, setIsPending] = useState(false);
const handleClick = (e)=> {
e.preventDefault();
setIsPending(true);
const todo = {task};
fetch('http://localhost:8000/tasks', {
method: 'POST',
headers: { "Content-Type": "application/json" },
body: JSON.stringify(todo)
})
.then(()=>{
setIsPending(false);
setTasks(prev => ([...prev, task]))
})
};
return (
<form className="new-task" onSubmit={handleClick}>
<input className="input"
type="text"
required
value={task}
onChange= { (e)=> setTask(e.target.value) }
/>
<button className="add-task">Add</button>
</form>
);
}
export default AddTask;
I am learning Redux. I cannot figure out how to set state.
I need to set state (I'm assuming with useDispatch) by using a login form. On the component Fake1, I am able to console.log the "user" passed with useSelector. If i hardcode a change in state on user.js ({ i.e., username: "beanbag", password: "122345" }), the change in state appears on Fake1, telling me that the mechanics of my setup are good, and that the problem is that state is not being set inside loginOnSubmit().
My code:
const initialStateValue = { username: "", password: "" };
export const userSlice = createSlice({
name: "user",
initialState: { value: initialStateValue },
reducers: {
login: (state, action) => {
state.value = action.payload;
},
logout: (state) => {
state.value = initialStateValue;
},
},
});
export const { login, logout } = userSlice.actions;
export default userSlice.reducer;
import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import Visibility from "#mui/icons-material/Visibility";
import VisibilityOff from "#mui/icons-material/VisibilityOff";
import InputAdornment from "#mui/material/InputAdornment";
import IconButton from "#mui/material/IconButton";
import Input from "#mui/material/Input";
import Button from "#mui/material/Button";
import LoginIcon from "#mui/icons-material/Login";
import AddCircleOutlineIcon from "#mui/icons-material/AddCircleOutline";
import Stack from "#mui/material/Stack";
import "./LoginForm.css";
import { useDispatch } from "react-redux";
import { login } from "../features/user";
function LoginForm() {
const [user, setUser] = useState(null);
const [loginUsername, setLoginUsername] = useState("");
const [loginError, setLoginError] = useState([]);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [values, setValues] = useState({
password: "",
showPassword: false,
});
const dispatch = useDispatch();
const navigate = useNavigate();
const handleChange = (prop) => (event) => {
setValues({ ...values, [prop]: event.target.value });
};
const handleClickShowPassword = () => {
setValues({
...values,
showPassword: !values.showPassword,
});
};
const handleMouseDownPassword = (event) => {
event.preventDefault();
};
// useEffect(() => {
// fetch("/authorize_user")
// .then((res) => res.json())
// .then(setUser);
// }, []);
const loginOnSubmit = (e) => {
e.preventDefault();
const newUser = {
username: loginUsername,
password: values.password,
};
// dispatch(login({username: loginUsername, password: values.password}))
fetch("/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(newUser),
}).then((res) => {
if (res.ok) {
res.json().then((newUser) => {
setUser(newUser);
setIsAuthenticated(true);
setLoginUsername("");
dispatch(login({ newUser }));
navigate("/fake1");
});
} else {
res.json().then((json) => setLoginError(json.error));
}
});
};
const handleSignupRoute = () => {
navigate("/signup");
};
return (
<form onSubmit={loginOnSubmit}>
<div>
<br></br>
<Input
className="test1"
value={loginUsername}
onChange={(e) => setLoginUsername(e.target.value)}
type="text"
label="Username"
placeholder="Username"
/>
<br></br>
<br></br>
<Input
id="standard-adornment-password"
type={values.showPassword ? "text" : "password"}
value={values.password}
// onChange={(e) => setValues(e.target.value)}
onChange={handleChange("password")}
placeholder="Password"
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickShowPassword}
onMouseDown={handleMouseDownPassword}
>
{values.showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
}
/>
<br></br>
<br></br>
<br></br>
<div className="test2">
<Stack direction="row" spacing={2}>
<Button
type="submit"
variant="outlined"
endIcon={<LoginIcon />}
className="btn-login"
>
Login
</Button>
<Button
onClick={handleSignupRoute}
variant="outlined"
endIcon={<AddCircleOutlineIcon />}
className="btn-signup"
>
Signup
</Button>
</Stack>
<br></br>
<br></br>
</div>
</div>
</form>
);
}
export default LoginForm;
import React from 'react'
import {useSelector} from 'react-redux'
const Fake1 = () => {
const user = useSelector(state => state.user.value)
console.log(user)
return (
<div>Fake1</div>
)
}
export default Fake1
I am getting this error and I don;t know what else to do.
I am using next.js and my code looks like this.
The _app.js:
import '../styles/globals.scss'
import React from 'react'
import Layout from '../components/Layout'
import Head from "next/head";
import Signin from "./signin";
import Register from "./register";
import { DataProvider } from "../store/GlobalState";
function MyApp ({
Component,
pageProps
}) {
if (typeof window !== 'undefined') {
if (window.location.pathname === '/signin') {
return (
<DataProvider>
<Signin/>
</DataProvider>
)
} else if (window.location.pathname === '/register') {
return (
<DataProvider>
<Register/>
</DataProvider>
)
}
}
return (
<DataProvider>
<Head>
<title>Above the Sky</title>
</Head>
<Layout>
<Component {...pageProps} />
</Layout>
</DataProvider>
)
}
export default MyApp
I am doing this because I want the register and the login pages to be separate from the layout, not having any header or footer whatsoever... If you have a hint on this , how I should do this better please tell me .... but this is not the main problem..
and now the Register.js:
import Head from 'next/head'
import { useContext, useEffect, useState } from "react";
import Link from 'next/link'
import valid from '../utils/valid'
import { DataContext } from "../store/GlobalState";
const Register = () => {
const [ mounted, setMounted ] = useState(false);
const initialState = {
email: '',
password: '',
cf_password: ''
};
const [ userData, setUserData ] = useState(initialState);
const {
email,
password,
cf_password
} = userData;
const {
state,
dispatch
} = useContext(DataContext)
const handleChangeInput = e => {
const {
name,
value
} = e.target
setUserData({
...userData,
[name]: value
})
dispatch({
type: 'NOTIFY',
payload: {}
})
}
const handleSubmit = async e => {
e.preventDefault()
const errorMessage = valid(email, password, cf_password)
if (errorMessage) {
return dispatch({
type: 'NOTIFY',
payload: { error: errorMessage }
})
}
dispatch({
type: 'NOTIFY',
payload: { success: 'Ok' }
})
}
useEffect(() => {
setMounted(true)
}, [])
return (
mounted
&&
<div style={{
backgroundColor: 'black',
height: '100vh'
}}>
<Head>
<title>Register Page</title>
</Head>
<div className="login-dark" style={{ height: "695px" }}>
<form className='container' onSubmit={handleSubmit}>
<div className="illustration"><i className="fas fa-thin fa-user-plus"/></div>
<div className="mb-3">
<label htmlFor="exampleInputEmail1" className="form-label">Email address</label>
<input type="email" className="form-control" id="exampleInputEmail1" aria-describedby="emailHelp"
name="email" value={email} onChange={handleChangeInput}/>
<div id="emailHelp" className="form-text">We'll never share your email with anyone else.</div>
</div>
<div className="mb-3">
<label htmlFor="exampleInputPassword1" className="form-label">Password</label>
<input type="password" className="form-control" id="exampleInputPassword1"
name="password" value={password} onChange={handleChangeInput}/>
</div>
<div className="mb-3">
<label htmlFor="exampleInputPassword2" className="form-label">Confirm Password</label>
<input type="password" className="form-control" id="exampleInputPassword2"
name="cf_password" value={cf_password} onChange={handleChangeInput}/>
</div>
<div className='button-container'>
<button type="submit" className="btn btn-primary btn-block">Register</button>
</div>
<a className="forgot" href="#">Forgot your email or password?</a>
<p className="have-account">Already have an account ? <Link href="/signin"><a style={{ color: 'crimson' }}>Login here</a></Link></p>
</form>
</div>
</div>
)
}
export default Register
When I render the register page I get this error in the console ..
"next-dev.js?3515:32 Warning: Did not expect server HTML to contain a in ."
These are my store files aswell:
Actions.js
export const ACTIONS = {
NOTIFY: 'NOTIFY',
AUTH: 'AUTH'
}
Reducer.js
import { ACTIONS } from './Actions';
const reducers = (state, action) => {
switch (action.type) {
case ACTIONS.NOTIFY:
return {
...state,
notify: action.payload
};
case ACTIONS.AUTH:
return {
...state,
auth: action.payload
};
default:
return state;
}
}
export default reducers
and the GlobalState.js
import { createContext, useReducer } from "react";
import reducers from "./Reducers";
export const DataContext = createContext()
export const DataProvider = ({ children }) => {
const initialState = {
notify: {},
auth: {}
}
const [ state, dispatch ] = useReducer(reducers, initialState)
const { cart, auth } = state
return (
<DataContext.Provider value={{
state,
dispatch
}}>
{children}
</DataContext.Provider>
)
}
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