Argument of type 'AuthModel | undefined' is not assignable to parameter of type 'Auth' - reactjs

Trying to login user via firebase, receiving the following error:
ERROR in src/app/modules/auth/core/_requests.ts:28:59
TS2345: Argument of type 'AuthModel | undefined' is not assignable to parameter of type 'Auth'.
Type 'undefined' is not assignable to type 'Auth'.
26 |
27 | export async function login(email: string, password: string) {
28 | const userCredential = await signInWithEmailAndPassword(auth, email, password);
| ^^^^
29 | const user = userCredential.user;
30 | const token = await user.getIdToken();
31 | return {
login.ts
/* eslint-disable jsx-a11y/anchor-is-valid */
import {useState} from 'react'
import * as Yup from 'yup'
import clsx from 'clsx'
import {Link} from 'react-router-dom'
import {useFormik} from 'formik'
import {getUserByToken, login} from '../core/_requests'
import {toAbsoluteUrl} from '../../../../_metronic/helpers'
import {useAuth} from '../core/Auth'
import { Firestore } from 'firebase/firestore'
import { useNavigate } from 'react-router-dom';
const loginSchema = Yup.object().shape({
email: Yup.string()
.email('Wrong email format')
.min(3, 'Minimum 3 symbols')
.max(50, 'Maximum 50 symbols')
.required('Email is required'),
password: Yup.string()
.min(3, 'Minimum 3 symbols')
.max(50, 'Maximum 50 symbols')
.required('Password is required'),
})
const initialValues = {
email: 'admin#demo.com',
password: 'demo',
}
/*
Formik+YUP+Typescript:
https://jaredpalmer.com/formik/docs/tutorial#getfieldprops
https://medium.com/#maurice.de.beijer/yup-validation-and-typescript-and-formik-6c342578a20e
*/
export function Login() {
const [loading, setLoading] = useState(false)
const {saveAuth, setCurrentUser} = useAuth()
const formik = useFormik({
initialValues,
validationSchema: loginSchema,
onSubmit: async (values, {setStatus, setSubmitting}) => {
setLoading(true)
try {
const auth = await login(values.email, values.password)
saveAuth(auth)
const {data: user} = await getUserByToken(auth.api_token)
setCurrentUser(user)
} catch (error) {
console.error(error)
saveAuth(undefined)
setStatus((error as Error).message)
} finally {
setSubmitting(false)
setLoading(false)
}
},
})
return (
<form
className='form w-100'
onSubmit={formik.handleSubmit}
noValidate
id='kt_login_signin_form'
>
{/* begin::Heading */}
<div className='text-center mb-10'>
<h1 className='text-dark mb-3'>Sign In to Web Construct</h1>
<div className='text-gray-400 fw-bold fs-4'>
New Here?{' '}
<Link to='/auth/registration' className='link-primary fw-bolder'>
Create an Account
</Link>
</div>
</div>
{/* begin::Heading */}
{formik.status ? (
<div className='mb-lg-15 alert alert-danger'>
<div className='alert-text font-weight-bold'>{formik.status}</div>
</div>
) : (
<div className='mb-10 bg-light-info p-8 rounded'>
<div className='text-info'>
Use account <strong>admin#demo.com</strong> and password <strong>demo</strong> to
continue.
</div>
</div>
)}
{/* begin::Form group */}
<div className='fv-row mb-10'>
<label className='form-label fs-6 fw-bolder text-dark'>Email</label>
<input
placeholder='Email'
{...formik.getFieldProps('email')}
className={clsx(
'form-control form-control-lg form-control-solid',
{'is-invalid': formik.touched.email && formik.errors.email},
{
'is-valid': formik.touched.email && !formik.errors.email,
}
)}
type='email'
name='email'
autoComplete='off'
/>
{formik.touched.email && formik.errors.email && (
<div className='fv-plugins-message-container'>
<span role='alert'>{formik.errors.email}</span>
</div>
)}
</div>
{/* end::Form group */}
{/* begin::Form group */}
<div className='fv-row mb-10'>
<div className='d-flex justify-content-between mt-n5'>
<div className='d-flex flex-stack mb-2'>
{/* begin::Label */}
<label className='form-label fw-bolder text-dark fs-6 mb-0'>Password</label>
{/* end::Label */}
{/* begin::Link */}
<Link
to='/auth/forgot-password'
className='link-primary fs-6 fw-bolder'
style={{marginLeft: '5px'}}
>
Forgot Password ?
</Link>
{/* end::Link */}
</div>
</div>
<input
type='password'
autoComplete='off'
{...formik.getFieldProps('password')}
className={clsx(
'form-control form-control-lg form-control-solid',
{
'is-invalid': formik.touched.password && formik.errors.password,
},
{
'is-valid': formik.touched.password && !formik.errors.password,
}
)}
/>
{formik.touched.password && formik.errors.password && (
<div className='fv-plugins-message-container'>
<div className='fv-help-block'>
<span role='alert'>{formik.errors.password}</span>
</div>
</div>
)}
</div>
{/* end::Form group */}
{/* begin::Action */}
<div className='text-center'>
<button
type='submit'
id='kt_sign_in_submit'
className='btn btn-lg btn-primary w-100 mb-5 '
disabled={formik.isSubmitting || !formik.isValid}
>
{!loading && <span className='indicator-label'>Continue</span>}
{loading && (
<span className='indicator-progress' style={{display: 'block'}}>
Please wait...
<span className='spinner-border spinner-border-sm align-middle ms-2'></span>
</span>
)}
</button>
{/* begin::Separator */}
<div className='text-center text-muted text-uppercase fw-bolder mb-5'>or</div>
{/* end::Separator */}
{/* begin::Google link */}
<a className='btn btn-flex flex-center btn-light btn-lg w-100 mb-5'>
<img
alt='Logo'
src={toAbsoluteUrl('/media/svg/brand-logos/google-icon.svg')}
className='h-20px me-3'
/>
Continue with Google
</a>
{/* end::Google link */}
{/* begin::Google link */}
<a href='#' className='btn btn-flex flex-center btn-light btn-lg w-100 mb-5'>
<img
alt='Logo'
src={toAbsoluteUrl('/media/svg/brand-logos/facebook-4.svg')}
className='h-20px me-3'
/>
Continue with Facebook
</a>
{/* end::Google link */}
{/* begin::Google link */}
<a href='#' className='btn btn-flex flex-center btn-light btn-lg w-100'>
<img
alt='Logo'
src={toAbsoluteUrl('/media/svg/brand-logos/apple-black.svg')}
className='h-20px me-3'
/>
Continue with Apple
</a>
{/* end::Google link */}
</div>
{/* end::Action */}
</form>
)
}
requests.ts
import axios from 'axios'
import {AuthModel, UserModel} from './_models'
import {auth} from "../../../../firebase.js";
import {
Auth,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signOut,
onAuthStateChanged,
} from 'firebase/auth';
const API_URL = process.env.REACT_APP_API_URL
export const GET_USER_BY_ACCESSTOKEN_URL = `${API_URL}/verify_token`
export const LOGIN_URL = `${API_URL}/login`
export const REGISTER_URL = `${API_URL}/register`
export const REQUEST_PASSWORD_URL = `${API_URL}/forgot_password`
// Server should return AuthModel - OLD SIGN IN
// export function login(email: string, password: string) {
// return axios.post<AuthModel>(LOGIN_URL, {
// email,
// password,
// })
// }
export async function login(email: string, password: string) {
const userCredential = await signInWithEmailAndPassword(auth, email, password);
const user = userCredential.user;
const token = await user.getIdToken();
return {
api_token: token,
email: user.email,
// add any other user data you need to the AuthModel object
};
}
// Server should return AuthModel
export function register(
email: string,
firstname: string,
lastname: string,
password: string,
password_confirmation: string
) {
return axios.post(REGISTER_URL, {
email,
first_name: firstname,
last_name: lastname,
password,
password_confirmation,
})
}
// Server should return object => { result: boolean } (Is Email in DB)
export function requestPassword(email: string) {
return axios.post<{result: boolean}>(REQUEST_PASSWORD_URL, {
email,
})
}
export function getUserByToken(token: string) {
return axios.post<UserModel>(GET_USER_BY_ACCESSTOKEN_URL, {
api_token: token,
})
}
firebase.js
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAuth } from "./app/modules/auth";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: "AIzaSyAwRC9aw8fFfTkm-hl7pjR5_CTB2leohOI",
authDomain: "web-construct-app.firebaseapp.com",
projectId: "web-construct-app",
storageBucket: "web-construct-app.appspot.com",
messagingSenderId: "576412080434",
appId: "1:576412080434:web:fbdd24284ee1d316e65f2b",
measurementId: "G-JZJCSM78J4"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
export const auth = getAuth(app)
export default app
auth.tsx
import {
FC,
useState,
useEffect,
createContext,
useContext,
useRef,
Dispatch,
SetStateAction,
} from 'react'
import {LayoutSplashScreen} from '../../../../_metronic/layout/core'
import {AuthModel, UserModel} from './_models'
import * as authHelper from './AuthHelpers'
import {getUserByToken} from './_requests'
import {WithChildren} from '../../../../_metronic/helpers'
type AuthContextProps = {
auth: AuthModel | undefined
saveAuth: (auth: AuthModel | undefined) => void
currentUser: UserModel | undefined
setCurrentUser: Dispatch<SetStateAction<UserModel | undefined>>
logout: () => void
}
const initAuthContextPropsState = {
auth: authHelper.getAuth(),
saveAuth: () => {},
currentUser: undefined,
setCurrentUser: () => {},
logout: () => {},
}
const AuthContext = createContext<AuthContextProps>(initAuthContextPropsState)
const useAuth = () => {
return useContext(AuthContext)
}
const AuthProvider: FC<WithChildren> = ({children}) => {
const [auth, setAuth] = useState<AuthModel | undefined>(authHelper.getAuth())
const [currentUser, setCurrentUser] = useState<UserModel | undefined>()
const saveAuth = (auth: AuthModel | undefined) => {
setAuth(auth)
if (auth) {
authHelper.setAuth(auth)
} else {
authHelper.removeAuth()
}
}
const logout = () => {
saveAuth(undefined)
setCurrentUser(undefined)
}
return (
<AuthContext.Provider value={{auth, saveAuth, currentUser, setCurrentUser, logout}}>
{children}
</AuthContext.Provider>
)
}
const AuthInit: FC<WithChildren> = ({children}) => {
const {auth, logout, setCurrentUser} = useAuth()
const didRequest = useRef(false)
const [showSplashScreen, setShowSplashScreen] = useState(true)
// We should request user by authToken (IN OUR EXAMPLE IT'S API_TOKEN) before rendering the application
useEffect(() => {
const requestUser = async (apiToken: string) => {
try {
if (!didRequest.current) {
const {data} = await getUserByToken(apiToken)
if (data) {
setCurrentUser(data)
}
}
} catch (error) {
console.error(error)
if (!didRequest.current) {
logout()
}
} finally {
setShowSplashScreen(false)
}
return () => (didRequest.current = true)
}
if (auth && auth.api_token) {
requestUser(auth.api_token)
} else {
logout()
setShowSplashScreen(false)
}
// eslint-disable-next-line
}, [])
return showSplashScreen ? <LayoutSplashScreen /> : <>{children}</>
}
export {AuthProvider, AuthInit, useAuth}
Tried passing savAuth as null, but no luck.

Related

log-in authentication with credentials, nextjs

I'm trying to add next-auth authentication with credentials(it's my first time and the source I'm using for following is using version 3 as I know, therefore there is a lot of difference and I couldn't find the right solution for days). Basically I have got form of registration and log-in form, and as a backend server I'm using mongodb. the registration form works normally, user is able to create account, but the log-in form doesn't seem to be working and in console when user sends request for logging in comes up error: {error: 'client.db.collection is not a function', status: 401, ok: false, url: null}.
This is pages/api/[...nextauth].js file.
import { verifyPassword } from "#/lib/auth";
import connectToDatabase from "#/lib/db";
import NextAuth from "next-auth/next";
import CredentialsProvider from "next-auth/providers/credentials";
export default NextAuth({
session: {
strategy: "jwt",
},
providers: [
CredentialsProvider({
async authorize(credentials, req) {
const client = await connectToDatabase();
const userCollection = client.db.collection("users");
const user = await userCollection.findOne({ email: credentials.email });
if (!user) {
client.close();
throw new Error("No user found!");
}
const isValid = verifyPassword(credentials.password, user.password);
if (!isValid) {
client.close();
throw new Error("Could not log you in.");
}
client.close();
return { email: user.email };
},
}),
],
});
and this is loginForm.js that should send request to log-in.
import React, { useRef } from "react";
import Link from "next/link";
import { signIn } from "next-auth/react";
const LoginForm = () => {
const emailRef = useRef();
const passwordRef = useRef();
const submitHandler = async (e) => {
e.preventDefault();
const enteredEmail = emailRef.current.value;
const enteredPassword = passwordRef.current.value;
try {
const result = await signIn("credentials", {
redirect: false,
email: enteredEmail,
password: enteredPassword,
});
console.log(result);
} catch (error) {
console.log(error.error);
}
};
return (
<div
className="container d-flex justify-content-center align-items-center"
style={{ width: "100%", height: "100vh" }}
>
<div className="col-6-sm">
<form onSubmit={submitHandler}>
<div className="form-outline mb-4">
<input
type="email"
id="form2Example1"
className="form-control"
ref={emailRef}
/>
<label className="form-label" for="form2Example1">
Email address
</label>
</div>
<div className="form-outline mb-4">
<input
type="password"
id="form2Example2"
className="form-control"
ref={passwordRef}
/>
<label className="form-label" for="form2Example2">
Password
</label>
</div>
<button type="submit" className="btn btn-dark m-1">
Sign in
</button>
<Link href="/register" className="btn btn-dark m-1">
Register
</Link>
</form>
</div>
</div>
);
};
export default LoginForm;

ReactJS component in header navigation does not re-render again after context value changed

I have authentication context provider to store login user info and show user's photo in header navigation.
I have profile edit page to edit user's display name and photo. After I update the user information to firebase and database via api, I set the user's updated info into context value again. But image on header navigation does not change until manually refresh the page or click the other routes. My I know what I have wrong to refresh the context value.
auth_prodiver.jsx
import React, { useState, useEffect, useContext, createContext } from "react";
import { useLocation, Navigate } from "react-router-dom";
import { signIn, signUp, updateCurrentUser } from "../helpers/gql_auth_helpers";
import paths from "../routes/paths";
import { store } from "../store/configure_store";
import { saveUser } from "../store/slice/auth_slice";
import { auth } from "../helpers/init-firebase";
import {
getAuth,
sendEmailVerification,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
sendPasswordResetEmail,
onAuthStateChanged,
signInWithPopup,
GoogleAuthProvider,
FacebookAuthProvider,
signOut,
confirmPasswordReset,
updateProfile,
verifyPasswordResetCode,
applyActionCode,
checkActionCode,
updatePassword,
} from "firebase/auth";
const AuthContext = createContext(null);
let accessToken = "";
export const getAccessToken = () => accessToken;
export const setAccessToken = (token) => {
accessToken = token;
};
export const AuthProvider = ({ user: usr, children }) => {
const [user, setUser] = useState(usr);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
if (user && user.emailVerified) {
setUser(user);
setAccessToken(user.getIdToken(true));
store.dispatch(saveUser(user));
} else {
setUser(null);
setAccessToken(null);
store.dispatch(saveUser(null));
}
});
return () => {
unsubscribe();
};
}, []);
async function login(email, password) {
return signInWithEmailAndPassword(auth, email, password).then(
async function (res) {
const auth = getAuth();
return await auth.currentUser
.getIdToken(true)
.then(async function (token) {
return await signIn(token);
});
}
);
}
async function register(name, email, password) {
return createUserWithEmailAndPassword(auth, email, password).then(
async function (res) {
const auth = getAuth();
return await updateProfile(auth.currentUser, {
displayName: name,
})
.then(async function () {
const auth = getAuth();
if (auth.currentUser != null) {
return await sendEmailVerification(auth.currentUser).then(
async function (res) {
return await auth.currentUser
.getIdToken(true)
.then(async function (token) {
await signUp(token);
});
}
);
}
})
.catch((error) => {
throw error;
});
}
);
}
async function resendEmailVerification() {
return sendEmailVerification(auth.currentUser);
}
async function forgotPassword(email) {
return sendPasswordResetEmail(auth, email, {
url: `https://myanmargita.com/login`,
});
}
async function confirmPassword(oobCode, newPassword) {
return confirmPasswordReset(auth, oobCode, newPassword);
}
async function updateCurrentUserPassword(newPassword) {
const auth = getAuth();
const user = auth.currentUser;
return updatePassword(user, newPassword);
}
async function updateUser(updateUserId, displayName, photoUrl) {
const auth = getAuth();
const currentUser = getAuth().currentUser;
return await updateProfile(auth.currentUser, {
displayName: displayName,
photoUrl: photoUrl,
})
.then(async function () {
const auth = getAuth();
if (auth.currentUser != null) {
updateCurrentUser(updateUserId, displayName, photoUrl)
.then(async(updatedUser) => {
if (updatedUser) {
await currentUser.reload();
setUser(currentUser);
setAccessToken(currentUser.getIdToken(true));
store.dispatch(saveUser(currentUser));
console.log("User Name : ", user.displayName)
} else {
setUser(null);
setAccessToken(null);
store.dispatch(saveUser(null));
}
})
.catch((error) => {
throw error;
});
}
})
.catch((error) => {
throw error;
});
}
async function resetPassword(actionCode) {
return verifyPasswordResetCode(auth, actionCode);
}
async function applyAction(actionCode) {
return applyActionCode(auth, actionCode);
}
async function checkAction(actionCode) {
return checkActionCode(auth, actionCode);
}
async function logout() {
return signOut(auth);
}
async function signInWithGoogle() {
const provider = new GoogleAuthProvider();
provider.setCustomParameters({
display: "popup",
});
return signInWithPopup(auth, provider);
}
async function signInWithFacebook() {
const provider = new FacebookAuthProvider();
provider.setCustomParameters({
display: "popup",
});
return signInWithPopup(auth, provider);
}
const value = {
user,
accessToken,
login,
register,
forgotPassword,
confirmPassword,
logout,
signInWithGoogle,
signInWithFacebook,
resetPassword,
applyAction,
checkAction,
resendEmailVerification,
updateCurrentUserPassword,
updateUser,
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
};
export const useAuth = () => {
return useContext(AuthContext);
};
export const RequireAuth = ({ children }) => {
let auth = useAuth();
let location = useLocation();
if (!auth?.user) {
return <Navigate to={paths.login} state={{ from: location }} />;
}
return children;
};
profile_edit_page.jsx
import React, { useEffect,useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as yup from "yup";
import PicturePicker from "../../components/picture_picker";
import ProgressBar from "../../controls/progress_bar";
import LoadingIndicator from "../../components/loading_indicator";
import {getUserWithId } from "../../gql/user";
import { useAuth } from "../../auth/auth_provider";
import { useQuery } from "#apollo/client";
import paths from '../../routes/paths';
import { storage} from "../../helpers/init-firebase";
import { ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";
const formSchema = yup.object().shape({
name: yup.string().required("Name is required."),
email: yup.string().required("Email is required."),
});
const formData = {
name: "",
email: "",
photoURL: "",
};
const ProfileEditPage = (props) => {
const { id } = useParams();
const [uploadProgress, setUploadProgress] = useState(0);
const [pictureUrl, setPictureUrl] = useState();
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
const { data, loading: dataLoading, error } = useQuery(getUserWithId,{variables:{
"where": {
"id": id
}
}});
let auth = useAuth();
const handleUpdate = async (edit_data) => {
console.log("Id : "+ id);
const user_name = edit_data.name.trim();
const email = edit_data.email.trim();
const file = edit_data.photoURL;
if (pictureUrl === file) {
auth.updateUser(id, user_name, edit_data.photoURL);
} else {
const file = edit_data.photoURL;
const storageRef = ref(storage, "images/users/" + file.name);
const uploadTask = uploadBytesResumable(storageRef,file);
uploadTask.on(
"state_changed",
(snapshot) => {
var progress = Math.round(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log("Progrss: ", progress);
setUploadProgress({ progress });
},
(error) => {
console.log("File Upload Error: ", error.message);
throw error;
},
() => {
getDownloadURL(uploadTask.snapshot.ref).then(async(url) => {
await auth.updateUser(id, user_name, url);
navigate(paths.getProfile(id));
});
}
);
}
};
const handleBack = () => {
navigate(-1);
};
useEffect(()=>{
if(!dataLoading && !error){
if(data?.user) {
setPictureUrl(data?.user?.photoURL);
formData.name = data?.user.displayName;
formData.email = data?.user.email;
formData.photoURL = data?.user.photoURL;
}
}
},[dataLoading,error]);
const EntryForm = () => {
return (
<Formik initialValues={formData} enableReinitialize={true} onSubmit={handleUpdate} validationSchema={formSchema}>
{({ dirty, values, errors, touched, handleChange, handleSubmit, handleReset, setFieldValue }) => {
return (
<Form autoComplete="off">
<div className="text-sm">
<div className="flex flex-nowrap">
<div className="w-48 p-2 m-2">Email</div>
<div className="p-2 m-2">
{values.email}
</div>
</div>
<div className="flex flex-nowrap">
<div className="w-48 p-2 m-2">Name</div>
<div className="p-2 m-2">
<Field
type="text"
className="p-2 w-96 textarea textarea-primary"
id="name"
name="name"
placeholder="Display Name"
value={values.name}
onChange={handleChange}
/>
<ErrorMessage name="name" component="span" className="text-sm text-red-500 px-2" />
</div>
</div>
<div className="flex flex-nowrap">
<div className="w-48 p-2 m-2">Profile Image</div>
<div className="flex flex-col items-left align-middle">
<div className="flex flex-col px-4 mt-8 mx-4 h-60 items-cente p-2 m-2" style={{ height: "250px", width: "200px" }}>
<PicturePicker url={pictureUrl} onChange={(file) => setFieldValue("photoURL", file)} value={values.photoURL} />
<ProgressBar className="px-2 pt-2 pb-1" percent={uploadProgress} />
<span className="text-red-600 self-center text-sm">{touched.photoURL && errors.photoURL}</span>
<ErrorMessage name="photoURL" component="span" className="text-sm text-red-500 px-2" />
</div>
</div>
</div>
<div className="flex flex-nowrap p-3 mt-5">
<button type="submit" onClick={handleSubmit} disabled={!dirty} className="btn btn-primary btn-sm mx-2 my-1 w-[66px]">
Update
</button>
<button disabled={!dirty} className="btn btn-primary btn-sm mx-2 my-1 w-[66px]" onClick={handleReset}>
Clear
</button>
</div>
</div>
</Form>
);
}}
</Formik>
);
};
return (
<div className="card">
<header className="px-5 py-4 border-b border-gray-100">
<h2 className="font-semibold text-gray-800">Edit Profile</h2>
</header>
<EntryForm />
<LoadingIndicator loading={loading} color="#000099" label="Uploading..." />
</div>
);
};
export default ProfileEditPage;
profile.jsx (profile component using in header.jsx)
import { useState, useRef, useEffect } from "react";
import {
ProfileIcon,
ResetPasswordIcon,
LogoutIcon,
} from "../assets/icons/svg_icons";
import { useAuth } from "../auth/auth_provider";
import { useNavigate } from "react-router-dom";
import paths from "../routes/paths";
import ProfileModal from "./profile_modal";
const Profile = () => {
const [isOpen, setIsOpen] = useState(false);
const [modal, setModal] = useState({
open: false,
id: "",
});
const wrapperRef = useRef(null);
const auth = useAuth();
const navigate = useNavigate();
const handleLogout = (e) => {
e.preventDefault();
auth.logout();
};
const handleResetPassword = (e) => {
e.preventDefault();
navigate(paths.resetpassword, { replace: true });
};
const handleProfile = (e) => {
e.preventDefault();
navigate(paths.getProfile(auth.user.uid));
};
const handleModalClose = async () => {
setModal({ ...modal, open: false });
};
const handleModalOpen = async () => {
setModal({ ...modal, id: auth.user.uid, open: true });
};
useOutsideClick(wrapperRef);
function useOutsideClick(ref) {
useEffect(() => {
function handleClickOutside(event) {
if (ref.current && !ref.current.contains(event.target)) {
setIsOpen(false);
}
}
// Bind the event listener
document.addEventListener("mousedown", handleClickOutside);
return () => {
// Unbind the event listener on clean up
document.removeEventListener("mousedown", handleClickOutside);
};
}, [ref]);
}
return (
<div className="flex justify-end" ref={wrapperRef}>
<div className="relative">
<div className="cursor-pointer" onClick={() => setIsOpen(!isOpen)}>
<div className="avatar online placeholder">
<div className="bg-neutral-focus text-neutral-content rounded-full w-12 h-12">
{auth.user && (
<div className="avatar online placeholder">
<div className="bg-neutral-focus text-neutral-content rounded-full w-12 h-12">
{auth.user ? (
auth.user?.photoURL == null ? (
<ProfileIcon className="w-10 h-10" />
) : (
<img src={auth.user?.photoURL} />
)
) : (
<div></div>
)}
</div>
</div>
)}
</div>
</div>
</div>
{isOpen && (
<div
className="origin-top-right absolute right-0 mt-2 w-80 rounded-md shadow-lg
bg-white ring-1 ring-black ring-opacity-5 divide-y divide-gray-100
focus:outline-none cursor-pointer z-50"
>
<div className="py-5">
<div className="flex justify-center" onClick={handleModalOpen}>
<div className="relative text-gray-700">
{auth.user ? (
auth.user?.photoURL == null ? (
<ProfileIcon className="w-20 h-20" />
) : (
<img
className="object-cover w-20 h-20 rounded-full"
src={auth.user?.photoURL}
alt={auth.user?.displayName}
/>
)
) : (
<div></div>
)}
<div className="flex absolute right-0 bottom-0 w-8 h-8 bg-gray-400 rounded-full justify-center items-center">
<svg
className="w-6 h-6"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
role="img"
width="1em"
height="1em"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 1024 1024"
>
<path
fill="currentColor"
d="M864 260H728l-32.4-90.8a32.07 32.07 0 0 0-30.2-21.2H358.6c-13.5 0-25.6 8.5-30.1 21.2L296 260H160c-44.2 0-80 35.8-80 80v456c0 44.2 35.8 80 80 80h704c44.2 0 80-35.8 80-80V340c0-44.2-35.8-80-80-80zM512 716c-88.4 0-160-71.6-160-160s71.6-160 160-160s160 71.6 160 160s-71.6 160-160 160zm-96-160a96 96 0 1 0 192 0a96 96 0 1 0-192 0z"
/>
</svg>
</div>
</div>
</div>
<div className="pt-2" onClick={handleProfile}>
{auth.user ? (
<h1 className="text-sm text-gray-700 p-0 m-0">
{auth.user.displayName}
</h1>
) : (
<div></div>
)}
{auth.user ? (
<h2 className="text-sm text-gray-700 p-0 m-0">
{auth.user.email}
</h2>
) : (
<div></div>
)}
</div>
<div className="pt-4">
<button
type="submit"
className="btn btn-sm btn-ghost rounded-lg mx-6 my-1 px-4 w-auto text-black border border-gray-400"
onClick={handleProfile}
>
Manage your account
</button>
</div>
</div>
<div className="py-1 mx-2">
<a
className="group flex items-center px-4 py-2 text-sm text-gray-700
hover:bg-primary hover:text-white"
onClick={handleResetPassword}
>
<ResetPasswordIcon className="w-8 h-8" />
<span className="px-1">Reset Password</span>
</a>
</div>
<div className="divide-x" />
<div className="py-1 mx-2">
<a
className="group flex items-center px-4 py-2 text-sm text-gray-700
hover:bg-primary hover:text-white"
onClick={handleLogout}
>
<LogoutIcon className="w-8 h-8" />
<span className="px-1">Logout</span>
</a>
</div>
</div>
)}
</div>
{modal.open && (
<ProfileModal id={auth.user.uid} onClose={handleModalClose} />
)}
</div>
);
};
export default Profile;
If you are sure that url has changed add key to img tag

Whi i get the error "token must be a string"?

I'm trying to use useEffect to set the behaviour for my room and at first, i wanna use the token and roomName to connect to the Twilio Video service. However, i got the error as "token must be a string" in my Room.js. I don't know what's wrong? can anyone help me?
VideoChat.js:
import React, { useState, useCallback } from "react";
import Lobby from "./Lobby";
import Room from "./Room"
function VideoChat() {
const [username, setUsername] = useState("");
const [roomName, setRoomName] = useState("");
const [token, setToken] = useState(null);
const handleUsernameChange = useCallback((event) => {
setUsername(event.target.value);
}, []);
const handleRoomNameChange = useCallback((event) => {
setRoomName(event.target.value);
}, []);
const handleSubmit = useCallback(
async (event) => {
event.preventDefault();
const data = await fetch("/video/token", {
method: "POST",
body: JSON.stringify({
identity: username,
room: roomName,
}),
headers: {
"Content-Type": "application/json",
},
}).then((res) => res.json());
setToken(data.token);
},
[username, roomName]
);
const handleLogout = useCallback((event) => {
setToken(null);
}, []);
return (
<div>
if (token){" "}
{
<div>
<Room roomName={roomName} token={token} handleLogout={handleLogout} />
</div>
}{" "}
else{" "}
{
<Lobby
username={username}
roomName={roomName}
handleUsernameChange={handleUsernameChange}
handleRoomNameChange={handleRoomNameChange}
handleSubmit={handleSubmit}
/>
}{" "}
</div>
);
}
export default VideoChat;
Lobby.js:
import React from "react";
const Lobby = ({
username,
handleUsernameChange,
roomName,
handleRoomNameChange,
handleSubmit
}) => {
return (
<form onSubmit={handleSubmit}>
<h2> Enter a room </h2>{" "}
<div>
<label htmlFor="name"> Name: </label>{" "}
<input
type="text"
id="field"
value={username}
onChange={handleUsernameChange}
required
/>
</div>{" "}
<div>
<label htmlFor="room"> Room name: </label>{" "}
<input
type="text"
id="room"
value={roomName}
onChange={handleRoomNameChange}
required
/>
</div>{" "}
<button type="submit"> Submit </button>{" "}
</form>
);
};
export default Lobby;
Room.js:
import React, { useState, useEffect } from "react";
import Video from "twilio-video";
const Room = ({ roomName, token, handleLogout }) => {
const [room, setRoom] = useState(null);
const [participants, setParticipants] = useState([]);
const remoteParticipants = participants.map(participant=> {
<p key={participant.sid}>{participant.identity}</p>
})
useEffect(() => {
const participantConnected = participant => {
setParticipants(prevParticipants => [...prevParticipants, participant]);
};
const participantDisconnected = participant => {
setParticipants(prevParticipants =>
prevParticipants.filter(p => p !== participant)
);
};
Video.connect(token, {
name: roomName
}).then(room => {
setRoom(room);
room.on('participantConnected', participantConnected);
room.on('participantDisconnected', participantDisconnected);
room.participants.forEach(participantConnected);
});
});
return (
<div className="room">
<h2>Room: {roomName}</h2>
<button onClick={handleLogout}>Log out</button>
<div className="local-participant">
{room ? (<p key={room.localParticipant.sid}>{room.localParticipant.identity}</p>) : ''}
</div>
<h3>Remote Participants</h3>
<p>{remoteParticipants}</p>
</div>
)
};
export default Room;
Sandbox link: https://codesandbox.io/s/beautiful-bhaskara-oo12s?file=/src/Lobby.js

TypeError: editMedicine is not a function - REACT

Not sure where I am going wrong here. editMedicine should be a function but it is not. When I click the edit medicine button, I get an error onSubmit. I know in other areas of my code I had some misspellings and capitalization issues, I am hoping this is the case here . I have been looking at this for so long that I cant quite spot whats going on.
import React, { Fragment, useState, useContext, useEffect } from "react";
import { GlobalContext } from "../context/GlobalState.js";
import { useHistory, Link } from "react-router-dom";
const Editmedicine = (route) => {
let history = useHistory();
const { medicines, editMedicine } = useContext(GlobalContext);
const [selectedUser, setSeletedUser] = useState({
id: null,
name: "",
directions: "",
});
const currentUserId = route.match.params.id;
useEffect(() => {
const medicineId = currentUserId;
const selectedUser = medicines.find(
(med) => med.id === parseInt(medicineId)
);
setSeletedUser(selectedUser);
}, []);
const onSubmit = (e) => {
e.preventDefault();
editMedicine(selectedUser);
history.push("/");
};
const handleOnChange = (userKey, value) =>
setSeletedUser({ ...selectedUser, [userKey]: value });
if (!selectedUser || !selectedUser.id) {
return <div>sdf</div>;
}
return (
<Fragment>
<div id="editContainer">
<form onSubmit={onSubmit}>
<div className="w-full mb-5">
<label
htmlFor="name"
>
Name of medicine
</label>
<input
value={selectedUser.name}
onChange={(e) => handleOnChange("name", e.target.value)}
type="text"
placeholder="Enter name"
/>
</div>
<div className="w-full mb-5">
<label
htmlFor="directions"
>
Directions
</label>
<input
value={selectedUser.directions}
onChange={(e) => handleOnChange("directions", e.target.value)}
type="text"
placeholder="Enter directions"
/>
</div>
<div className="flex items-center justify-between">
<button id="editMedicine">
Edit medicine
</button>
</div>
<div className="text-center mt-4 text-gray-500">
<Link to="/">Cancel</Link>
</div>
</form>
</div>
</Fragment>
);
};
export default Editmedicine;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
// here is the globalState file
import React, { createContext, useReducer } from "react";
import AppReducer from "./AppReducer";
//start by setting the initial state
const initialState = {
medicines: [
{
id: 1,
name: "Daily Multivitamin",
directions: "Take once a day with 2 glasses of water"
},
{
id: 2,
name: "Herbal capsule",
directions: "Take once a day with food"
},
{
id: 3,
name: "Tincture",
directions: "1 drop a day, 3x a day."
}
]
};
//create context
export const GlobalContext = createContext(initialState);
//provider component
export const GlobalProvider = ({ children }) => {
const [state, dispatch] = useReducer(AppReducer, initialState);
function removeMedicine(id) {
dispatch({
type: "REMOVE_MEDICINE",
payload: id
});
}
//this might have to be ADD_MEDICINE or S instead
function addMedicine(medicines) {
dispatch({
type: "ADD_MEDICINE",
payload: medicines
});
}
function editMedicine(medicines) {
dispatch({
type: "EDIT_MEDICINE",
payload: medicines
});
}
return (
<GlobalContext.Provider
value={{
medicines: state.medicines,
removeMedicine,
addMedicine,
editMedicine
}}
>
{children}
</GlobalContext.Provider>
);
};

React-Hooks: How can we pass a selected option from material-ui dialog dropdown to server

I would like to pass an option (either In or Out) from the material-ui dialog drop-down box once the SAVE button is clicked.
ISSUE
I have named the select field as dailyinput, and i want to get the selected value in while making an axios call (PUT).
Currently, am getting that value as undefined. I have added a demo link:
codesandboxlink
// Availability.js
import DailyStatusDialog from "../modal/DailyStatus";
const Availability = () => {
const [deleteDialog, setDeleteDialog] = useState(false);
const [playerId, setPlayerId] = useState("");
const [helperText, setHelperText] = useState('');
const loginUserEmail = localStorage.getItem('loginEmail');
const [dailyinput, setDailyInput] = useState('');
const displayAvailabilityStatus = () => {
setDeleteDialog(true);
}
const onSubmit = (dailyinput) =>{
const dailyStatus = () => {
try {
const params = {
email: loginUserEmail,
};
const res = axios.put('http://localhost:8000/service/availability', dailyinput, {params} );
console.log("Front End success message:" + res.data.success);
if (res.data.success) {
// push do the rest of code after save here...
}
else {
console.log(res.data.message);
setHelperText(res.data.message);
}
} catch (e) {
setHelperText(e.response.data.message);
}
}
return () => { dailyStatus() }
}
return (
<div className="availability_wrapper">
<h2>Availability</h2>
<div className="wrap">
<div className="container">
<div className="dailystatus_section">
<span className="playerdailyrecord">
<div className="row">
<div className="playerdailyrecord_main">
<span className="dailstatus_playerphoto">
<img
className="dailstatus_playerImage"
src="images/photo-1592436806055.JPG"
/>
</span>
<span className="dailstatus">In</span>
<span className="dailstatus_playerposition">Forward</span>
</div>
<div className="playerdailyrecord_main">
<span className="dailstatus_playerphoto">
<img
className="dailstatus_playerImage"
src="images/photo-1592436806055.JPG"
/>
</span>
<span className="dailstatus">In</span>
<span className="dailstatus_playerposition">Forward</span>
</div>
</div>
<span className="">
<button
className="OverlayDailyStatus"
onClick={displayAvailabilityStatus}
>Enter</button>
</span>
</span>
</div>
</div>
<div>
<div className="container">
<div className="playerdistribution_section">
<h4>Team Selection</h4>
</div>
</div>
<div className="container">
<div className="weeklycount_graph_section">
<h4>Weekly Player Availability</h4>
</div>
</div>
</div>
</div>
<DailyStatusDialog
onSubmit={onSubmit(dailyinput)}
open={deleteDialog}
onClose={() => setDeleteDialog(false)}
/>
</div>
);
}
export default Availability;
// DailyStatus.js
import NativeSelect from '#material-ui/core/NativeSelect';
import Dialog from "#material-ui/core/Dialog";
import DialogActions from "#material-ui/core/DialogActions";
import DialogContent from "#material-ui/core/DialogContent";
import DialogContentText from "#material-ui/core/DialogContentText";
import DialogTitle from "#material-ui/core/DialogTitle";
import axios from 'axios'
export default function DailyStatusDialog({ open, onClose, onSubmit }) {
const [dailyinput, setDailyInput] = useState('');
const handleChange = (e) => {
e.persist();
setDailyInput({ dailystatus: e.target.value });
}
return (
<Dialog
open={open}
onClose={onClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{"Enter today's availability ?"}
</DialogTitle>
<DialogActions>
<InputLabel htmlFor="select">I am </InputLabel>
<NativeSelect
defaultValue={'DEFAULT'}
name="dailyinput"
onChange={e =>handleChange(e)}
id="select"
>
<option value="DEFAULT" disabled>Choose an option</option>
<option value="in">In</option>
<option value="out">Out</option>
</NativeSelect>
</DialogActions>
<DialogActions>
<Button onClick={onClose} color="primary">Cancel</Button>
<Button onClick={onSubmit} color="primary" autoFocus>Save</Button>
</DialogActions>
</Dialog>
);
}
// server.js
app.put('/service/availability', async (req, res) => {
try {
const userEmail = req.query.email;
const dailystatus = req.body.dailyinput;
var selector = { where: { email: userEmail } };
const dailyStatus = await UserModel.update(dailystatus, selector);
res.status(200).json({ dailystatus });
} catch (e) {
res.status(500).json({ message: e.message });
}
});
There's some syntax issue in this line
onSubmit={onSubmit(dailyinput)}
You are thinking it right, however onSubmit is a functor which expects a closure to first send dailyinput in and then export a function to feed onSubmit prop here.
An example of this functor could be
const onSubmit = (v) => {
const dailyStatus = async () => {
try {
do something about this v
}
}
return () => { dailyStatus() }
}
async might complicate things here a bit, try use a plain api call without async function if you have trouble on that area.

Resources