Unable to fetch data from Form using FormData - node.js

I am creating an 'edit profile' page for a dashboard the technologies that I use for the same are Next.js, Node.js & MongoDB.
Note: skip to the backend part if you just wanted to know the issue.
Frontend
Firstly,let me explain the Frontend part.
I am using useRef() inorder to reference data(name,bio) in the inputfields. which are working nicely.
Everything is fine the issue is in the handlesbumit() event_handler.
I am using FormData to send my form data to the backend API
If you're thinking why I'm not using a usual body object to send data the reason is that I have to add the profile picture updation later for which I have to send files , which as far I know we can't do that with an Object and yeah just to inform you it works fine if I would have used that Object part but can't use it with profilepicture updation.
The value that I have consoled out for the references are all good, and the rest of the handler is just as it is written can't find anything odd in that.
import { useUser } from '../../../lib/hooks';
import React, { useState, useEffect, useRef } from 'react';
import Head from 'next/head';
import { ImBook, ImListNumbered } from 'react-icons/im';
import { AiFillGithub, AiOutlineTwitter, AiFillFacebook, AiFillInstagram, AiFillLinkedin } from 'react-icons/ai'
import { FaFacebook, FaStackOverflow } from 'react-icons/fa';
const ProfileSection = () => {
const [user, { mutate }] = useUser();
const [isUpdating, setIsUpdating] = useState(false);
const nameRef = useRef();
const profilePictureRef = useRef();
const bioRef = useRef();
const [msg, setMsg] = useState({ message: '', isError: false });
useEffect(() => {
nameRef.current.value = user.name;
bioRef.current.value = user.Bio;
}, [user]);
const handleSubmit = async (event) => {
event.preventDefault();
if (isUpdating) return;
setIsUpdating(true);
console.log(nameRef.current.value); //Testing
console.log(bioRef.current.value); //Testing
const formData = new FormData();
formData.append('name', nameRef.current.value);
formData.append('Bio', bioRef.current.value);
console.log(formData.get('name'));
const res = await fetch('/api/user', {
method: 'PATCH',
body: formData,
});
if (res.status === 200) {
const userData = await res.json();
mutate({
user: {
...user,
...userData.user,
},
});
setMsg({ message: 'Profile updated' });
} else {
setMsg({ message: await res.text(), isError: true });
}
};
return (
<>
<Head>
<title>Settings</title>
</Head>
<main>
<div class="row">
<div class="col s12 m12">
<div className="card-panel br-10">
{msg.message ? <p style={{ color: msg.isError ? 'red' : '#0070f3', textAlign: 'center' }}>{msg.message}</p> : null}
<form onSubmit={handleSubmit}>
<div className="row">
<div className="col s12 m6 l6">
<label htmlFor="name">
Name
<input
required
id="name"
name="name"
type="text"
ref={nameRef}
/>
</label>
</div>
<div className="col s12 m6 l6">
<label htmlFor="bio">
Bio
<textarea
id="bio"
name="bio"
type="text"
ref={bioRef}
/>
</label>
</div>
</div>
<div className="center-align">
<button disabled={isUpdating} className="btn" type="submit" >Save</button>
</div>
</form>
</div>
</div>
</div>
</main>
</>
);
};
const SettingPage = () => {
const [user] = useUser();
if (!user) {
return (
<>
<p>Please sign in</p>
</>
);
}
return (
<>
<ProfileSection />
</>
);
};
export default SettingPage;
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
import { useUser } from '../../../lib/hooks';
import React, { useState, useEffect, useRef } from 'react';
import Head from 'next/head';
import { ImBook, ImListNumbered } from 'react-icons/im';
import { AiFillGithub, AiOutlineTwitter, AiFillFacebook, AiFillInstagram, AiFillLinkedin } from 'react-icons/ai'
import { FaFacebook, FaStackOverflow } from 'react-icons/fa';
const ProfileSection = () => {
const [user, { mutate }] = useUser();
const [isUpdating, setIsUpdating] = useState(false);
const nameRef = useRef();
const profilePictureRef = useRef();
const bioRef = useRef();
const [msg, setMsg] = useState({ message: '', isError: false });
useEffect(() => {
nameRef.current.value = user.name;
bioRef.current.value = user.Bio;
}, [user]);
const handleSubmit = async (event) => {
event.preventDefault();
if (isUpdating) return;
setIsUpdating(true);
console.log(nameRef.current.value);
console.log(bioRef.current.value);
const formData = new FormData();
formData.append('name', nameRef.current.value);
formData.append('Bio', bioRef.current.value);
console.log(formData.get('name'));
const res = await fetch('/api/user', {
method: 'PATCH',
body: formData,
});
if (res.status === 200) {
const userData = await res.json();
mutate({
user: {
...user,
...userData.user,
},
});
setMsg({ message: 'Profile updated' });
} else {
setMsg({ message: await res.text(), isError: true });
}
};
return (
<>
<Head>
<title>Settings</title>
</Head>
<main>
<div class="row">
<div class="col s12 m12">
<div className="card-panel br-10">
{msg.message ? <p style={{ color: msg.isError ? 'red' : '#0070f3', textAlign: 'center' }}>{msg.message}</p> : null}
<form onSubmit={handleSubmit}>
<div className="row">
<div className="col s12 m6 l6">
<label htmlFor="name">
Name
<input
required
id="name"
name="name"
type="text"
ref={nameRef}
/>
</label>
</div>
<div className="col s12 m6 l6">
<label htmlFor="bio">
Bio
<textarea
id="bio"
name="bio"
type="text"
ref={bioRef}
/>
</label>
</div>
</div>
<div className="center-align">
<button disabled={isUpdating} className="btn" type="submit" >Save</button>
</div>
</form>
</div>
</div>
</div>
</main>
</>
);
};
const SettingPage = () => {
const [user] = useUser();
if (!user) {
return (
<>
<p>Please sign in</p>
</>
);
}
return (
<>
<ProfileSection />
</>
);
};
export default SettingPage;
Backend
Now, the backend API for the same handlesubmit() event_handler i.e. 'api/user'
Please ignore the handler, it's just a predefined middleware npm next-connect which itself checks what type of request is coming if its 'PATCH' it will run handler.patch.
The Issue is the value of name & Bio is undefined,which means its not getting values from req.body;
And to check I also consoled out req.body which give out this
The data is correct but req.body is not a Object its a String now and I get it, its because I'm using formdata so how to get the values of name & Bio from this req.body ?
import nextConnect from 'next-connect';
import middleware from '../../../middlewares/middleware';
import { extractUser } from '../../../lib/api-helpers';
const handler = nextConnect();
handler.use(middleware);
handler.get(async (req, res) => res.json({ user: extractUser(req) }));
handler.patch(async (req, res) => {
if (!req.user) {
req.status(401).end();
return;
}
const { name, Bio } = req.body;
await req.db.collection('users').updateOne(
{ _id: req.user._id },
{
$set: {
name:name,
Bio: Bio,
},
},
);
res.json({ user: { name, Bio } });
});
export default handler;

I have encountered a this issue.
I was resolve it by use 2 form, a form use to get user's info as email, password and the other for send user's picture.
Maybe has best practice for this case.

Related

Warning: Each child in a list should have a unique "key" prop and does not save in my json-server

I keep getting error "Warning: Each child in a list should have a unique "key" prop" on console log. I think there is something wrong in the
axios.delete('http://localhost:3000/contacts/${id}
' or
axios.put('http://localhost:3000/contacts/${isUpdate.id}'
It works, but does not configure in the json-server that I made. So the changes does not happen in the json-server.
The "save" button works, and added new data into my json-server. But, when I tried to edit, or delete, it does not save the changed in json-server.
My code:
import "./App.css";
import List from "./List";
import { useState,useEffect } from "react";
import {uid} from "uid";
import axios from "axios";
function App() {
const [contacts, setContacts] = useState([]);
function handleChange(e) {
let data = { ...formData };
data[e.target.name] = e.target.value;
setFormData(data);
}
const [isUpdate, setIsUpdate] = useState({ id: null, status: false});
const [formData, setFormData] = useState({
name: "",
telp: "",
});
useEffect(() => {
axios.get("http://localhost:3000/contacts").then((res) => {
console.log(res.data);
setContacts(res?.data ?? []);
});
}, []);
function handleSubmit(e){
e.preventDefault();
alert("Oke Bos!");
let data = [...contacts];
if(formData.name === "") {
return false;
}
if(formData.telp === "") {
return false;
}
if ( isUpdate.status){
data.forEach((contact) => {
if (contact.id === isUpdate.id) {
contact.name = formData.name;
contact.telp = formData.telp;
}
});
axios.put('http://localhost:3000/contacts/${isUpdate.id}', {
name: formData.name,
telp: formData.telp,
})
.then((res) => {
alert("Berhasil edit data!");
});
} else {
let newData = { id: uid(), name: formData.name, telp: formData.telp };
data.push(newData);
axios.post("http://localhost:3000/contacts", newData).then((res) => {
alert("Data telah disimpan cok!");
});
}
// tambah kontak yee
setIsUpdate({id: null, status: false});
setContacts(data);
setFormData({ name: "", telp: ""});
}
function handleEdit(id) {
let data = [...contacts];
let foundData = data.find((contact) => contact.id === id);
setFormData({ name: foundData.name, telp: foundData.telp});
setIsUpdate({id: id, status: true});
}
function handleDelete(id) {
let data = [...contacts];
let filteredData = data.filter((contact) => contact.id !== id);
axios.delete('http://localhost:3000/contacts/${id}').then((res) => {
alert("Data telah dihapus");
});
setContacts(filteredData);
}
return (
<div className="App">
<h1 className="px-3 py-3">My Contact List</h1>
<form onSubmit={handleSubmit} className="px-3 py-4">
<div className="form-group">
<label htmlFor="">Name</label>
<input type="text"
className="form-control"
onChange={handleChange}
value={formData.name}
name="name" />
</div>
<div className="form-group mt-3">
<label htmlFor="">No. Telp</label>
<input type="text"
className="form-control"
onChange={handleChange}
value={formData.telp}
name="telp" />
</div>
<div>
<button type="submit" className="btn btn-primary w-100 mt-3">
Save
</button>
</div>
</form>
<List handleDelete={handleDelete} handleEdit={handleEdit} data={contacts} />
</div>
);
}
export default App;
The list component:
import React from "react";
export default function List({data,handleEdit,handleDelete}) {
return (
<div className="list-group">
{
data.map((contact) => {
return (
<div className="list-group-item list-group-item-action">
<div className="d-flex w-100 justify-content-between">
<h5 className="mb-1">{contact.name}</h5>
<div>
<button onClick={() => handleEdit(contact.id)} className="btn btn-sm btn-link">Edit</button>
<button onClick={() => handleDelete(contact.id)} className="btn btn-sm btn-link">Del</button>
</div>
</div>
<p className="mb-1">{contact.telp}</p>
</div>
);
})}
</div>
);
}
I follow the tutorial, the code is like this, i checked it in so many times. But still it does not configure well into json-server like what i see on youtube.

How to send formData that includes image

I'm trying to send my form using React frontend to NodeJS backend and I'm not getting any data in formData() object, here is my code for React:
import { useState, useEffect } from 'react'
import Axios from 'axios'
import Notification from '../../../components/Notification'
const DashboardWorkAdd = ({ subTitle = 'works' }) => {
//Form States
const [workName, setWorkName] = useState('')
const [workGithub, setWorkGithub] = useState('')
const [workOnlineLink, setWorkOnlineLink] = useState('')
const [workDate, setWorkDate] = useState('')
const [workDesc, setWorkDesc] = useState('')
const [workAdded, setworkAdded] = useState('')
const [workAddedMsg, setworkAddedMsg] = useState('')
const [workImgFile, setWorkImgFile] = useState('')
const [preview, setPreview] = useState()
const formMsg = document.querySelector('.notification__msg')
const updateWorkImg = (e) => {
const file = e.target.files[0]
if (file) {
const fileType = file.type.split('/')[0]
const fileSizeToMB = file.size / 1000000
const MAX_FILE_SIZE = 1 //mb
if (formMsg) {
formMsg.classList.remove('hidden')
if (fileType !== 'image') {
formMsg.textContent = 'you can add only image file'
} else if (fileSizeToMB > MAX_FILE_SIZE) {
formMsg.textContent = `you can't add more than ${MAX_FILE_SIZE} MB`
return
} else {
formMsg.classList.add('hidden')
formMsg.textContent = ''
setWorkImgFile(file)
}
}
}
}
useEffect(() => {
// if there's an image
if (workImgFile) {
const reader = new FileReader()
reader.onloadend = () => setPreview(reader.result)
reader.readAsDataURL(workImgFile)
} else {
setPreview(null)
}
}, [workImgFile])
const handleAddWork = async (e) => {
e.preventDefault()
//using FormData to send constructed data
const formData = new FormData()
formData.append('workImg', workImgFile)
formData.append('workName', workName)
formData.append('workGithub', workGithub)
formData.append('workOnlineLink', workOnlineLink)
formData.append('workDate', workDate)
formData.append('workDesc', workDesc)
console.log(formData)
if (
(workName === '' || workGithub === '' || workOnlineLink === '' || workDate === '',
workDesc === '')
) {
formMsg.textContent = 'please add all data'
} else {
try {
const response = await Axios.post(
`${
process.env.NODE_ENV === 'development'
? process.env.REACT_APP_API_LOCAL_URL
: process.env.REACT_APP_API_URL
}/workAdd`,
{ workImgFile, workName, workGithub, workOnlineLink, workDate, workDesc }
)
const { workAdded, message } = response.data
setworkAdded(workAdded)
setworkAddedMsg(message)
} catch (err) {
console.error(err)
}
}
}
return (
<>
<h3 className='text-3xl mt-20 mb-12 text-center font-semibold'>{subTitle}</h3>
<div className='h-full'>
<div className='flex flex-col gap-3 py-4 text-sm font-semibold'>
<Notification sendStatus={workAdded} sendStatusMsg={workAddedMsg} />
<div className='notification__msg'></div>
<form
method='POST'
className='flex flex-col gap-14'
encType='multipart/form-data'
onSubmit={handleAddWork}
>
<label
htmlFor='workImg'
className='flex flex-wrap justify-center gap-5 md:justify-between items-center cursor-pointer'
>
<img
src={
preview === null ? 'https://source.unsplash.com/random?webdev' : preview
}
alt='Work Portfolio Preview'
className='w-36 h-36'
/>
<input
type='file'
name='workImg'
id='workImg'
className='bg-blue-500 py-6 px-28 rounded-lg text-white uppercase font-semibold cursor-pointer'
accept='image/*'
onChange={updateWorkImg}
multiple
required
/>
</label>
<div className='dashboard-group'>
<label htmlFor='workName'>work name</label>
<input
type='text'
id='workName'
autoFocus
onChange={(e) => {
setWorkName(e.target.value.trim())
}}
required
/>
</div>
<div className='dashboard-group'>
<label htmlFor='workLinkGithub'>Github Link</label>
<input
type='text'
id='workLinkGithub'
min='5'
max='500'
onChange={(e) => setWorkGithub(e.target.value.trim())}
required
/>
</div>
<div className='dashboard-group'>
<label htmlFor='workLinkOnline'>Online Linl</label>
<input
type='text'
id='workLinkOnline'
min='5'
max='500'
onChange={(e) => setWorkOnlineLink(e.target.value.trim())}
required
/>
</div>
<div className='dashboard-group'>
<label htmlFor='workDate'>Work Date</label>
<input
type='date'
id='workDate'
min='5'
max='500'
onChange={(e) => setWorkDate(e.target.value.trim())}
required
/>
</div>
<div className='dashboard-group'>
<label htmlFor='workDescription'>word description</label>
<textarea
name='workDescription'
id='workDescription'
minLength='10'
maxLength='300'
className=''
onChange={(e) => setWorkDesc(e.target.value.trim())}
required
></textarea>
</div>
<div className='flex justify-around text-lg'>
<button
type='submit'
className='bg-green-500 hover:bg-green-600 py-2 px-20 rounded-lg text-white transition-colors'
>
Add
</button>
</div>
</form>
</div>
</div>
</>
)
}
export default DashboardWorkAdd
Here is my code for NodeJS:
const WorksModel = require('../models/WorkModel')
const { v4: uuidv4 } = require('uuid')
module.exports = async (req, res) => {
const { workName, workGithub, workOnlineLink, workDate, workDesc } = req.body
const { workImg } = req.files
const workImgName = uuidv4() + workImg.name
const workImgMovePath = `${__dirname}/../../client/public/uploads/${workImgName}`
const workImgDisplayPath = `/uploads/${workImgName}`
const works = new WorksModel({
workImgDisplayPath,
workName,
workGithub,
workOnlineLink,
workDate,
workDesc
})
try {
await works.save()
workImg.mv(workImgMovePath, (err) => {
if (err) {
res.send({ message: `sorry, something wrong with the server: ${error}` })
return res.status(500).send(err)
}
})
res.send({
message: 'added succesfully',
workAdded: 1
})
} catch (error) {
res.send({
message: `something went wrong ${error}`,
workAdded: 0
})
}
}
and my WorkModel file is :
const mongoose = require('mongoose')
const WorkSchema = new mongoose.Schema({
workImgDisplayPath: {
type: String,
required: true
},
workName: {
type: String,
required: true
},
workGithub: {
type: String,
required: true
},
workOnlineLink: {
type: String,
required: true
},
workDate: {
type: Date,
required: true,
default: new Date().toLocaleDateString('ar-EG', {
weekday: 'long',
year: 'numeric',
month: 'short',
day: 'numeric'
})
},
workDesc: {
type: String,
required: true
}
})
const WorkModel = mongoose.model('mhmdhidrPortfolio', WorkSchema)
module.exports = WorkModel
the issue I'm having when I get the data in the backend is the req.files are undefined, I googled a lot and I don't know the issue exactly.
Thanks for your help.
I solved my problem, I just had to add:
const fileUpload = require('express-fileupload')
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(fileUpload())
inside my index.js (server) file

Unexpected token " in JSON at position 0

My Goal for this one is to Add ObjectId inside the array
In my backend Im declare schema on this code:
tchStudents: [{
type: Schema.Types.ObjectId,
ref: "Student"
}]
THen Im do adding an ObjectId to insert to the array of ObjectID:
My BackEnd is very fine
router.put('/assignAddStudents/:tchID', async (req,res) => {
try {
const searchTch = await Teacher.findOne({ tchID: req.params.tchID })
if(!searchTch){
return res.status(404).send({
success: false,
error: 'Teacher ID not found'
});
} else {
let query = { tchID: req.params.tchID }
let assignedStudentObjID = {$push:{tchStudents: req.body.tchStudents }}
Teacher.updateOne(query, assignedStudentObjID ,() => {
try{
return res.status(200).send({
success: true,
msg: 'Student ID has been assigned'
});
} catch(err) {
console.log(err);
return res.status(404).send({
success: false,
error: 'Teacher ID not found'
})
}
})
}
} catch (err) {
console.log(err)
}
})
But my Front End Not working
err: BAD REQUEST(400) Unexpected token " in JSON at position 0
import React, {useState} from 'react'
import axios from 'axios'
import { URL } from '../../utils/utils'
import { Modal, Button } from 'react-materialize';
import ListTchStudents from '../lists/ListTchStudents';
const trigger =
<Button
style={{marginLeft:'2rem'}}
tooltip="Add More..."
tooltipOptions={{
position: 'top'
}}
className="btn-small red darken-4">
<i className="material-icons center ">add_box</i>
</Button>;
const MdlAddStudents =({teacher}) => {
const [data, setData] = useState('');
const { tchStudents} = data;
const {
tchID,
} = teacher; // IF WE RENDER THIS IT TURNS INTO OBJECT
const assignedStudent = () => {
// BUT WE SENT IT TO THE DATABASE CONVERT TO JSON.STRINGIFY to make ObjectId
const requestOpt = {
method: 'PUT',
headers: { 'Content-Type': 'application/json'},
body: JSON.stringify(data)
}
axios.put(`${URL}teachers/assignAddStudents/${tchID}`, data,requestOpt)
.then(res => {
setData(res.data.data)
})
}
return (
<Modal header="Add Students" trigger={trigger}>
Please ADD and REMOVE Student ID No. for {tchID}
<div>
<ul
style={{marginBottom:'2rem'}}
className="collection">
{
Object.values(teacher.tchStudents).map(tchStudent => {
return(
<ListTchStudents
tchStudent={tchStudent}
/>
);
})
}
</ul>
<div className="row">
<div className="col s6 offset-s3"></div>
<div className="input-field">
<label
htmlFor=""
className="active black-text"
style={{fontSize:'1.3rem'}}>
Add Students here:
</label>
<input
type="text"
name="tchStudents"
value={(tchStudents)}
className="validate"
onChange={(e) => setData(e.target.value)}
/>
</div>
</div>
</div>
{/* BUT WE SENT IT TO THE DATABASE CONVERT TO JSON.STRINGIFY to send ObjectId to the database
*/}
<div className="row">
<div className="col s2 offset-s3" ></div>
<Button
onClick={assignedStudent}
tooltip="Add Students"
tooltipOptions={{
position: 'right'
}}
className="btn green darken-3">
<i className="material-icons ">add_circle</i>
</Button>
</div>
<p>There are {Object.values(teacher.tchStudents).length} student/s
assigned</p>
</Modal>
)
}
// MdlAddStudents.propTypes = {
// assignedStudents: PropTypes.func.isRequired,
// }
export default MdlAddStudents;
// export default connect(null, (assignedStudents))(MdlAddStudents);
Thank you for helping out
The problem stems from you attempting to wrap your tchStudents state property in an object named data.
My advice is to keep it very simple
// it's just a string
const [tchStudents, setTchStudents] = useState("")
const assignedStudent = () => {
// create your request payload
const data = { tchStudents }
// no config object required
axios.put(`${URL}teachers/assignAddStudents/${tchID}`, data)
.then(res => {
// not sure what you want to do here exactly but
// `res.data` should look like
// { success: true, msg: 'Student ID has been assigned' }
setTchStudents("") // clear the input ¯\_(ツ)_/¯
})
}
The only other change is to use the new setter name in your <input>...
<input
type="text"
name="tchStudents"
value={tchStudents}
className="validate"
onChange={(e) => setTchStudents(e.target.value)}
/>

Why did it failed to load? the server responded with a status of 404 (Not Found)

I have my app(express+Next.js.MOngoDB).I signuped all my users,works Ok. Now when I try signin
http://localhost:5000/signin
I got error
Uncaught (in promise) Error: Request failed with status code 404
at createError (webpack-internal:///../../node_modules/axios/lib/core/createError.js:16)
at settle (webpack-internal:///../../node_modules/axios/lib/core/settle.js:17)
Terminal
GET /__nextjs_original-stack-frame?isServerSide=false&file=webpack-internal%3A%2F%2F%2F..%2F..%2
POSTMAN signin works fine
app.js(just API)
const app = express();
app.use('/api/v1/auth', auth);
Next.js,Signin
import SigninComponent from '../components/frontauth/SigninComponent';
const Signin = () => {
return (
<Layout>
<h2 className="text-center pt-3 pb-4">Signin</h2>
<div className="row">
<div className="col-md-6 offset-md-3">
<SigninComponent />
</div>
</div>
</Layout>
);
};
SigninComponent
/* eslint-disable no-undef */
import axios from 'axios';
import React, { useState } from 'react';
import { API } from '../../config';
const SigninComponent = () => {
const [values, setValues] = useState({
email: '',
password: ''
});
const [loading, setLoading] = useState(false);
const { email, password } = values;
const handleSubmit = async (e) => {
e.preventDefault();
const { email, password } = values;
const user = { email, password};
await axios.post(`${API}/api/v1/auth/signin`, user);
};
const handleChange = name => e => {
setValues({ ...values, error: false, [name]: e.target.value });
};
const showLoading = () => (loading ? <div className="alert alert-info">Loading...</div> : '');
const signinForm = () => {
return (
<form onSubmit={handleSubmit}>
<div className="form-group">
<input
value={values.email}
onChange={handleChange('email')}
type="email"
className="form-control"
placeholder="Type your email"
/>
</div>
<div className="form-group">
<input
value={values.password}
onChange={handleChange('password')}
type="password"
className="form-control"
placeholder="Type your password"
/>
</div>
<div>
<button className="btn btn-primary">Signin</button>
</div>
</form>
);
};
return <React.Fragment>
{showLoading()}
{signinForm()}
</React.Fragment>;
};
export default SigninComponent;
How should I edit .babelrc,I have only one line,should I add plugins?
"presets": ["next/babel"]
Why does the terminal point to SSR FALSE?

Redirect to profile after login

I am doing google auth with passport and
I wanna redirect to the profile after successfully logged in.
The problem is my method of this.props.history.push('/profile') dont work.
By the way, it's from my router that I need to redirect the user to profile.
All the authentication is in the route.
Here is my code any suggestions?
The if (user) {} is true my only problem is on how to redirect the user.
Route:
const Strategy = require('passport-local').Strategy
const mongoose = require('mongoose')
const GoogleUser = require('../models/google');
const GoogleAuth = new Strategy(
{ passReqToCallback: true, usernameField: "email", passwordField: "id" },
function(req, email, id, done) {
GoogleUser.findOne({ email: req.body.email })
.lean()
.exec((err, user) => {
if (err) {
return done(err, null);
}
if (!user) {
let newUser = new GoogleUser({
email: req.body.email,
id: req.body.id,
name: req.body.name,
token: req.body.token,
image: req.body.image
});
newUser.save((error, inserted) => {
if (error) {
return done(error, null);
}
return done(null, inserted);
});
}
if (user) {
this.props.history.push("/profile");
}
});
}
);
module.exports = GoogleAuth;
Here is the React code if needed:
import React, { Component } from 'react';
import axios from "axios";
import {Redirect} from "react-router-dom"
import styles from '../styles/loginsignup.css'
import logo from '../img/nowyourguest.png'
import 'bootstrap/dist/css/bootstrap.min.css'
import 'jquery/dist/jquery.min.js'
import 'bootstrap/dist/js/bootstrap.min.js'
export default class Login extends Component {
componentDidMount() {
this.googleSDK();
console.log('sfsfd');
}
prepareLoginButton = () => {
console.log(this.refs.googleLoginBtn);
this.auth2.attachClickHandler(this.refs.googleLoginBtn, {},
(googleUser) => {
let profile = googleUser.getBasicProfile();
const email = profile.getEmail()
const id = profile.getId()
const name = profile.getName()
const token = googleUser.getAuthResponse().id_token
const image = profile.getImageUrl()
axios({
url: "/authentication/google",
method: "POST",
data: {
email,
id,
name,
token,
image
}
})
.then(response => {
const isAuthenticated = response.data.isAuthenticated
window.localStorage.setItem('isAuthenticated', isAuthenticated);
this.props.history.push('/profile')
})
.catch(error =>{
this.setState({
errorMessage:error.response.data.message
})
})
})
}
googleSDK = () => {
window['googleSDKLoaded'] = () => {
window['gapi'].load('auth2', () => {
this.auth2 = window['gapi'].auth2.init({
client_id: 'clientID',
cookiepolicy: 'single_host_origin',
scope: 'profile email'
});
this.prepareLoginButton();
});
}
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "https://apis.google.com/js/platform.js?onload=googleSDKLoaded";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'google-jssdk'));
}
state = {
email: '',
password: ''
};
handleSubmit = event => {
event.preventDefault();
const {email, password } = this.state;
axios({
url: "/authentication/signin",
method: "POST",
data: {
email,
password
}
})
.then(response => {
const isAuthenticated = response.data.isAuthenticated
window.localStorage.setItem('isAuthenticated', isAuthenticated);
this.props.history.push('/profile')
})
.catch(error =>{
this.setState({
errorMessage:error.response.data.message
})
})
};
handleChange = event => {
const {name, value} = event.target;
this.setState({
[name]:value
})
}
render() {
const isAuthenticated = window.localStorage.getItem('isAuthenticated');
if (isAuthenticated) {
return <Redirect to='/profile' />
}
return(
<div>
<div>
<nav className="navbar navbar-expand-lg navbar-light bg-info sticky-top" id="shadow">
<a className="navbar-brand text-warning" href="/">NowYourGuest</a>
<button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon" />
</button>
<div className="collapse navbar-collapse" id="navbarSupportedContent" >
<ul className="navbar-nav mr-auto">
<li className="nav-item">
<a className="nav-link text-white" href="/add-your-accomodation">Add your accomodation</a>
</li>
<li className="nav-item active">
<a className="nav-link" href="/login">Login<span className="sr-only">(current)</span></a>
</li>
<li className="nav-item">
<a className="nav-link text-white" href="/signup">Signup</a>
</li>
</ul>
</div>
<form className="form-inline my-2 my-lg-0">
<input className="form-control mr-sm-2" type="search" placeholder="Search for accomodation" aria-label="Search" />
</form>
</nav>
</div>
<div className="container">
<div className="card card-container">
<center><img id="profile-img" className="profile-img" src={logo} /></center>
<p id="profile-name" className="profile-name-card" />
<form onSubmit={this.handleSubmit} className="form-signin">
<span id="reauth-email" className="reauth-email" />
<input type="text" id="inputEmail" className="form-control" name="email" onChange={this.handleChange} placeholder="Email address" required autofocus />
<input type="password" id="inputPassword" className="form-control" name="password" onChange={this.handleChange} placeholder="Password" required />
<center><p style={{color: 'red'}}>{this.state.errorMessage}</p></center>
<button className="btn btn-lg btn-primary btn-block btn-signin" type="submit">Sign in</button>
</form>
<button className="loginBtn loginBtn--google" ref="googleLoginBtn">Login with Google</button>
<a href="#" className="forgot-password">
Forgot the password?
</a>
</div>
</div>
</div>
)
}
}
module.exports = Login;
you need to use "withRouter" from "react-router-dom".
All you need to do is import withRouter and wrap your component at export:
import { withRouter } from "react-router-dom";
//.
//.
//.
export default class (withRouter(Login))
You can also use Redirect component from "react-router-dom" and return it instead
import { Redirect } from "react-router-dom";
// Your code
render() {
if (User) {
return <Redirect to={"/route"} />
}
return(
// If user is not logged...
)
}
I just made 2 different routes in one login and one register.
And it perfectly works.

Resources