How to get the object id after the button click in Reactjs - node.js

I am working in MERN project.
what I want
I am fetching the project list from the backend If i click on any project it should give the members list that is working on that project so i want to get the objectid of that clicked project
what i tried
import React, { useState, useEffect } from 'react'
import { NavLink } from 'react-router-dom'
import { useNavigate } from 'react-router-dom';
const AdminDash = () => {
const navigate = useNavigate()
const [userData, setuserData] = useState([])
const [data, setData] = useState({});
const callAboutPage = async () => {
try {
const res = await fetch("/alldata", {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
credentials: "include"
})
const data = await res.json()
setuserData(data)
console.log(setuserData);
if (!res.status === 200) {
const error = new Error(res.error)
throw error
}
} catch (error) {
console.log(error);
navigate("/")
}
}
function handleButtonClick(id) {
fetch(`/api/get-data/${id}`)
.then(response => response.json())
.then(data => {
setData(data);
console.log(data);
});
}
useEffect(() => {
callAboutPage()
}, [])
return (
<>
<div className='container mt-5'>
<div className='row'>
<div className='col-sm-10 col-md-10 col-lg-10'>
<div class="row align-items-md-stretch">
<div class="col-md-6">
<div class="h-100 p-5 text-bg-light rounded-3">
<h2>Current Ongoing Projects</h2>
<ol class="list-group list-group-numbered mt-5">
{
userData.map((item, i) => (
<li class="list-group-item d-flex justify-content-between align-items-start">
<div class="ms-2 me-auto">
<NavLink onClick={() => handleButtonClick()} to="/admindash" className="text-decoration-none"><div class="fw-bold">{item.name}</div></NavLink>
Content for list item
</div>
<i class="bi bi-at"></i>
</li>
))}
</ol>
<label>{data.name}</label>
</div>
</div>
</div>
</div>
</div>
</div>
</>
)
}
export default AdminDash
backend
app.get('/api/get-data/:id', (req, res) => {
ProjectSchema.findById(req.params.id, (err, doc) => {
if (err) {
res.send(err);
} else {
res.json(doc);
}
});
});
the output i get in my console
{stringValue: '"undefined"', valueType: 'string', kind: 'ObjectId', value: 'undefined', path: '_id', …}
How to achieve this ..any suggestions?

In the mapping onClick pass item.id.
First make sure you getting it in your data while Fetching.
<NavLink onClick={() => handleButtonClick(item.id)} to="/admindash" className="text-decoration-none">{item.name}

Related

Unable to use the server side props in Next JS while using the axios as middleware in the React Context

I would like to get the request using the status side props in Next JS, however, I'm receiving an error that status code is not defines which I declared in the context api file.
This is from my client side next js page
function Instructor({ courses }) {
const router = useRouter();
const { Meta } = Card;
return (
<div className="overflow-hidden">
<InstructorRoute>
<h1 className="jumbotron square">This is Instructor Dashboard</h1>
<div class="row">
{courses &&
courses.map((course, index) => {
return (
<div class="col-sm-4 scaling">
<Badge.Ribbon
text={course.published ? "Published" : "Draft"}
color={course.published ? "green" : "gold"}
>
<div class="card">
<div class="card-body">
<Link href={`/instructor/course/view/${course.slug}`}>
<a className="card-title text-black fw-bold">
<ReactMarkdown> {course.name}</ReactMarkdown>
</a>
</Link>
<p class="card-text text-truncate">
{course.description}
</p>
<img
style={{ cursor: "pointer" }}
onClick={(e) => {
e.preventDefault();
router.push(
`/instructor/course/view/${course.slug}`
);
}}
class="card-img-bottom "
height="200px"
src={
course.image ? course.image.Location : "/course.png"
}
alt="Card image cap"
/>
<hr className="fullWidth" />
<p class="mt-2 card-text">
<p class=" fs-6 badge bg-warning">
{course.lessons.length} Lessons
</p>
</p>
</div>
</div>
</Badge.Ribbon>
</div>
);
})}
</div>
</InstructorRoute>
</div>
);
}
export async function getStaticProps(context) {
const { data } = await axios.get("/api/instructor-courses");
// setCourses(data);
console.log(data);
return {
props: {
courses: data,
},
};
}
export default Instructor;
This is from my context api page:
function Provider({ children }) {
const router = useRouter();
const [state, dispatch] = useReducer(rootReducer, initialState);
useEffect(() => {
dispatch({
type: "LOGIN",
payload: JSON.parse(window.localStorage.getItem("user")),
});
}, []);
axios.interceptors.response.use(
(response) => {
return response;
},
(err) => {
let res = err.response;
console.log(res);
if (res.status === 401 && res.config && !res.config.__isRetryRequest) {
return new Promise((resolve, reject) => {
axios
.get("api/logout")
.then((data) => {
console.log("401 error > logout");
dispatch({ type: "LOGOUT" });
window.localStorage.removeItem("user");
router.push("/login");
})
.catch((err) => {
console.log("Error", err);
reject(err);
});
});
}
return Promise.reject(err);
}
);
useEffect(() => {
async function getCsrfToken() {
const { data } = await axios.get("/api/csrf-token");
console.log("CSRF", data);
axios.defaults.headers["X-CSRF-Token"] = data.getCsrfToken;
}
getCsrfToken();
}, []);
return (
<Context.Provider value={{ state, dispatch }}>{children}</Context.Provider>
);
}
export { Context, Provider };
This is my app.js page:
function MyApp({ Component, pageProps }) {
return (
<Provider>
<Nav></Nav>
<ToastContainer
theme="colored"
position="top-center"
limit={3}
autoClose={1500}
/>
<Component {...pageProps}></Component>
</Provider>
);
}
This is the error that I'm receiving. However, the code works if I don't use server side rendering or static side rendering
Server Error
TypeError: Cannot read property 'status' of undefined
This error happened while generating the page. Any console logs will be displayed in the terminal window.
Source
context\index.js (39:14) # eval
37 | let res = err.response;
38 | console.log(res);
> 39 | if (res.status === 401 && res.config && !res.config.__isRetryRequest) {
| ^
40 | return new Promise((resolve, reject) => {
41 | axios
42 | .get("api/logout")

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)}
/>

Delete an item with ReactJS

I'm trying to delete an item after the user clicks on the Delete button. Through the handleDelete function, I am passing an id (idBooks) via axios when the user clicks a book. How do I withdraw it from the click?
Below you will find the React code and then the backend side code in node js.
Frontend
(class extends React.Component {
handleDelete = (e) => {
e.preventDefault();
const { book } = this.props;
axios.delete("http://localhost:8081/delete", book.idBooks )
.then(res => {
console.log(res.data);
}).catch(err => {
console.warn(err.warn);
});
};
render() {
const { book, classes } = this.props;
const token = localStorage.getItem('token');
return(
<Paper className= { classes.marginTopBottom }>
<h2 className={ classes.title }>
{ book.title }
</h2><hr />
<div className= { classes.scrollingDiv }>
<p>
{ book.plot }
</p>
</div>
<hr/>
<div className={ classes.pStyle }>
<p>Publish date:<br /> { new Date(book.publish_date).toLocaleDateString() }</p>
<p>Author:<br /> { book.author }
</p>
<p>Genre:<br /> { book.genre }</p>
</div>
<div>
{ token && (
<Button className={ classes.delete } size="small" onClick={this.handleDelete} type="button" variant="contained" color="primary"
component= {Link} to="delete">
Delete
<DeleteIcon className={ classes.rightIcon } />
</Button>
)}
</div>
</Paper>
)
}
});
Backend
const deleteBook = (req, res) => {
const connection = mysql.createConnection(connectionProperties);
connection.connect();
const query = `DELETE FROM Books WHERE idBooks = ${ req.body.idBooks }`;
connection.query(query, (err, res) => {
if (err) {
res.status(500).send(err);
} else {
res.status(200).send('Book deleted correctly.');
}
});
};
I'd add a prop onDeleteCallback, and on successful delete call that function with deleted book id. In parent component (with all the books are listed) update the state with filtered out books.
I guess passing the parameter might help you fix this issue.
On the delete Button add a parameter to the onClick={()=>this.handleDelete(e,book.idBooks)}
Change the handleDelete function a bit as below
handleDelete = (e,idBooks) => {
e.preventDefault();
axios.delete("http://localhost:8081/delete", idBooks )
.then(res => {
console.log(res.data);
}).catch(err => {
console.warn(err.warn);
});
};

Axios GET Request doesnt like undefined header(x-auth-token)

So I have a react app where the navbar component refuses to display after refreshing. The axios call to the endpoint doesnt appear to work if the header x-auth-token value is undefined.
I randomly set sethAuthtoken to a garbage value and it gives a 401 error, which makes sense, but if the localstorage.token has no value, I get nothing till it times out with the following message net::ERR_EMPTY_RESPONSE
This is action file, the navbar is referencing from:
Auth.js
import setAuthToken from '../utils/setAuthToken';
export const loadUser = () => async (dispatch) => { if
(localStorage.token) { setAuthToken(localStorage.token); }
const config = {
headers: {
"Content-Type": "application/json"
}
};
try {
const res = await axios.get("/api/auth", config);
dispatch({
type: USER_LOADED,
payload: res.data
});
} catch (err) {
dispatch({
type: AUTH_ERROR
});
}};
This is the back end route
router.get("/", auth, async (req, res) => {
try {
const user = await User.findById(req.user.id).select("-password");
res.json(user).status(200);
} catch (error) {
console.error(error.message);
res.status(500).send("Server error");
}});
This is the Navbar component code, which calls the action
import React, { Fragment } from "react";
import { Link } from "react-router-dom";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { logout } from "../../actions/auth";
const Navbar = ({ auth: { isAuthenticated, loading }, logout }) => {
const authLinks = (
<ul>
<li>
<a onClick={logout} href="!#">
<i className="fas fa-sign-out-alt" />{" "}
<span className="hide-sm">Logout</span>
</a>
</li>
</ul>
);
const guestLinks = (
<ul>
<li>
Developers
</li>
<li>
<Link to="/register">Register</Link>
</li>
<li>
<Link to="/login">Login</Link>
</li>
</ul>
);//will load guestlinks if not authenticated, authlinks if authenticated
return (
<nav className="navbar bg-dark">
<h1>
<Link to="/">
<i className="fas fa-code"></i> DevConnector
</Link>
</h1>
{loading ? null : (
<Fragment> {isAuthenticated ? authLinks : guestLinks} </Fragment>
)}
</nav>
);
};
Navbar.propTypes = {
logout: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired
};
const mapStateToProps = (state) => ({
auth: state.auth
});
export default connect(
mapStateToProps,
{ logout }
)(Navbar);
I just put an else statement in the auth action file for it dispatch the AUTH_ERROR to the reducer, when theres no token in localstorage.
export const loadUser = () => async dispatch => {
if (localStorage.token) {
setAuthToken(localStorage.token);
} else {
dispatch({
type: AUTH_ERROR
});
}

Reactjs - setState doesn't re-render after update

Simply all i'm trying to do is re render the notes list when i add another note to the database. i tried several methods even redux dispatch method. but none worked and it kinda make sense because when i add a note i don't add anything so it can get the updated notes through /budget. maybe i have a big misunderstanding.
here's how i add a new note
export function saveOneNote() {
// saving a note
const _id = $('input[name="_id"]').val(),
firstItem = $('input[name="firstItem"]').val(),
firstPrice = $('input[name="firstPrice"]').val(),
secondItem = $('input[name="secondItem"]').val(),
secondPrice = $('input[name="secondPrice"]').val(),
thirdItem = $('input[name="thirdItem"]').val(),
thirdPrice = $('input[name="thirdPrice"]').val(),
tBudget = $('input[name="tBudget"]').val();
let currency = $("#currency").val();
console.log(currency);
$.ajax({
url: "/newNote",
type: "post",
dataType: "json",
contentType: "application/json",
data: JSON.stringify({
currency,
_id,
firstItem,
firstPrice,
secondItem,
secondPrice,
thirdItem,
thirdPrice,
tBudget
}),
success: function(Data) {
console.log("note was saved!", Data);
},
error: function(err, status, xhr) {
console.log("err", err);
}
});
}
here's how i fetch notes
class ShowAll extends Component {
constructor(props){
super(props);
this.state = {
Data: [],
length:[],
searchbyid:[],
isLoggedIn:[]
}
}
componentDidMount(){
// fetch notes
Rquest.get('/budget').then((res)=>{
let DataString = Array.from(res.body);
this.setState((prevState,props)=>{
return {
Data: DataString,
length: res.body.length
}
})
}).catch((err)=> {
console.log(err);
})
// check if user is logged in
Request.get('/auth').then((user)=>{
if(user){
this.setState({
isLoggedIn: true
})
}
}).catch((err)=> {
this.setState({
isLoggedIn: false
})
});
}
render(){
const count = this.state.length;
const myNotes = this.state.Data;
const isLoggedIn = this.state.isLoggedIn;
const listItems = myNotes.map((dynamicData)=>{
return(
<Fragment key={dynamicData.id}>
<div className='jumbotron'>
<div className='row'>
<button className='btn btn-danger delete-note-btn' onClick={DeleteOneNote}>Delete</button>
<input className='col-12 title form-control' id='deleteById' value={dynamicData._id} readOnly/>
<div className="dropdown-divider"></div> {/*line divider*/}
<div className='col-6' >
<ul className='list-unstyled'>
<li className='items'>items</li>
<li >{dynamicData.firstItem}</li>
<li >{dynamicData.secondItem}</li>
<li >{dynamicData.thirdItem}</li>
{/* <li>Total Budget :</li> */}
</ul>
</div>
<div className='dynamicData col-6'>
<ul className ='list-unstyled'>
<li className='prices'>Prices</li>
<li>{dynamicData.firstPrice} {dynamicData.currency}</li>
<li>{dynamicData.secondPrice} {dynamicData.currency}</li>
<li>{dynamicData.thirdPrice} {dynamicData.currency}</li>
</ul>
</div>
</div>
<h3 className='col-12 totalprice'>{dynamicData.tBudget} {dynamicData.currency}</h3>
</div>
</Fragment>
)
})
return (
<Fragment>
{isLoggedIn ===true?(
<div className='myNotesList '>
number of notes : {count}
{listItems}
</div>
):(
<Fragment>
</Fragment>
)
}
</Fragment>
)
}
}
React components are re-rendering only on state or props change. In your code - you're not mutating state nor props of your component.
What you should do in your case probably is to re-fetch the items after save or add the new items to the state or pass through props.
Example:
class Notes extends React.Component {
state = { note: '', notes: [] }
changeNote = ({ target: { value } }) => {
this.setState({ note: value });
}
addNote = () => {
this.setState((state) => ({ notes: [...state.notes, state.note] }));
}
render() {
return (
<div>
<input type="text" onChange={this.changeNote} />
<button onClick={this.addNote}>Add</button>
<ul>
{this.state.notes.map(note =>
<li>{note}</li>
)}
</ul>
</div>
)
}
}
ReactDOM.render(
<Notes />,
document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.production.min.js"></script>
<div id="app">
</div>

Resources