I am building a user login system and currently the way I have it set up, the user types in their email and password and then that information is sent to my server to be checked if accurate. If it is, I create a JWT token and send it back along with the mongodb _id of the user, I then in another request use the mongodb _id of the user from the first request to retrieve all the information (blogs in this case) associated with their mognodb _id. All of this works except for the issue I am running into is the axios request does not work the first time. So I have to hit submit to log in for an error to occur TypeError: Cannot read properties of undefined (reading 'data') and then when I hit submit again it works. I believe the problem is getBasic not getting set before the rest of the code runs on the first attempt, and on the second attempt it is already defined. I am not sure how I can fix this without maybe a manual timer before I do the getBasic.data.map, but I know there must be a better way to fix this.
And just to add this is just me trying to test this concept so I understand later things like bcrypt will need to be added to the password instead of plaintext in the database, etc.
Frontend:
function Login() {
const [user, setUser] = useState()
const [getBasic, setGetBasic] = useState()
const [Item, setItem] = useState({
email: '',
password: ''
});
async function fetchData() {
const sentItem = {
user: user.data.user._id,
}
await axios.post('http://localhost:3001/api', sentItem)
.then(result => setGetBasic(result))
}
const loginFunc = async () => {
const checkUser = {
email: Item.email,
password: Item.password
}
await axios.post('http://localhost:3001/api/login', checkUser)
.then(result => setUser(result))
.then(fetchData())
}
if (user && getBasic) {
localStorage.setItem('key', JSON.stringify(user.data.token))
console.log(localStorage.getItem('key'))
return (
<div>
<div>
<input onChange={handleChange} name="email" value={Item.email} placeholder="email" />
<input onChange={handleChange} name="password" value={Item.password} placeholder="password" />
<button onClick={loginFunc}>Submit</button>
</div>
{
getBasic.data.map(({ text, _id, }, i) => {
return (
<div>
<p>{_id}</p>
<p>{text}</p>
</div>
);
})
}
)
}
return (
<div>
<input onChange={handleChange} name="email" value={Item.email} placeholder="email" />
<input onChange={handleChange} name="password" value={Item.password} placeholder="password" />
<button onClick={loginFunc}>Submit</button>
</div>
)
}
export default Login
Server:
app.post('/api/login', async (req, res) => {
const user1 = await User.findOne({ email: req.body.email, password: req.body.password })
if (user1) {
const payload = { name: req.body.name, email: req.body.email };
const token = jwt.sign(payload, private_key, { expiresIn: '200s' });
console.log('correct login')
res.status(200).send({ auth: true, token: true, user: user1 })
} else {
console.log('incorrect login')
}
})
app.post('/api', async (req, res) => {
Blog.find({ user: req.body.user }, function (err, result) {
if (err) {
console.log(err)
} else {
res.send(result)
}
})
})
While nature of React.useState is asynchronous, and changes will not be reflected immediately, closures take their role in situations like this, as pointed out in link in below.
More details provided here - The useState set method is not reflecting a change immediately
As stated in error from your example, cannot read data of undefined - meaning user is undefined, and you try to access to user.data which leads to error.
const sentItem = {
user: user.data.user._id,
}
Instead of
await axios.post('http://localhost:3001/api/login', checkUser)
.then(result => setUser(result))
.then(fetchData())
why don't you try this (remove fetchData function if not reused on other places...):
const result = await axios.post('http://localhost:3001/api/login', checkUser);
const sentItem = {
user: result.data.user._id,
}
axios.post('http://localhost:3001/api', sentItem)
.then(result => setGetBasic(result));
setUser(result);
I think you should create user state like this
const [user, setUser] = useState({})
.then(fetchData())
The then method accepts a function which runs when the other stuff is done. However, you are not passing in a function here, but the return result from calling fetchData.
Try
.then(fetchData)
or
.then(() => fetchData())
Related
For now, my test site is as follows:
First a user can register an account, after which they will be able to update their profile, since when registering, all they input is their email and password, so their name attribute is still an empty string.
My User model:
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
email: {type: String, required: true},
passwordHash: {type:String, required: true},
firstName: {type:String, default:""},
});
const User = mongoose.model("user", userSchema);
module.exports = User;
Other things like lastName, address, bio, etc will be easily added once I figure this issue out.
After registering, the user will have access to a simple form that allows them to enter a name to create a profile (for now, just the first name).
My CreateProfile.js file that I use as a React component is as follows:
import React, { useContext, useState } from "react";
import axios from "axios";
import AuthContext from "../context/AuthContext";
import { useNavigate } from "react-router-dom";
function CreateProfile() {
const [firstName, setFirstName] = useState("");
const {getLoggedIn} = useContext(AuthContext);
const navigate = useNavigate();
async function createProfile(e) {
e.preventDefault();
try {
const profileData = {
firstName,
};
console.log(profileData)
await axios.post("http://localhost:5000/auth/createProfile", profileData);
await getLoggedIn();
navigate("/");
} catch (error) {
console.error(error);
}
}
return (
<div>
<h1>Create your profile</h1>
<form onSubmit={createProfile}>
<input type="text"
placeholder="First Name"
onChange={(e) => setFirstName(e.target.value)}
value={firstName}
/>
<button type="submit">Create Profile</button>
</form>
</div>
);
}
export default CreateProfile;
When the "Create Profile" button gets clicked, you get automatically rerouted back to the temporary "home page" via navigate("/"), a feature that does work correctly at the moment.
The profileData gets sent into the userRouter, who's relevant code is as follows:
router.post("/createProfile", async (req,res) => {
try {
const updatedUser = await User.findOneAndUpdate(
{ $set: {firstName: req.body.firstName} },
{ new: false }
);
res.json(updatedUser);
} catch (error) {
console.error(error);
res.status(500).send();
}
});
And just to make sure I handled my route setup correctly, here's my server-side index.js:
const express = require("express");
const mongoose = require("mongoose");
const dotenv = require("dotenv");
const cookieParser = require("cookie-parser");
const cors = require("cors");
dotenv.config();
//set up server
const app = express();
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port: ${PORT}`));
app.use(express.json());
app.use(cookieParser());
app.use(cors({
origin: ["http://localhost:3000"],
credentials: true,
}));
//remove the warning
mongoose.set('strictQuery', true)
//connect to mongoDB
mongoose.connect(process.env.MDB_CONNECT, {
useNewUrlParser: true,
useUnifiedTopology: true,
}, (err) => {
if (err) return console.error(err);
console.log("Connected to MongoDB");
});
// set up routes
app.use("/auth", require("./routers/userRouter"));
And one more, here's the front end AuthContext.js to handle checking if the user is logged in:
import React, { createContext, useEffect, useState } from "react";
import axios from "axios";
const AuthContext = createContext();
function AuthContextProvider(props) {
const [loggedIn, setLoggedIn] = useState(undefined);
async function getLoggedIn() {
const loggedInRes = await axios.get("http://localhost:5000/auth/loggedIn");
setLoggedIn(loggedInRes.data);
}
useEffect(() => {
getLoggedIn();
}, []);
return <AuthContext.Provider value={{loggedIn, getLoggedIn}}>
{props.children}
</AuthContext.Provider>;
};
export default AuthContext;
export {AuthContextProvider};
What did I do wrong that is preventing MongoDB from picking up the updated firstName? When I enter "test" in the form and click the submit button, the object does get created but doesn't go to MongoDB. What should I do instead, and is my problem in the userRouter or somewhere else?
I've been looking around online for previously asked questions similar to this and tried a few different things in my userRouter.js file, before I ended up with I currently have (that still doesn't work), to no avail.
Attempt 1:
router.post("/createProfile", async (req,res) => {
var _id = req.body._id;
console.log(_id);
var profile = {
firstName: req.body.firstName,
}
User.findByIdAndUpdate(_id, req.body.firstName, {new:false}, function(
err,
profile
) {
if (err) {
console.log("err", err);
res.status(500).send(err);
} else {
console.log("success");
console.log(profile);
console.log(_id);
console.log(req.body.email);
res.send(profile);
}
});
});
I've also tried instead doing res.send(req.body.firstName) which is pretty silly and obviously didn't work (if you couldn't already tell, I'm new to this).
I've also tried:
try {
const profile = await User.create(req.body);
console.log("success");
res.send(profile);
} catch (error) {
console.log(req.body);
console.log("err", error);
res.status(500).send(error);
}
Which definitely did not work.
And finally,
try {
const {firstName} = req.body;
const test = User.updateOne(firstName);
res.send(test);
} catch (error) {
console.error(error);
res.status(500).send();
}
From other answers, I've seen things where people would do res.json() or res.send() or res.put() but I'm not sure which one I should be doing in my situation, though I doubt that's the root cause of my issue.
I'd appreciate any help, if you need any additional context or code I'll edit my post accordingly. My registration/login/logout stuff is working fine, I just can't for the life of me seem to create the post-registration profile creation functionality correctly.
To update a document previously created you will need to add the _id in the body or in the path params of the route. So you will need to add some logic in the front-end to retrieve the logged user's id.
// ... all imports
router.post('/createProfile', async (req, res) => {
const {_id, firstName} = req.body;
try {
const updatedUser = await User.findByIdAndUpdate(
_id,
{firstName: firstName},
{new: true}, // "new: true" or "returnOriginal: false"
);
res.status(200).json(updatedUser);
} catch (error) {
// ...
}
});
The function findByIdAndUpdate need the id, the new values and optionally a options object (https://mongoosejs.com/docs/api.html#model_Model-findByIdAndUpdate). "You should set the new option to true to return the document after update was applied".
You can also do this operation using findOneAndUpdate:
const updatedUser = await User.findOneAndUpdate(
{_id: new mongoose.Types.ObjectId(req.body._id)},
{$set: {firstName: req.body.firstName}},
{new: true}
);
EDIT1:
In the front-end you will need to retrieve the logged user's id. I think that this data is inside the return of the "getLoggedIn" function which is called You need to use your "loggedIn" data.
// ... all imports
function CreateProfile() {
const navigate = useNavigate();
const [firstName, setFirstName] = useState('');
const {
loggedIn, // Get the "loggedIn" user data
getLoggedIn
} = useContext(AuthContext);
async function createProfile(e) {
e.preventDefault();
try {
/**
* I think that the user is already logged in to fill the "first name",
* the "getLoggedIn" was already called and "loggedIn" has some data inside.
*/
const id = loggedIn.id;
await axios.post('http://localhost:5000/auth/createProfile', {
id,
firstName,
});
await getLoggedIn();
navigate('/');
} catch (error) {
console.error(error);
}
}
return (
<div>
<h1>Create your profile</h1>
<form onSubmit={createProfile}>
<input
type='text'
placeholder='First Name'
onChange={e => setFirstName(e.target.value)}
value={firstName}
/>
<button type='submit'>Create Profile</button>
</form>
</div>
);
}
I am new to programming and just started full-stack project with NodeJS and React. I read somewhere a while ago that saving JWT tokens inside http-only cookies is better in terms of security than just saving them in localstorage, so I decided to implement that, but don't know how.
This is my Login controller in express
const signIn = async (req: Request, res: Response): Promise<any> => {
const userEmail = req.body.email;
const userPassword = req.body.password;
const user: any = await User.findOne({ email: userEmail }).clone();
const isValid = await user.comparePassword(userPassword);
if (isValid) {
const tokenObject = utils.issueJWT(user);
res.cookie("jwt", tokenObject.token, {
httpOnly: true,
maxAge: tokenObject.expiresIn,
});
res.send(tokenObject.token);
} else
res
.status(401)
.json({ success: false, msg: "You entered the wrong password" });
};
But I don't know how to access stored cookie with React and then authenticate user.
This is my Login component in React
import { SyntheticEvent, useState } from "react";
import { Navigate } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
function SignIn() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [navigate, setNavigate] = useState(false);
const submit = async (e: SyntheticEvent) => {
e.preventDefault();
await fetch("http://localhost:8080/login", {
method: "POST",
headers: { "Content-type": "application/json" },
body: JSON.stringify({ email, password }),
credentials: "include",
});
setNavigate(true);
};
if(navigate){
return <Navigate to="/users"/>
}
return (
<form action="/login" method="post" onSubmit={submit}>
<div className="form-outline mb-4">
<input
type="email"
id="form2Example1"
className="form-control"
name="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<label className="form-label" htmlFor="form2Example1">
Email address
</label>
</div>
<div className="form-outline mb-4">
<input
type="password"
id="form2Example2"
className="form-control"
name="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<label className="form-label" htmlFor="form2Example2">
Password
</label>
</div>
<button type="submit" className="btn btn-primary btn-block mb-4">
Sign in
</button>
</form>
);
}
export default SignIn;
Token is really stored inside cookie if checked in inspect or POSTMAN:
postman screenshot
While I worked with EJS forms I had my personal implementation of verifying if the user was authenticated or not and it worked:
const verifyJWT = (req: Request, res: Response, next: NextFunction) => {
const signedToken = req.cookies.jwt;
if (signedToken) {
jwt.verify(
signedToken,
config.PRIV_KEY,
{ algorithms: ["RS256"] },
async (err: any, decodedToken: any) => {
if (err) {
// tslint:disable-next-line:no-console
console.log(err);
} else {
// tslint:disable-next-line:no-console
console.log(decodedToken);
next();
}
}
);
} else {
res.redirect("/login");
}
};
Do I need to implement something similar to this?
You cannot access httpOnly cookie in browser it is just send along to browser so it can then be used in the backend . You can access it in your backend app .
I mean if httpOnly cookie is accessed in browser than it is similar to localStorage . Learn to implement the accessToken and refreshToken approach for Auethntication and authorization.
This is one example:
https://www.geeksforgeeks.org/jwt-authentication-with-refresh-tokens/#:~:text=Since%20access%20tokens%20aren't,in%20a%20very%20short%20duration.
I'm having a problem trying to learn authentication with JWT, cause I don't know what to do after checking the username and password with bcrypt and creating the token.
I was thinking that maybe it's because I don't understand how headers works, so I hope someone can help me with my problem.
Here's the relevant parts of my code:
Index (back-end where i'm generating the token and sending it to the front-end):
app.post('/login', (req, res) => {
usersModel.users.findOne({username: req.body.username}, (err, user) => {
if(err) {
res.json('ERRO!: ' + err)
}
else {
if(user!=null) {
bcrypt.compare(req.body.password, user.password, (err, response) => {
if(err) {
res.json('ERRO!: ' + err)
}
else {
if(response==true) {
const token = jwt.sign({
id: user._id
}, SECRET, { expiresIn: '30000'})
res.json({message: 'Passwords batem!', token})
}
if(response==false) {
res.json('Passwords não batem!')
}
}
})
}
}
})
})
Login.js:
import React from "react"
import {Link} from "react-router-dom" import { useState, useEffect } from "react";
export default function Login() {
const [username, setUsername] = useState("") const [password, setPassword] = useState("") const [listOfUsers, setListOfUsers] = useState([])
const handleSubmit = () => {
fetch('http://localhost:3001/login', {
method: 'POST', headers: {
'Content-Type': 'application/json'
}, body: JSON.stringify({username, password})
}).then((response) => response.json()).then((res) => {
console.log(res)
})
}
return (
<div className="logindiv">
<h1>Login: </h1>
<div className="inputlogindiv">
<input className="inputlogin" type="text" placeholder="Username..." onChange={(e) => {
setUsername(e.target.value)
}}></input>
<input className="inputlogin" type="text" placeholder="Password..." onChange={(e) => {
setPassword(e.target.value)
}}></input>
</div>
<br></br> <br></br>
<div className="logindivbuttons">
<button className="buttonregisterlogin" onClick={handleSubmit}>Login</button> <button className="buttonregisterlogin">Register</button>
</div>
<br></br>
</div>
)
}
After I send the token to the front-end it shows the token and the message that the password matches, but I need to know how to acess this token or send it to the header when I fetch on the front-end to complete the authentication logic.
I have to create private routes using JWT, if someone is able to help-me, please, do it, cause I'm really having a bad time lately.
First of all after successful call to api, you need to save your token somewhere, it can be localstorage for example. You will need it to pass token to your requests and check is user authenticated. After saving token somewhere you need to make a redirect to some protected route, so the user wouldn't stay on login page.
Make sure that you also check if user authenticated on login\registration pages, so user couldn't see them when he logged in.
Here is a link about how to configure private routes in react-router-dom
Hope it helped.
Good afternoon/morning!
I have been working on this for a few days and can't seem to figure this one out. It worked perfect running locally using json server and localhost. Now that its deployed to heroku I get this problem.
PROBLEM: If I log in a user, it won't display anything unless I refresh. If I had a different user logged in recently, it will display their data unless I hit refresh. This is just for testing, I am doing all of this from my computer.
WHAT IVE TRIED Just about everything I can think of. Looking at the network console to see the order of when things are fetched, I am pretty sure I see the problem.
When I first log in I get this order in the network tab
login
tokenIsValid
get
users
when I refresh I get
profile
(static chunks for heroku)
react devtools backend
tokenIsValid
get
users
I am pretty new at this, but seeing the get request for users come after the get request for my users data is causing this issue. When I refresh, It does't matter the order because I am still logged in. That's my logic anyway.
So here is my code for the relative components
App.js
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import Profile from "./Components/Profile";
import Main from "./Components/Main";
import UserContext from "./Components/UserContext";
import SearchResults from "./Components/SearchResults";
function App() {
const [userData, setUserData] = React.useState({
token: null,
user: null,
});
return (
<div className="App">
<UserContext.Provider value={{ userData, setUserData }}>
<Router>
<Switch>
<Route exact path="/" component={Main} />
<Route path="/profile" component={Profile} />
<Route path="/search" component={SearchResults} />
</Switch>
</Router>
</UserContext.Provider>
</div>
);
}
export default App;
Login.jsx
... the code for my get request
const handleSubmit = async (e) => {
e.preventDefault();
try {
const loginUser = { email, password };
const loginRes = await axios.post("/users/login", loginUser, authToken);
setUserData({
token: loginRes.data.token,
user: loginRes.data,
});
console.log(loginRes.data.token)
localStorage.setItem("auth-token", loginRes.data.token);
history.push("/profile");
} catch (err) {
err.response.data.msg && setError(err.response.data.msg);
}
};
ShowRecords.jsx
....
//set state for records and userData
const [newRecords, newRecordData] = React.useState([]);
const { setUserData } = React.useContext(UserContext);
....
//fetch record data
const fetchData = async () => {
const result = await axios.get("record/get", authToken);
newRecordData(result.data);
console.log(result.data)
};
// see if user is logged in already, if not set a token and userData
const checkLoggedIn = async () => {
let token = localStorage.getItem("auth-token");
if (token === null) {
localStorage.setItem("auth-token", "");
token = null;
}
const tokenRes = await axios.post("/users/tokenIsValid", null, {
headers: { "x-auth-token": token },
});
if (tokenRes.data) {
const userRes = await axios.get("/users", {
headers: { "x-auth-token": token },
});
setUserData({
token: userRes.data,
user: userRes.data,
});
}
};
React.useEffect(() => {
checkLoggedIn()
fetchData();
console.log("data");
}, []);
....
edit: here is my backend
UserRoute.js
... relavent routes
router.post("/login", async (req, res) => {
try {
const { email, password } = req.body;
// validate
if (!email || !password)
return res.status(400).json({ msg: "Wait... some fields are empty!" });
const user = await User.findOne({ email: email });
if (!user)
return res
.status(400)
.json({ msg: "No account with this email has been registered." });
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) return res.status(400).json({ msg: "Invalid credentials." });
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET);
res.json({
token,
user: {
id: user._id,
displayName: user.displayName,
},
});
} catch (err) {
res.status(500).json({ error: err.message });
}
});
router.post("/tokenIsValid", async (req, res) => {
try {
const token = req.header("x-auth-token");
if (!token) return res.json(false);
const verified = jwt.verify(token, process.env.JWT_SECRET);
if (!verified) return res.json(false);
const user = await User.findById(verified.id);
if (!user) return res.json(false);
return res.json(true);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
router.get("/", auth, async (req, res) => {
const user = await User.findById(req.user);
res.json({
displayName: user.displayName,
id: user._id,
});
});
...
RecordRoute.js
... all other routes here aren't related and work fine
//get records
router.get("/get", auth, async(req,res) =>{
const records = await Record.find({ userId: req.user});
res.json(records)
})
auth.js
const jwt = require("jsonwebtoken");
const auth = (req, res, next) => {
try {
const token = req.header("x-auth-token");
if (!token)
return res
.status(401)
.json({ msg: "No authentication token, authorization denied." });
const verified = jwt.verify(token, process.env.JWT_SECRET);
if (!verified)
return res
.status(401)
.json({ msg: "Token verification failed, authorization denied." });
req.user = verified.id;
next();
} catch (err) {
res.status(500).json({ error: err.message });
}
};
module.exports = auth;
Any help would be greatly appreciated. I have been stumped on this, and it's the last thing for this app I need to do!
I don't know if this is best practices but I simply could not find another way to fix this problem.
I added
window.location.reload();
to the register.jsx and login.jsx axios request. I know this forces a reload of the document, but it's simply the only way I could get this to work, after 5 days of trying to fix this I am okay with that. If anyone knows a different way, I am all ears.
I'm using the basic Formik template to work on a Login Form.
onSubmit={(
values,
{ setSubmitting, setErrors /* setValues and other goodies */ }
) => {
props.logMeIn(values);
// LoginToSystem(values).then(
// user => {
// setSubmitting(false);
// // do whatevs...
// // props.updateUser(user)
// },
// errors => {
// setSubmitting(false);
// // Maybe transform your API's errors into the same shape as Formik's
// //setErrors(transformMyApiErrors(errors));
// console.log(errors);
// }
// );
}}
This problem is within the onSubmit section; The demo code is commented out but it uses a LoginToSystem function that seems to be a promise. I can not figure out 'what' this function is supposed to me. My function that handles this would be props.logMeIn() - Which also does not work as intended
If the login is successful, it will currently work as expected, and everything is fine. However, if the login fails (404, 401, whatever) the form will remain there, and the setSubmitting log stays there so Submit is grayed out but nothing is done.
If I try to replace LoginToSystem with my function, I get an error on the .then that I can't perform .then on undefined.
I'm wondering if perhaps this is because my function is not set up like a Promise?
loginClickHandler = (user) => {
let userObj = {
email: user.email,
password: user.password
}
axios.post('api/v1/auth/sign_in', userObj)
.then((res) => {
console.log(res.headers);
let loggedInUser = {
'access_token': res.headers['access-token'],
'client': res.headers['client'],
'uid':res.headers['uid'],
'signedIn': true
};
this.setState({
user: loggedInUser
})
this.props.retrieve(user.email);
})
.catch((err) => {
console.log(err);
return err
})
};
My function does properly catch (Thanks to axios) on the .then/.catch, but perhaps I am supposed to modify those to provide a callback so that onSubmit can properly fire?
With some guidance I was able to resolve this one simpler. Axios is natively returning a 'promise' so I just needed to ensure the outcome of the function was axios' method in the end.
loginClickHandler = (user) => {
let userObj = {
email: user.email,
password: user.password
}
const request = axios.post('api/v1/auth/sign_in', userObj);
request.then((res) => {
console.log(res.headers);
let loggedInUser = {
'access_token': res.headers['access-token'],
'client': res.headers['client'],
'uid': res.headers['uid'],
'signedIn': true
};
this.setState({user: loggedInUser, auth: true, anchorEl: null})
}).catch((err) => {
console.log(err);
// setErrors({ test: 'This was an error' })
})
return request;
};
In onSubmit there's a second argument for setting your errors. I added flow to be able to see the types better in this answer for you.
<Formik
initialValues={...}
... // Other Props
onSubmit={this.handleSubmit} // This is where you handle your login logic
render={this.renderForm} // Render your form here.
You have a callback to help you set errors in the second argument
handleSubmit = (
user: FormValues,
{ setErrors }: FormikActions<FormValues>
) => {
return axios.post('api/v1/auth/sign_in', userObj)
...
.catch(e) => {
setErrors({ username: 'Invalid Username' }) // Object you want to put here.
}
}
In your render form function you now have errors that you can use based on what you called in your setErrors
renderForm = ({
... // These are your other FormikProps you're using
errors // You want to use this
}: FormikProps<FormValues>) => (
... // Your rendering here
// errors.username
)
For flow types on Formik
https://github.com/flowtype/flow-typed/blob/master/definitions/npm/formik_v0.9.x/flow_v0.53.x-/formik_v0.9.x.js