How do I connect to a local API via react/formik? - javascript

me and my business partner is coming up with a basic coding model for our respective fields. He's backend, while I'll be doing front end. He made a test api with endpoints that I'm trying to connect to via React/Axios/Formik. I tested to make sure that the endpoints work in postman, and they do, so now I just have to properly code it on my end. Yet I seem to be having trouble. I keep getting a "NET::ERR_SSL_Protocol_Error" when ever I try to make a post via a login/register component I built. Here is the code for each relevant component:
Update:
Thanks to a user here, I managed to get rid of that pesky SSL error. Now I'm getting a 400 status. I'm not sure how to pass in data to a api via formik. Here's the updated code for registration below:
SignUp.js
// import React, {useState} from "react";
// import axiosWithAuth from "../utilils/axiosWithAuth";
// import { useHistory } from "react-router-dom";
// const initialState = {
// username: "",
// password: "",
// email: "",
// role: ""
// };
// // const initialFormErrors = {
// // username: "",
// // password: "",
// // };
// const Registration = (props) => {
// const { push } = useHistory();
// const [formValues, setFormValues] = useState(initialState);
// // const [formErrors, setFormErrors] = useState(initialFormErrors);
// // const [disabled, setDisabled] = useState(togDisabled);
// const handleChange = e => {
// const name = e.target.name;
// const value = e.target.value;
// setState({
// ...state.credentials,
// [name]:value
// });
// setFormValues({
// ...formValues,
// [name]:value
// });
// };
// const register = e => {
// e.preventDefault();
// const signUpData = {
// username: formValues.name,
// password: formValues.password,
// email: formValues.email,
// role: formValues.role
// }
// axiosWithAuth()
// .post("/api/auth/register", signUpData)
// .then((res) => {
// console.log(res);
// push("/login");
// })
// .catch(err =>
// console.error("bk: SignUp.js: Registration: err.message: ", err.message)
// );
// };
// return (
// <div>
// <form onSubmit={register}>
// <div>
// <label>Username</label>
// <input
// type="text"
// name="username"
// value={formValues.name}
// onChange={handleChange}
// /> </div>
// <div>
// <label>Password</label>
// <input
// type="password"
// name="password"
// value={formValues.password}
// onChange={handleChange}
// /></div>
// <div>
// <label>Email</label>
// <input
// type="email"
// name="email"
// value={formValues.email}
// onChange={handleChange}
// /></div>
// <div>
// <label>Role</label>
// <input
// type="text"
// name="role"
// value={formValues.role}
// onChange={handleChange}
// /></div>
// <button>Sign Up</button>
// </form>
// </div>
// );
// }
// export default Registration;
import React, { useState } from 'react';
import axiosWithAuth from '../utilils/axiosWithAuth';
import { useHistory } from 'react-router-dom';
import {Formik, Form} from 'formik';
import FormikControl from '../Forms/FormikControl';
const Registration = (props) => {
const { push } = useHistory();
const initialValues = {
username: '',
password: '',
email: '',
role:''
}
const [state, setState] = useState(initialValues);
const validate = values => {
let errors = {}
//If each of the values are not valid return a required text, otherwise return null.
if (!values.username) { errors.username = 'required' };
if (!values.password) { errors.password = 'required' };
if (!values.email) { errors.email = 'required' };
if (!values.role) { errors.role = 'required' };
return errors;
}
const onSubmit = (values) => {
console.log('form data', values);
axiosWithAuth()
.post("/api/auth/register", state )
.then(res => {
setState(res.values)
push("/login");
})
.catch(err =>
console.error("bk: Login.js: Register: err.message: ", err.message)
);
}
return (
<Formik
initialValues={initialValues}
validate={validate}
onSubmit={onSubmit}
>
{formik => (
<Form>
<FormikControl
control='input'
type='text'
label='Username'
name='username'
// value={initialValues.username}
/>
<FormikControl
control='input'
type='password'
label='Password'
name='password'
// value={initialValues.password}
/>
<FormikControl
control='input'
type='email'
label='Email'
name='email'
// value={initialValues.email}
/>
<FormikControl
control='input'
type='text'
label='Role'
name='role'
// value={initialValues.role}
/>
<button type='submit'>Submit</button>
</Form>
)}
</Formik>
)
}
export default Registration;
Login.js
import React from 'react';
import axiosWithAuth from '../utilils/axiosWithAuth';
import setToken from '../utilils/';
import { useHistory } from 'react-router-dom';
import {Formik, Form} from 'formik';
import FormikControl from '../Forms/FormikControl';
const Login = (props) => {
const { push } = useHistory();
const initialValues = {
username: '',
password: '',
}
const validate = values => {
let errors = {}
//If each of the values are not valid return a required text, otherwise return null.
if (!values.username) { errors.username = 'required' };
if (!values.password) { errors.password = 'required' };
return errors;
}
const handleSubmit = (e) => {
const loginData = {
username: initialValues.username ,
password: initialValues.password
}
axiosWithAuth()
.post("/api/auth/login", loginData)
.then(res => {
setToken(res.data.token);
push("/protected");
})
.catch(err =>
console.error("bk: Login.js: login: err.message: ", err.message)
);
}
return (
<Formik
initialValues={initialValues}
validate={validate}
onSubmit={handleSubmit}
>
{formik => (
<Form>
<FormikControl
control='input'
type='text'
label='Username'
name='username'
/>
<FormikControl
control='input'
type='password'
label='Password'
name='password'
/>
<button type='submit'>Submit</button>
</Form>
)}
</Formik>
)
}
export default Login;
axiosWithAuth.js
import axios from "axios";
const axiosWithAuth = () => {
const token = localStorage.getItem("token");
return axios.create({
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
},
baseURL: "http://localhost:8000"
});
};
export default axiosWithAuth;
tokenStore.js
const tokenState = "";
//
export const getToken = () => {
return localStorage.getItem(tokenState);
};
//
export const setToken = (newToken) => {
localStorage.setItem(tokenState, newToken);
};
//
export const clearToken = () => {
localStorage.removeItem(tokenState);
};
PrivateRoute.js
import React from "react";
import { Route, Redirect } from "react-router-dom";
import { getToken } from './tokenStore';
const PrivateRoute = ({ component: Component, ...props }) => {
const token = getToken();
return (
<Route
{...props}
render = {() => {
return token ? <Component />: <Redirect to="/login" />;
}}
/>
);
};
export default PrivateRoute;
App.js
import React from 'react';
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import Login from './Components/Login';
import PrivateRoute from './utilils/PrivateRoute';
import Registration from './Components/SignUp';
import DevPage from './Pages/Dev';
function App() {
return (
<Router >
<div>
<nav>
<Link to="/login">Login </Link>
<Link to="/SignUP">SignUp </Link>
<Link to="/protected"> Dev</Link>
</nav>
<Switch>
<PrivateRoute
path="/protected"
component={DevPage} />
<Route exact path="/login" component={Login} />
<Route exact path="/SignUP"><Registration/></Route>
<Route component={Login} />
</Switch>
</div>
</Router>
);
}
export default App;
I plan on refactoring the code in ContextAPI once the tests turn out successful. All I'm asking for here is validation that the code is proper and to fill in the blanks of what I'm missing or what I need to correct. Let me know if you need the backend code as well and I'll add that in future edits. Thank you in Advance.

You're calling httpS://localhost:8000 in axiosWithAuth.js, the error is for an invalid or inexistent SSL certificate. Call http://localhost:8000 while you're in development and make sure to transition to https when you know where you're hosting this thing and have a valid url and ssl certificate

Related

Hi I am getting a undefined for my value in my form data, I assumed that I passed props correctly and that I set my keys for my form data correctly

Hi I am getting undefined in my form value for name, I thought I passed my props and set my event so that my form data can be pre-filled. I am not sure what is going on, is my props not being passed properly? I wonder if I am not declaring my my from data keys correctly. I am not sure what I am doing.
Main Container
import { Route, Routes, useParams, useNavigate } from "react-router-dom";
import { useState, useEffect } from "react";
import EventDetials from "../screens/EventDetails";
import EditEvent from "../screens/EditEvent";
// import CreateEvent from "../screens/CreateEvent";
import MainEvents from "../screens/EditEvent";
import { getAllEvents, getOneEvent, postEvent, putEvent, getUserEvent } from "../services/event.js";
export default function MainContainer(props) {
const [events, setEvents] = useState([]);
const params = useParams
const {id} = params
const history = useNavigate()
const [event, setEvent] = useState(null);
useEffect(() => {
const fetchEvents = async () => {
const eventList = await getAllEvents();
setEvents(eventList);
};
fetchEvents();
}, []);
console.log(events
const handleUpdateEvent = async (id, formData) => {
const eventItem = await putEvent(id, formData);
setEvent((prevState) =>
prevState.map((event) => {
return event.id === Number(id) ? eventItem : event;
})
);
history.push(`/event/${event.id}`);
};
return (
<div>
<Routes>
<Route path= "events" element={<MainEvents events={events} />} />
<Route path= "editEvent/:id" element={<EditEvent events={events} handleUpdateEvent={handleUpdateEvent} />} />
<Route
path= "eventsDetails"
element={<EventDetials events={events} />}
/>
<Route path= "createEvent" events= {events}/>
</Routes>
</div>
);
}
EditEvent screen component
import { Link, useParams, useNavigate } from "react-router-dom";
import { useState, useEffect } from "react";
export default function EditEvent(props) {
const params = useParams;
const { id } = params;
const history = useNavigate;
console.log(props.events)
const [formData, setFormData] = useState({
name: "",
place: "",
date: "",
time: "",
});
useEffect(() => {
const preFillFormData = () => {
const singleEvent = props.events.find(
eventItem => eventItem.id === Number(id)
);
setFormData({
name: singleEvent.name,
place: singleEvent.place,
date: singleEvent.date,
time: singleEvent.time,
});
};
if (props.events.length) {
preFillFormData();
}
}, [props.events, id]);
const handleChange = (e) => {
const { name, value } = e.target;
setFormData((prevState) => ({
...prevState,
[name]: value,
}));
};
const handleSubmit = (e) => {
e.preventDefault();
props.handleUpdateEvent(id, formData);
};
return (
<div>
<form onSubmit={handleSubmit}>
<label>
Name:
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
/>
</label>
<label>
Place
<input
type="text"
name="place"
value={formData.place}
onChange={handleChange}
/>
</label>
<label>
Date
<input
type="text"
name="date"
value={formData.date}
onChange={handleChange}
/>
</label>
<label>
Time
<input
type="text"
name="time"
value={formData.time}
onChange={handleChange}
/>
</label>
<button type="submit">Submit</button>
</form>
</div>
);
}

Why I am getting undefined from the localStorage?

I am trying to learn firebase by reading their documentation. I have added login using google account and email password. When I click on log in, I am dispatching setLogInValue and the state is updating but getting the value of isLoggedIn: undefined. But when I first visit the page the value of isLoggedIn is null. I expect the value of isLoggedIn: true after clicking on log in and redirect to homepage but as I am getting undefined the routing is not working.
slice.js
import { createSlice } from '#reduxjs/toolkit';
const initialState = {
email: localStorage.getItem('email'),
isLoggedIn: localStorage.getItem('loggedInStatus'),
};
export const slice = createSlice({
name: 'slice',
initialState,
reducers: {
setLogInValue: (state, action) => {
state.email = localStorage.setItem('email', action.payload);
state.isLoggedIn = localStorage.setItem('loggedInStatus', true);
},
},
});
export const { setLogInValue } = slice.actions;
export default slice.reducer;
Login.jsx
import React, { useState } from 'react';
import {
getAuth,
signInWithPopup,
signInWithEmailAndPassword,
GoogleAuthProvider,
} from 'firebase/auth';
import { useDispatch } from 'react-redux';
import { setLogInValue } from '../redux/slice';
import { useNavigate } from 'react-router-dom';
import { auth } from '../firebase/firebase';
const Login = () => {
const [input, setInput] = useState({ email: '', password: '' });
const inputHandler = event => {
setInput({ ...input, [event.target.name]: event.target.value });
};
const dispatch = useDispatch();
const navigate = useNavigate();
// const auth = getAuth();
const googleSignIn = () => {
const provider = new GoogleAuthProvider();
signInWithPopup(auth, provider)
.then(result => {
const credential = GoogleAuthProvider.credentialFromResult(result);
const token = credential.accessToken;
// The signed-in user info.
const user = result.user;
dispatch(setLogInValue(user.email));
// localStorage.setItem('loggedInStatus', true);
navigate('/', { replace: true });
})
.catch(error => {
// Handle Errors here.
const errorCode = error.code;
const errorMessage = error.message;
// The email of the user's account used.
const email = error.customData.email;
// The AuthCredential type that was used.
const credential = GoogleAuthProvider.credentialFromError(error);
// ...
console.log(errorMessage);
});
};
const submitHandler = event => {
event.preventDefault();
// const auth = getAuth();
signInWithEmailAndPassword(auth, input.email, input.password)
.then(userCredential => {
// Signed in
const user = userCredential.user;
console.log(user);
dispatch(setLogInValue(input.email));
setInput({
email: '',
password: '',
});
navigate('/', { replace: true });
})
.catch(error => {
const errorCode = error.code;
const errorMessage = error.message;
console.log(errorMessage);
});
};
return (
<div className="container">
<form
className="w-75 mx-auto mt-5"
onSubmit={event => submitHandler(event)}>
<div className="form-floating mb-3">
<input
name="email"
type="email"
className="form-control"
placeholder="name#example.com"
value={input.email}
onChange={inputHandler}
/>
<label>Email address</label>
</div>
<div className="form-floating">
<input
name="password"
type="password"
className="form-control"
placeholder="Password"
value={input.password}
onChange={inputHandler}
/>
<label>Password</label>
</div>
<button
type="submit"
className="btn btn-primary btn-lg d-block mx-auto mt-3">
Log in
</button>
</form>
<button
className="btn btn-primary btn-lg d-block mx-auto mt-3"
onClick={googleSignIn}>
Log in With Google
</button>
</div>
);
};
export default Login;
Home.jsx
import React from 'react';
import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap/dist/js/bootstrap.min.js';
import 'bootstrap-icons/font/bootstrap-icons.css';
import { Routes, Route, Navigate } from 'react-router-dom';
import Nav from './components/Nav';
import Home from './pages/Home';
import Login from './pages/Login';
import { useSelector } from 'react-redux';
const App = () => {
const { isLoggedIn } = useSelector(state => state.info);
console.log('App:', isLoggedIn);
let routes;
if (isLoggedIn) {
routes = (
<>
<Nav />
<Routes>
<Route path="/" element={<Home />} />
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</>
);
} else {
routes = (
<>
<Routes>
<Route path="/login" element={<Login />} />
<Route path="*" element={<Navigate to="/login" replace />} />
</Routes>
</>
);
}
return <>{routes}</>;
};
export default App;
Nav.jsx
import React from 'react';
import { Link } from 'react-router-dom';
const Nav = () => {
return (
<nav className="navbar navbar-expand-lg bg-light ">
<div className="container">
<Link className="navbar-brand" to="/">
Navbar
</Link>
<Link className="nav-link" to="/">
Home
</Link>
</div>
</nav>
);
};
export default Nav;
Why I am getting undefined instead of "true/false/" of isLoggedIn value from the localStorage? How can I fix that?
The problem is in the code in 'slice.js':
setLogInValue: (state, action) => {
state.email = localStorage.setItem('email', action.payload);
state.isLoggedIn = localStorage.setItem('loggedInStatus', true); // <<<< Problem here: function localStorage.setItem return 'undefined' value
},
so you should fix like this:
setLogInValue: (state, action) => {
localStorage.setItem('loggedInStatus', true)
localStorage.setItem('email', action.payload);
state.email = action.payload;
state.isLoggedIn = true;
},

how do we pass the user id into a route in app.js?

my teammate and I are stuck on solving a critical problem, which is how do we pass the user_id from one component to another in app.js . For example, we are able to register, login, and logout perfectly; but when we try to submit information in another component like personal form it says user_id is not defined. Also we are using JWT Tokens for authorization, and authentication. We are using local storage only, we did not implement redux.
App.js
import React, { Fragment, useState, useEffect } from "react";
import "./App.css";
import {
BrowserRouter as Router,
Routes,
Route,
Navigate,
} from "react-router-dom";
import { toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
// import { useNavigation } from '#react-navigation/native';
import Home from "./components/Home";
import Login from "./components/Login";
import Register from "./components/Register";
import MedicalForm from "./components/MedicalForm";
import PersonalForm from "./components/PersonalForm";
import Navbar from "./components/Navbar/index";
toast.configure();
function App() {
// we want to make sure it set to false first
const [isAuthenticated, setAuthenticated] = useState(false);
//this is going to the be toggle function to set the auth
const setAuth = (Boolean) => {
setAuthenticated(Boolean);
};
// this is going to check if the user is authenticated even if the
// page is refreshed
async function isAuth() {
try {
const response = await fetch("http://localhost:4001/auth/is-verify", {
method: "GET",
headers: { token: localStorage.token },
});
const parseRes = await response.json();
parseRes === true ? setAuthenticated(true) : setAuthenticated(false);
console.log(parseRes);
} catch (err) {
console.error(err.message);
}
}
useEffect(() => {
isAuth();
});
return (
<Fragment>
<Router>
{/* reason why we use render instead of component props is because
anytime we send props to a component we don't want it to remount */}
<Navbar />
<div className="container">
<Routes>
{/* if(!isAuthenticated){ if this is true, pass setAuth to Login, and if it comes out true, then navigate to login page
<Login setAuth={setAuth} />}
else{
<Navigate to="/home" />
} */}
<Route
exact
path="/login"
element={
!isAuthenticated ? (
<Login setAuth={setAuth} />
) : (
<Navigate to="/home" />
)
}
/>
<Route
exact
path="/register"
element={
!isAuthenticated ? (
<Register setAuth={setAuth} />
) : (
<Navigate to="/login" />
)
}
/>
<Route
exact
path="/home"
element={
isAuthenticated ? (
<Home setAuth={setAuth} />
) : (
<Navigate to="/login" />
)
}
/>
<Route
exact
path="/mform"
element={
isAuthenticated ? (
<MedicalForm setAuth={setAuth} />
) : (
<Navigate to="/login" />
)
}
/>
<Route
exact
path="/pform"
element={
isAuthenticated ? (
<PersonalForm setAuth={setAuth} />
) : (
<Navigate to="/login" />
)
}
/>
</Routes>
</div>
</Router>
</Fragment>
);
}
export default App;
PersonalForm.js
`Login.js
import React, { Fragment, useState } from "react";
// import { Link } from "react-router-dom";
import { toast } from "react-toastify";
const Personalform = (props) => {
const [username, setUsername] = useState("");
const [inputs, setInputs] = useState({
first_name: "",
last_name: "",
pronoun: "",
occupation: "",
phone_number: "",
city: "",
state: "",
zip: "",
});
const {
first_name,
last_name,
pronoun,
occupation,
phone_number,
city,
state,
zip,
} = inputs;
const onChange = (e) => {
// take in every input and target the input value of name
//like email,username, and password
setInputs({ ...inputs, [e.target.name]: e.target.value });
};
const onSubmitForm = async (e) => {
e.preventDefault();
try {
const body = {
first_name,
last_name,
pronoun,
occupation,
phone_number,
city,
state,
zip,
};
// console.log(user_id)
const response = await fetch(
`http://localhost:4001/pform/${props.user_id}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
token: localStorage.token,
},
body: JSON.stringify(body),
}
);
const parseRes = await response.json();
setUsername(parseRes.username);
if (parseRes.token) {
// we want to save the token to our local storage
localStorage.setItem("token", parseRes.token);
console.log(parseRes);
//now we want to setAuth to true
props.setAuth(true);
toast.success("submit succesfully"); // then use toastify
} else {
// if false
props.setAuth(false); // set auth to false
toast.error(parseRes); // set the toast to send and error
}
} catch (err) {
console.error(err.message);
}
};
const logout = (e) => {
e.preventDefault();
localStorage.removeItem("token");
props.setAuth(false);
toast.success("Logged out successfully");
};
return (
<Fragment>
{username}
<h1 className="text-center my-5">Personal Form</h1>
<form onSubmit={onSubmitForm}>
<input
type="text"
// this is a name of an input
name="first_name"
placeholder="first_name"
className="form-control my-3"
value={first_name}
onChange={(e) => onChange(e)}
/>
<input
type="text"
name="last_name"
placeholder="Last Name"
className="form-control my-3"
value={last_name}
onChange={(e) => onChange(e)}
/>
<input
type="text"
name="pronoun"
placeholder="pronoun"
className="form-control my-3"
value={pronoun}
onChange={(e) => onChange(e)}
/>
<input
type="text"
name="occupation"
placeholder="occupation"
className="form-control my-3"
value={occupation}
onChange={(e) => onChange(e)}
/>
<input
type="text"
name="phone_number"
placeholder="phone number"
className="form-control my-3"
value={phone_number}
onChange={(e) => onChange(e)}
/>
<input
type="text"
name="city"
placeholder="city"
className="form-control my-3"
value={city}
onChange={(e) => onChange(e)}
/>
<input
type="text"
name="state"
placeholder="state"
className="form-control my-3"
value={state}
onChange={(e) => onChange(e)}
/>
<input
type="text"
name="zip"
placeholder="zip"
className="form-control my-3"
value={zip}
onChange={(e) => onChange(e)}
/>
<button className="btn btn-success btn-block">Submit</button>
</form>
<button className="btn btn-primary" onClick={(e) => logout(e)}>
logout
</button>
</Fragment>
);
};
export default Personalform;
index.js or the navbar component
import React from "react";
import {
Nav,
NavLink,
Bars,
NavMenu,
NavBtn,
NavBtnLink,
} from "./NavbarElements";
const Navbar = () => {
return (
<>
<Nav>
<NavLink to="/">
<h1>Logo</h1>
</NavLink>
<Bars />
<NavMenu>
<NavLink to="/pform" activeStyle>
Personal Form
</NavLink>
</NavMenu>
<NavBtn>
<NavBtnLink to="/login">Login</NavBtnLink>
</NavBtn>
</Nav>
</>
);
};
export default Navbar;
login.js
import React, { Fragment, useState } from "react";
import { Link } from "react-router-dom";
import { toast } from "react-toastify";
const Login = ({ setAuth }) => {
const [inputs, setInputs] = useState({
email: "",
password: "",
});
const { email, password } = inputs;
const onChange = (e) => {
setInputs({ ...inputs, [e.target.name]: e.target.value });
};
const onSubmitForm = async (e) => {
e.preventDefault();
try {
const body = { email, password };
const response = await fetch("http://localhost:4001/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
const parseRes = await response.json();
if (parseRes.token) {
localStorage.setItem("token", parseRes.token);
setAuth(true);
toast.success("login successfully!");
} else {
setAuth(false);
toast.error(parseRes);
}
} catch (err) {
console.error(err.message);
}
};
return (
<Fragment>
<h1>Login</h1>
<form onSubmit={onSubmitForm}>
<input
type="email"
name="email"
placeholder="email"
className="form-control my-3"
value={email}
onChange={(e) => onChange(e)}
/>
<input
type="password"
name="password"
placeholder="password"
className="form-control my-3"
value={password}
onChange={(e) => onChange(e)}
/>
<button className="btn btn-success btn-block">submit</button>
</form>
<Link to="/register">Register</Link>
</Fragment>
);
};
export default Login;
Register
import React, { Fragment, useState } from "react";
import { Link } from "react-router-dom";
import { ToastContainer, toast } from "react-toastify";
const Register = ({ setAuth }) => {
const [inputs, setInputs] = useState({
email: "",
password: "",
username: "",
});
const { email, password, username } = inputs;
const onChange = (e) => {
// take in every input and target the input value of name
//like email,username, and password
setInputs({ ...inputs, [e.target.name]: e.target.value });
};
const onSubmitForm = async (e) => {
e.preventDefault();
try {
const body = { email, password, username };
const response = await fetch("http://localhost:4001/auth/register", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
const parseRes = await response.json();
if (parseRes.token) {
localStorage.setItem("token", parseRes.token);
setAuth(true);
toast.success("Registered Successfully!");
} else {
setAuth(false);
toast.error(parseRes);
}
} catch (err) {
console.error(err.message);
}
};
return (
<Fragment>
<h1 className="text-center my-5">Register</h1>
<form onSubmit={onSubmitForm}>
<input
type="email"
// this is a name of an input
name="email"
placeholder="email"
className="form-control my-3"
value={email}
onChange={(e) => onChange(e)}
/>
<input
type="password"
name="password"
placeholder="password"
className="form-control my-3"
value={password}
onChange={(e) => onChange(e)}
/>
<input
type="text"
name="username"
placeholder="username"
className="form-control my-3"
value={username}
onChange={(e) => onChange(e)}
/>
<button className="btn btn-success btn-block">Submit</button>
</form>
<Link to="/login">Login</Link>
</Fragment>
);
};
export default Register;
jwtauth.js
const router = require("express").Router();
const { json, response } = require("express");
const pool = require("../db");
const bcrypt = require("bcrypt");
const jwtGenerator = require("../utils/jwtGenerator");
const validInfo = require("../middleware/validInfo");
const authorization = require("../middleware/authorization");
//registering
router.post("/register", validInfo, async (req, res) => {
try {
// 1. destructure the req.body(name,email,password)
const { username, email, password } = req.body;
// 2. check if user exists (if user exists then throw error)
const user = await pool.query(
"SELECT * FROM login_credentials WHERE email =$1",
[email]
);
if (user.rows.length !== 0) {
return res.status(401).json("User already exists");
}
// res.json(user.rows);
// 3. bycrpyt the user password
const saltRound = 10;
const salt = await bcrypt.genSalt(saltRound);
const bcryptPassword = await bcrypt.hash(password, salt);
// 4. enter the new user inside our database
const newUser = await pool.query(
"INSERT INTO login_credentials (username,email,password) VALUES ($1,$2,$3) RETURNING *",
[username, email, bcryptPassword]
);
// res.json(newUser.rows[0]);
// 5. generate our jwt token
const token = jwtGenerator(newUser.rows[0].user_id);
res.json({ token });
} catch (err) {
console.error(err.message);
res.status(500).send("server error");
}
});
//login route
router.post("/login", validInfo, async (req, res) => {
try {
//1. destructure the req.body
const { email, password } = req.body;
//2. check if user doesn't exist (if not we throw error)
const user = await pool.query(
"SELECT * FROM login_credentials WHERE email=$1",
[email]
);
if (user.rows.length === 0) {
return res.status(401).json("password or email is incorrect");
}
//3. check if incoming password is the same the database password
const validPassword = await bcrypt.compare(password, user.rows[0].password);
console.log(validPassword);
if (!validPassword) {
return res.status(401).json("password or email is incorrect");
}
//4. give the jwt token
const token = jwtGenerator(user.rows[0].user_id);
res.json({ token });
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
});
router.get("/is-verify", authorization, async (req, res) => {
try {
res.json(true);
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
});
module.exports = router;
authorization.js
const jwt = require("jsonwebtoken");
require("dotenv").config();
// before it hits routes it's going to get access to the requested
// resonse then if everything ends up working ok, it will continue on
// with the process of next so it can keep going with the routes
module.exports = async (req, res, next) => {
try {
const jwtToken = req.header("token");
if (!jwtToken) {
return res.status(401).json("Not Authorized");
}
// if this is verified it is going to return us a payload that we can use within our routes
const payload = jwt.verify(jwtToken, process.env.jwtSecret);
req.user = payload.user;
next();
} catch (err) {
console.error(err.message);
return res.status(403).json("Not Authorized");
}
};
validInfo.js
module.exports = (req, res, next) => {
const { email, username, password } = req.body;
function validEmail(userEmail) {
return /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(userEmail);
}
if (req.path === "/register") {
if (![email, username, password].every(Boolean)) {
return res.status(401).json("Missing Credentials");
} else if (!validEmail(email)) {
return res.status(401).json("Invalid Email");
}
} else if (req.path === "/login") {
if (![email, password].every(Boolean)) {
return res.status(401).json("Missing Credentials");
} else if (!validEmail(email)) {
return res.status(401).json("Invalid Email");
}
}
next();
};
you can create a file for auth context then create a context and export it
export const AuthContext= React.createContext({userId:"",setUserId: ()=>{}}); //the param here should be the default value (default shape of the object you wanna pass to children)
Then in your app.js import that context and wrap your other components with
const [userId, setUserId] = useState("");
<AuthContext.Provider value={{userId:userId, setUserId:setUserId}}> //the object contains the state or functions you wanna access from child components
//your other components
</AuthContext.Provider>
now inside any screen or component that needs to access or set the user id (or any other value or callback you passed) you can just import the context and access the value like this
const {userId,setUserId} = useContext(AuthContext)
you can also refer to this example:
https://fatmali.medium.com/use-context-and-custom-hooks-to-share-user-state-across-your-react-app-ad7476baaf32
You can set vars in res object like this in node.js. But it your code is react not node.js.
res.user.user_id = user_id;
and then use it anywhere with:
let user_id = res.user.user_id;

react js user gets redirected to another page on refresh

I have a react js app with react-router-dom v6 to handle the routes. The routes functionality worked just fine before i added firebase firestore, but now for some reason when i'm in the seeker page and i reload it, the home page gets rendered. It's not a problem with user authentication because that's handled by the login component, but i couldn't find the problem in my configuration.
This is my app component.
import React, {useEffect, useState} from "react";
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import { createBrowserHistory } from 'history';
import ReactDOM from "react-dom";
import "./styles/style.scss";
import Home from "./pages/home";
import Seeker from "./pages/seeker";
import NotFound from "./pages/notFound";
import Login from "./pages/login";
const App = () => {
const [token, setToken] = useState(false);
useEffect(() => {
if(localStorage.getItem("token") === null){
setToken(false);
} else {
setToken(true);
}
}, [])
return (
<BrowserRouter history={createBrowserHistory}>
<Routes>
<Route exact path="/" element={<Login isAuth={token}/>}/>
<Route exact path="/home" element={token ? <Home /> : <Navigate to="/"/>}/>
<Route exact path="/seeker" element={token ? <Seeker /> : <Navigate to="/"/>}/>
<Route path="*" element={<NotFound />}/>
</Routes>
</BrowserRouter>
)
}
ReactDOM.render(
<App />,
document.getElementById("root"))
The login component sets the token on localstorage correctly and the routes use it to render conditionally the components. But when i'm in the "/seeker" url the refresh takes me to "/home".
import React, { useState, useEffect } from "react";
import { Formik, Form, Field, ErrorMessage } from 'formik';
import axios from "axios";
require("babel-core/register");
require("babel-polyfill");
import GridRender from "../components/gridRender";
import { NavBar } from "../components/navBar";
async function getHeros(name) {
return await axios.get(
`https://www.superheroapi.com/api.php/${name}`
);
}
const Seeker = () => {
const [searchedHero, setSearchedHero] = useState("")
const [matchedHeros, setMatchedHeros] = useState([]);
useEffect(() => {
let isMounted = true;
if (isMounted) {
getHeros(searchedHero).then((heros) => {
const hero = heros.data.response != "error" ? heros.data.results : []
localStorage.setItem("addHeroAction", "true");
setMatchedHeros(hero);
}, console.error);
}
return () => { isMounted = false };
}, [searchedHero]);
return (
<div>
<NavBar />
<div className="seeker">
<h1 className="title">Find your next teammate!</h1>
<Formik /> //// here is a large form that i erased so it doesn't get long
{matchedHeros.length != 0 && <GridRender matchedHeros = {matchedHeros}/>}
</div>
</div>
)
}
export default Seeker;
And here is my firebase config (i use version 9) in case it's important because the bug appeared after firebase was implemented.
import { initializeApp } from "firebase/app";
import "regenerator-runtime/runtime";
import { addDoc, deleteDoc, doc } from 'firebase/firestore';
import { getFirestore, collection } from 'firebase/firestore'
const firebaseConfig = {
apiKey: "xxxxxxxxxxxxxxxxxx",
authDomain: "xxxxxxxxxxxxxxxxxxxx",
databaseURL: "xxxxxxxxxxxxxxxxxxxxx",
projectId: "superteam-maker",
storageBucket: "xxxxxxxxxxxxxxx",
messagingSenderId: "xxxxxxxxxxxxx",
appId: "xxxxxxxxxxxxxxxxxxxxxxxxxx"
};
initializeApp(firebaseConfig);
const db = getFirestore();
const colRef = collection(db, "team")
const addHero = (hero) => {
addDoc(colRef, {
name : hero.name,
powerstats :{
combat: parseInt(hero.powerstats.combat),
durability: parseInt(hero.powerstats.durability),
intelligence: parseInt(hero.powerstats.intelligence),
power: parseInt(hero.powerstats.power),
speed: parseInt(hero.powerstats.speed),
strength : parseInt(hero.powerstats.strength)
},
id: parseInt(hero.id),
image: {url : hero.image.url},
} )
}
const deleteHero = (hero) => {
const docRef = doc(db, "team", hero.id);
deleteDoc(docRef);
}
export {addHero, deleteHero, colRef}
////////UPDATE/////////
The login component autehnticates the user with a test API that returns a random token, so the user can only enter if a certain email and password get subbmited. If the auth is successfull the user gets rendered to "/home".
Here's the code fro the login component.
import React, {useEffect, useState} from "react";
import { Formik } from 'formik';
import { Navigate } from 'react-router-dom';
import axios from "axios";
const Login = (props) => {
const [token, setToken] = useState(false);
useEffect(() => {
if(localStorage.getItem("token") === null){
setToken(false);
} else {
setToken(true);
}
}, [])
const verification = () => {
if( token && props.isAuth){
return <Navigate to="/home"/>
}
}
let logInError = {};
return(
<div className="login__background">
<div className="login container">
{verification()}
<h1 className="display-1 title login__title">Make a team as <span>powerfull</span> as you!</h1>
<h5 className="login-title__sub">Please log in to create your superteam</h5>
<Formik
initialValues={{ email: '', password: '' }}
validate={values => {
const errors = {};
if (!values.email) {
errors.email = 'Required';
} else if (
!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
) {
errors.email = 'Invalid email address';
}
if (!values.password){
errors.password = "Required";
}
return errors;
}}
onSubmit={(values) => {
axios.post("http://challenge-react.alkemy.org/", values)
.then(response => {
let token = response.data.token
localStorage.setItem("token", token);
localStorage.setItem("addHeroAction", false);
setToken(true);
return <Navigate to="/home"/>
})
.catch(error => {
if (error.response) {
logInError = error.response.data.error;
window.alert(logInError);
} else if (error.request) {
console.log(error.request);
} else {
console.log('Error', error.message);
}
console.log(error.config);
})
}}>
{({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit
}) => (
<form onSubmit={handleSubmit}>
<label className="form-label label" name="email">Email</label>
<br />
<input
className="form-control form-control-lg input"
type="email"
name="email"
onChange={handleChange}
onBlur={handleBlur}
value={values.email}
/>
{errors.email && touched.email && errors.email}
<br />
<label className="form-label label " name="password">Password</label>
<br />
<input
className="form-control form-control-lg input"
type="password"
name="password"
onChange={handleChange}
onBlur={handleBlur}
value={values.password}
/>
{errors.password && touched.password && errors.password}
<br />
<button type="submit" className="btn btn-danger">
Submit
</button>
</form>
)}
</Formik>
</div>
</div>
)
}
export default Login;
It also gets an IsAuth propr from App.js in case it's the first time the user enters the page and there is no token saved.
The issue I see is the token state being initially false. The useEffect runs at the end of the render cycle, so for the initial render token is false
const [token, setToken] = useState(false);
useEffect(() => {
if (localStorage.getItem("token") === null) {
setToken(false);
} else {
setToken(true);
}
}, []);
and any Route being rendered using the token state will use this initial false value and act accordingly.
<Route path="/seeker" element={token ? <Seeker /> : <Navigate to="/" />} />
If on the "/seeker" path and the token value is false, the Navigate component is rendered.
Assuming all your authentication flow is correct and working and sets the token value into localStorage then you should initialize the token state from localStorage.
const initializeState = () => !!JSON.parse(localStorage.getItem("token"));
const [token, setToken] = useState(initializeState);

Error: Actions must be plain objects. Use custom middleware for async actions. React-redux error

index.js
import store from "./store";
import { createStore } from 'redux';
import reducers from './reducers';
import { Provider } from 'react-redux';
console.log('init state', store.getState());
ReactDOM.render(
<Provider store={ store }>
<App />
</Provider>,
document.getElementById('root')
);
registerServiceWorker();
action.js
import * as types from "./types";
import axios from 'axios';
import { incrementProgress, decrementProgress } from "./progress";
const userLogin = username => ({
type: types.AUTH_LOGIN,
username,
});
const userLogout = () => ({
type: types.AUTH_LOGOUT,
});
const fakeLoginRequest = (username, password) => {
alert(username, password)
}
export const doLogin = username => {
alert(username)
};
export const doLogout = () => dispatch => {
dispatch(userLogout());
};
LoginForm.jsx - Dumb Component
class LoginForm extends React.Component {
constructor(props) {
super(props);
this.state = {
username: '',
password: '',
};
this.handleClick = this.handleClick.bind(this);
}
fakeLogin = e => {
const { username } = this.state;
e.preventDefault();
if (!username) {
return alert("Provide a username.");
}
this.props.doLogin(username);
this.setState({ username: "" });
};
render() {
return (
<div>
<MuiThemeProvider>
<div>
<TextField
hintText="Enter your Username"
floatingLabelText="Username"
onChange = {(event,newValue) => this.setState({username:newValue})}
/>
<br/>
<TextField
type="password"
hintText="Enter your Password"
floatingLabelText="Password"
onChange = {(event,newValue) => this.setState({password:newValue})}
/>
<br/>
<RaisedButton label="Submit" primary={true} style={style} onClick={this.fakeLogin}/>
</div>
</MuiThemeProvider>
</div>
);
}
}
export default LoginForm;
LoginPage.jsx - Wise Component
const LoginPage = ({ auth, doLogin, doLogout }) => (
<div>
<NavBar/>
<div className="row">
<div style={{display: 'flex', justifyContent: 'center'}} className="col-md-4 col-md-offset-4">
<LoginForm doLogin={doLogin} doLogout={doLogout} auth={auth} />
</div>
</div>
</div>
);
const mapStateToProps = state => ({
auth: state.auth,
});
export default connect(mapStateToProps, { doLogin, doLogout })(LoginPage);
I am totally new to react-redux and I am trying to create a flow that recognizes the current login status of the user. What I am trying to with the codes above is that I wanted to make sure that the username and password pass correctly and alerts them.
However, I get the error
Error: Actions must be plain objects. Use custom middleware for async actions. React-redux error.
On this.props.doLogin(username); of the LoginForm.jsx. It seems like it's also related to the onClick of the button. I don't understand because the function that I am calling is not async.
What am I doing wrongly?
Like the error says, your action creators have to return an object.
export const doLogin = username => {
console.log('READ THE DOCS. IT IS VERY HELPFUL TO READ THE DOCS.');
console.log(username);
return {
type: 'YOUR_ACTION_TYPE',
payload: { whatever: 'you', want: 'it', to: 'be' }
};
};

Categories