Can't access data in Context in MERN Stack Application - node.js

Why I can't access my product data from context?
Here is where product data came from:
const Products = props => {
const [user, setUser, addToCart, setChosenImage] = useContext(UserContext);
const [product, productData] = useContext(ProductContext);
...
<div className="product--TopButton--Container">
<button className="about--Button" onClick={() => productData(product._id)}>About</button>
</div>
Here is the context:
export const ProductContext = React.createContext({})
const ProductProvider = ProductContext.Provider;
const ProductContextProvider = (props) => {
const [product, setProduct] = useState({})
const productData = (id) => {
fetch (`${API}/products/details/${id}`, {
method: 'GET',
})
.then (response => response.json ())
.then (response => {
setProduct(response)
})
.then(() => window.location = `/details/${id}`)
.catch (error => {
console.error (error);
})
}
console.log(product)
return (
<ProductProvider value={[product, productData]}>
{props.children}
</ProductProvider>
)
}
export default ProductContextProvider;
Note: The console.log is showing the correct product. But I can't access it in the ProductAbout component.
Here is how I provide it:
<ProductContextProvider>
<Route exact path="/products" component={Products} />
<Route exact path="/details/:id" component={ProductAbout} />
</ProductContextProvider>
Here is where I'm trying to use it:
const ProductAbout = () => {
const [product] = useContext(ProductContext)
console.log(product)
return(
<div>hi{product?.name}</div>
)
}
export default ProductAbout;

Related

[Unhandled promise rejection: FirebaseError: Function addDoc() called with invalid data. Unsupported field value: a custom Class object

I want to post data in firebase firestore database but I don't understand why that does'nt work
I Can post prewrite title in database but not with custom title from an input
traceback :
[Unhandled promise rejection: FirebaseError: Function addDoc() called with invalid data. Unsupported field value: a custom Class object (found in field date in document events/T58NdobxrvVchtaQjYx0)]
My addEvent function to add eventDate and a title to an firebase events collection
firebase.js
export async function addEvent(date, title) {
const eventsRef = collection(db, EVENTS_COLLECTION);
const newEventRef = await addDoc(eventsRef, {
date,
title,
})
.then(() => {
// Data saved successfully!
console.log("data submitted");
})
.catch((error) => {
// The write failed...
console.log(error);
});
const newEvent = await getDoc(newEventRef);
return newEvent;
}
My calendar to add data on firebase.
I have a button that open picker date time modal and next modal with an input to add data with submit and cancel button.
CalendarScreen.js
import React, { useState } from "react";
import { View, Button, StyleSheet, Modal, TextInput } from "react-native";
import { addEvent } from "../../firebase/firebase";
import CalendarPicker from "react-native-calendar-picker";
import DateTimePickerModal from "react-native-modal-datetime-picker";
export default function CalendarScreen({ selectedDate, onDateChange }) {
const [isDatePickerVisible, setIsDatePickerVisible] = useState(false);
const [isTitleModalVisible, setIsTitleModalVisible] = useState(false);
const [eventTitle, setEventTitle] = useState("");
const showDatePicker = () => {
setIsDatePickerVisible(true);
};
const hideDatePicker = () => {
setIsDatePickerVisible(false);
};
const showTitleModal = () => {
setIsTitleModalVisible(true);
};
const hideTitleModal = () => {
setIsTitleModalVisible(false);
};
const handleAddEvent = (eventDate, eventTitle) => {
hideDatePicker(eventDate, eventTitle);
showTitleModal();
};
const handleSubmitTitle = (eventDate, eventTitle) => {
addEvent(eventDate, eventTitle);
hideTitleModal();
setEventTitle("");
};
return (
<View>
<CalendarPicker
style={styles.calendar}
selectedDate={selectedDate}
onDateChange={onDateChange}
/>
<Button title="Add Event" onPress={showDatePicker} />
<DateTimePickerModal
isVisible={isDatePickerVisible}
mode="datetime"
onConfirm={handleAddEvent}
onCancel={hideDatePicker}
/>
<Modal style={styles.modal} visible={isTitleModalVisible}>
<View>
<TextInput
placeholder="Event Title"
value={eventTitle}
onChangeText={setEventTitle}
style={styles.input}
/>
<Button title="Submit" onPress={handleSubmitTitle} />
<Button title="Cancel" onPress={hideTitleModal} />
</View>
</Modal>
</View>
);
}
EDIT When string is typed
export async function getEvents() {
const eventsRef = collection(db, EVENTS_COLLECTION);
const docSnap = await getDocs(eventsRef);
const events = [];
docSnap.forEach((doc) => {
events.push({ id: doc.id, ...doc.data() });
});
return events;
}
CalendarScreen.js
export default function CalendarScreen({ selectedDate, onDateChange }) {
const [isDatePickerVisible, setIsDatePickerVisible] = useState(false);
const showDatePicker = () => {
setIsDatePickerVisible(true);
};
const hideDatePicker = () => {
setIsDatePickerVisible(false);
};
const handleAddEvent = (eventDate) => {
addEvent(eventDate);
hideDatePicker();
};
return (
<View>
<CalendarPicker selectedDate={selectedDate} onDateChange={onDateChange} />
<Button title="Add Event" onPress={showDatePicker} />
<DateTimePickerModal
isVisible={isDatePickerVisible}
mode="datetime"
onConfirm={handleAddEvent}
onCancel={hideDatePicker}
/>
</View>
);
temporary SOLUTION : See you database even with the error
CalendarScreen.js
import React, { useState } from "react";
import { View, Button, StyleSheet, Modal, TextInput } from "react-native";
import { addEvent } from "../../firebase/firebase";
import CalendarPicker from "react-native-calendar-picker";
import DateTimePickerModal from "react-native-modal-datetime-picker";
import moment from "moment-timezone";
moment.tz.setDefault("Europe/Paris");
export default function CalendarScreen({ selectedDate, onDateChange }) {
const [isDatePickerVisible, setIsDatePickerVisible] = useState(false);
const [isModalVisible, setIsModalVisible] = useState(false);
const [eventTitle, setEventTitle] = useState("");
const showDatePicker = () => {
setIsDatePickerVisible(true);
};
const hideDatePicker = () => {
setIsDatePickerVisible(false);
};
const showModal = () => {
setIsModalVisible(true);
};
const hideModal = () => {
setIsModalVisible(false);
};
const handleAddEvent = (eventDate) => {
addEvent(eventDate, eventTitle);
showModal();
};
const handleSubmit = (eventDate) => {
addEvent(eventDate, eventTitle);
hideModal();
hideDatePicker();
};
return (
<View>
<CalendarPicker selectedDate={selectedDate} onDateChange={onDateChange} />
<Button title="Add Event" onPress={showDatePicker} />
<DateTimePickerModal
isVisible={isDatePickerVisible}
mode="datetime"
onConfirm={handleAddEvent}
onCancel={hideDatePicker}
/>
<Modal animationType="slide" transparent={false} visible={isModalVisible}>
<View>
<TextInput
placeholder="Event Title"
value={eventTitle}
onChangeText={setEventTitle}
/>
<Button
title="Submit"
onPress={(eventDate) => handleSubmit(eventDate)}
/>
<Button title="Cancel" onPress={hideModal} />
</View>
</Modal>
</View>
);
export async function addEvent(date, title) {
const eventsRef = collection(db, EVENTS_COLLECTION);
const newEventRef = await addDoc(eventsRef, {
date: date,
title: title,
})
.then(() => {
// Data saved successfully!
console.log("data submitted");
})
.catch((error) => {
// The write failed...
console.log(error);
});
const newEvent = await getDoc(newEventRef);
return newEvent;
}
SOLUTION updated from scra:
export async function addEvent(event) {
try {
const eventsRef = collection(db, EVENTS_COLLECTION);
const docRef = await addDoc(eventsRef, event);
console.log("Event is added, id :", docRef.id);
} catch (error) {
console.error(error);
}
}
const handleDatePicked = (date) => {
const formattedDate = date.toLocaleDateString();
const formattedTime = date.toLocaleTimeString();
setDate(`${formattedDate} ${formattedTime}`);
setDateTimePickerVisible(false);
};
const handleSubmit = async () => {
try {
await addEvent({ ...event, date });
} catch (error) {
console.error(error);
}
};
return (
<View>
<TextInput
onChangeText={(text) => setEvent({ ...event, name: text })}
placeholder="Event name"
/>
<Text>{date}</Text>
<Button
Color="red"
title="Pick date and time"
onPress={showDateTimePicker}
/>
<DateTimePicker
mode="datetime"
isVisible={isDateTimePickerVisible}
onConfirm={handleDatePicked}
onCancel={() => setDateTimePickerVisible(false)}
/>
<Button title="Submit" onPress={handleSubmit} />
</View>
);
The error indicates that you try to write a document with an unsupported field value for the field date.
You will find here the list of the data types supported by Firestore. You need to transform the Object returned by your date picker to an Object that has a type supported by Firestore.

Delete request not working in React+Express+MongoDB app

im working through fullstackopen course along TOP, every excercise went well so I drifted off the course to build simple todo app to solidify the knowledge i gained so far. So i developed front end with react, then the back end with node express connected to mongoDB. All seemed fine but then the delete request stopped working - every other request works fine, only the delete causes errors. After requesting a delete the page crashes, BUT in the database the request is fulfilled and the note is removed. So when I reconnect to the node server and refresh the page, the content is up to date and everything seems to work again.
RESTclient is saying that delete request works fine. But in the browser, when i click delete button, after like a second the app crashes and this is shown in the console:
Notes.js:20 Uncaught TypeError: Cannot read properties of null (reading 'id')
at Notes.js:20:27
at Array.map (<anonymous>)
at b (Notes.js:19:16)
at xo (react-dom.production.min.js:167:137)
at Pi (react-dom.production.min.js:197:258)
at Eu (react-dom.production.min.js:292:88)
at bs (react-dom.production.min.js:280:389)
at gs (react-dom.production.min.js:280:320)
at vs (react-dom.production.min.js:280:180)
at ls (react-dom.production.min.js:271:88)
server.js:
require("dotenv").config();
const express = require("express");
const morgan = require("morgan");
const cors = require("cors");
const mongoose = require("mongoose");
const Note = require("./models/note");
const app = express();
app.use(express.static("build"));
app.use(express.json());
app.set("json spaces", 2);
app.use(cors());
app.use(morgan("tiny"));
/// DEFINE DEFAULT PORT //
const PORT = process.env.PORT;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
/// DEFINE BASIC ROUTES
app.get("/notes", (request, response, next) => {
Note.find({}).then((notes) => response.json(notes));
});
app.get("/notes/:id", (request, response, next) => {
Note.findById(request.params.id)
.then((note) => {
if (note) {
response.json(note);
} else {
response.status(404).end();
}
})
.catch((error) => next(error));
});
/// DELETE ///
app.delete("/notes/:id", (request, response, next) => {
Note.findByIdAndRemove(request.params.id)
.then((response) => response.status(204).end())
.catch((error) => next(error));
});
/// UPDATE ///
app.put("/notes/:id", (request, response, next) => {
const {content, done} = request.body
Note.findByIdAndUpdate(
request.params.id,
{content, done},
{new: true, runValidators: true, context: "query"},
)
.then(updatedNote => response.json(updatedNote))
.catch(error => next(error))
})
/// ADD ///
app.post("/notes", (request, response, next) => {
const body = request.body;
if (!body.content) {
return response.status(400).json({
error: "content missing",
});
}
const note = new Note({
content: body.content,
done: false,
});
note
.save()
.then((saved) => response.json(saved))
.catch((error) => next(error));
});
/// HANDLE UNDEFINED ROUTES ///
const unknownEndpoint = (request, response) => {
response.status(404).send({ error: "unknown endpoint" });
};
app.use(unknownEndpoint);
/// HANDLE ERRORS ///
const errorHandler = (error, request, response, next) => {
console.error(error.message);
if (error.name === "CastError") {
return response.status(400).send({ error: "malformatted id" });
} else if (error.name === "ValidationError") {
return response.status(400).json({ error: error.message });
}
next(error);
};
app.use(errorHandler);
front-end,
app.js:
import { useState, useEffect } from "react";
import css from "./App.css"
import Button from "./Button";
import Input from "./Input";
import noteService from "./services/NoteService";
import Notes from "./Notes";
function App() {
const [notes, setNotes] = useState([]);
const [newNote, setNewNote] = useState("");
useEffect(() => {
noteService.getAll().then((response) => {
setNotes(response);
});
}, []);
const handleInput = (event) => {
const content = event.target.value;
setNewNote(content);
};
const handleSubmit = (event) => {
event.preventDefault();
const note = { content: newNote, done: false };
noteService
.create(note)
.then((response) => setNotes(notes.concat(response)));
setNewNote("");
};
const handleDelete = (id) => {
noteService
.trash(id)
.then(setNotes(notes.filter((note) => note.id !== id)));
};
const toggleStatus = (id) => {
const note = notes.find((item) => item.id === id);
const updated = { ...note, done: !note.done };
noteService.update(id, updated).then((response) => {
setNotes(notes.map((note) => (note.id !== id ? note : response)));
});
};
const showDone = () => {
noteService.getAll().then((response) => {
setNotes(response.filter((note) => note.done));
});
};
const showUndone = () => {
noteService.getAll().then((response) => {
setNotes(response.filter((note) => !note.done));
});
};
const showAll = () => {
noteService.getAll().then((response) => {
setNotes(response);
});
};
return (
<div className="container">
<h1>TO_DO NOTES</h1>
<div className="header">
<Input action={handleInput} value={newNote} />
<Button text={"Add"} action={handleSubmit} />
</div>
<div>
<Button text="Show All" action={showAll} />
<Button text="Show Done" action={showDone} />
<Button text="Show Undone" action={showUndone} />
</div>
<Notes notes={notes} action={handleDelete} toggle={toggleStatus}/>
</div>
);
}
export default App;
Notes.js:
import Button from "./Button";
import css from "./Notes.css";
const Note = ({ item, action, toggle }) => {
return (
<li
onClick={() => toggle(item.id)}
className={item.done ? "done" : "undone"}
>
{item.content} <Button text="x" action={() => action(item.id)} />
</li>
);
};
const Notes = ({ notes, action, toggle }) => {
return (
<>
<ul>
{notes.map((item) => (
<Note key={item.id} item={item} action={action} toggle={toggle} />
))}
</ul>
</>
);
};
export default Notes;
NoteService.js:
import axios from "axios";
const baseUrl = "/notes";
const getAll = () => {
const request = axios.get(baseUrl);
return request.then((response) => response.data);
};
const create = (newObject) => {
const request = axios.post(baseUrl, newObject);
return request.then((response) => response.data);
};
const update = (id, newObject) => {
const request = axios.put(`${baseUrl}/${id}`, newObject);
return request.then((response) => response.data);
};
const trash = id => {
const request = axios.delete(`${baseUrl}/${id}`)
return request.then(result => result.data)
}
export default {
getAll,
create,
update,
trash,
};
I would really appreciate some help. I compared this project with the other one i have thats structured the same, the other one is working but here cannot figure out what is wrong.
In the Notes.js file within the Notes component, where you are iterating using notes.map, change it to notes?.map and see if that works.

update real time in react and nodejs [duplicate]

This question already exists:
update context real time in react [duplicate]
Closed 1 year ago.
I am trying to achieve when user upload their profile image on website and it auto change the old profile picure into new profile picture instead user have to log out and log back in to make it works.
Here is my front end code base:
const UserCard = ({ picture, name, userEmail, isVerified, id, setPicture, setUser}) => {
const [imageSelected, setImageSelected] = useState("");
useEffect(() => {
if (imageSelected !== '') {
uploadImage();
}
}, [imageSelected]);
const uploadImage = () => {
const formData = new FormData();
formData.append("file", imageSelected);
formData.append("id", id);
axios
.post("/api/v1/users/upload/image", formData, {
headers: { "Content-Type": "multipart/form-data" },
})
.then((response) => {
setPicture(response.data.data.imageUrl);
setUser(prev => ({ ...prev, picture: response.data.data.imageUrl }));
});
};
// End of Method
const inputFile = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted file input element
inputFile.current.click();
};
return (
<div className="avatar--icon_profile">
<Card className="profile--card_container">
<CardContent>
{picture ? (
<div>
<input
className="my_file"
type="file"
ref={inputFile}
onChange={(e) => setImageSelected(e.target.files[0])}
/>
<div className="profile-image">
<Avatar
src={picture}
alt="Avatar"
className="avatar--profile_image"
onClick={onButtonClick}
/>
</div>
</div>
</div>
and here is my backend router to send image from client to cloudinary (where I store all the images):
router.post('/upload/image', function (req, res, next) {
const dUri = new Datauri();
const dataUri = (req) => dUri.format(path.extname(req.name).toString(), req.data);
if (req.files !== undefined && req.files !== null) {
const { file, id } = req.files;
const newFile = dataUri(file).content;
cloudinary.uploader.upload(newFile)
.then(result => {
const imageUrl = result.url;
const data = {id : req.body.id, imageUrl };
updateAvatar(data);
return res.status(200).json({ message: 'Success', data: { imageUrl } });
}).catch(err => res.status(400).json({message:'Error', data: { err}}));
} else {
return res.status(400).json({ message: 'Error' });
}
});
How can I achieve it ?
Added GlobalState.js:
const GlobalState = (props) => {
// User State -----------------------------------------------------------------------------
const [currentUser, setUser] = useState(props.serverUserData);
// This method is passed through context to update currentUser
const updateUser = (userData) => {
setUser(userData);
};
// Modal State -----------------------------------------------------------------------------
const [isModalOpen, setModalOpenState] = useState(false);
const [modalToDisplay, setModalToDisplay] = useState("signup");
// This function will be provided to any function that needs to toggle the modal.
const toggleModal = () => {
// Take the previous state and flip it.
setModalOpenState((prevState) => !prevState);
};
// This method is passed through context to update the next modal to open.
const setModal = (name) => {
// Take the passed in modal name and set state.
setModalToDisplay(name);
};
// Loading State ---------------------------------------------------------------------------
// NOT REACT STATE
const [loading, setLoadingState] = useState(false);
// This state will be used as messages in effects to refetch data.
const [reloadThisData, setWhatToReload] = useState("");
// User profile id for query ----------------------------------------------------------------
const [userProfileId, setUserProfileId] = useState("");
// Flag to determine if the header should change css style. ----------------------------------------------------------------
const [adjustBrightness, setAdjustBrightness] = useState(false);
// This is the object passed to GlobalContext.Provider
const providerValues = {
isModalOpen,
toggleModal,
modalToDisplay,
setModal,
currentUser,
updateUser,
loading,
setLoadingState,
reloadThisData,
setWhatToReload,
userProfileId,
setUserProfileId,
adjustBrightness,
setAdjustBrightness,
};
return (
<GlobalContext.Provider value={providerValues}>
{props.children}
</GlobalContext.Provider>
);
};
export default GlobalState;
Added console.log(currentUser):
{id: "a9aa869e-e28b-4a06-b5c7-88571d490e04", name: "nhan nguyen", email: "nhannguyen4119#gmail.com", publicId: "nh1615539696370", picture: "http://res.cloudinary.com/teammateme/image/upload/v1617073225/hvckrm6bklbpjk9njrlf.jpg", …}
email: "nhannguyen4119#gmail.com"
id: "a9aa869e-e28b-4a06-b5c7-88571d490e04"
isSessionValid: true
name: "nhan nguyen"
picture: "http://res.cloudinary.com/teammateme/image/upload/v1617073225/hvckrm6bklbpjk9njrlf.jpg"
publicId: "nh1615539696370"
__proto__: Object
Update my code after edit:
const UserProfile = () => {
const appState = useContext(GlobalContext);
const { currentUser, setUser } = appState;
const { email, name, id } = currentUser;
const [isVerified, setIsVerified] = useState(false);
const [picture, setPicture] = useState(currentUser.picture);
const checkVerificationData = () => {
axios.get("/api/v1/profiles/profile").then((res) => {
const { data } = res;
if (data.verifiedDT) {
setIsVerified(data.verifiedDT);
}
});
};
useEffect(() => {
checkVerificationData();
}, [isVerified]);
const classes = useStyles();
return (
<div className={classes.root}>
<Grid item xs={12}
container
direction="row"
justify="center"
alignItems="center"
spacing={4}>
<Grid item>
<Grid item>
<UserCard
picture={picture}
setPicture={setPicture}
userEmail={email}
name={name}
isVerified={isVerified}
id={id}
setUser={setUser}
/>
<br />
</Grid>
<div>
<Grid item>
<div className="profile--layout_userInfo">
<Grid item>
<UserInfo />
</Grid>
</div>
</Grid>
</div>
</Grid>
<div>
<Grid item>
<div className="profile--layout_ratings_reviews_block">
<UserRatingsDetailed userEmail={email} />
</div>
</Grid>
</div>
</Grid>
</div>
);
};
export async function getServerSideProps(context) {
let serverUserData = {};
if (
context.req.session.passport !== undefined &&
context.req.session.passport.user !== undefined
) {
const userPassportInfo = context.req.session.passport.user;
const { id, name, email, publicId, picture } = userPassportInfo;
const isSessionValid = context.req.isAuthenticated();
serverUserData = {
id,
name,
email,
publicId,
picture,
isSessionValid,
};
}
return { props: { serverUserData } };
}
UserProfile.propTypes = {
serverUserData: PropTypes.object,
};
export default UserProfile;
My suggestion from the comments in answer form is to create a manager similar to your other managers in the global context of your react application.
const GlobalState = (props) => {
// Profile Image -----------------------------------------------------------------------
const [currentProfileImage, setProfileImage] = useState(/*props.serverUserData.profileImage??*/);
...
};
export default GlobalState;
Don't forget to update your providerValues to include these new values.
Then, anywhere you use the profile image URI use the currentProfileImage variable from your context provider.
Lastly, when you upload your image to your server, and receive the new URI in the response use the setProfileImage function from global state to update the profile image in your global state.
const uploadImage = () => {
const formData = new FormData();
formData.append("file", imageSelected);
formData.append("id", id);
axios.post("/api/v1/users/upload/image", formData, {
headers: {"Content-Type": "multipart/form-data"},
}).then((response) => {
GlobalState.setProfileImage(response.data.data.imageUrl);
});
};
*I don't have a complete view of your program, and so this is my best guess of a reasonable way to implement your desired behaviour.
You'll need to update the initial state of the useState directive to reflect where in your props structure the profileImage URI is actually located, and you'll need to update the GlobalState placeholder to reflect however you are actually accessing your provided context.

How to patch/put data in nodejs and mongodb API using react hook

I have an API which I'm using react to consume. The data displays list of products and the prices in tabular form. I've tested all API endpoints with Postman, all routes are working fine. Now, I'm building a react frontend to consume the API. I'm having issues updating a product. Below is my code.
My Home.js
const Home = () => {
const classes = useStyles();
const history = useHistory()
const [error, setError] = useState(null);
const [isLoaded, setIsLoaded] = useState(false);
const [items, setItems] = useState([]);
const [currentName, setCurrentName] = useState("");
const [currentPrice, setCurrentPrice] = useState("");
const editRow = (id, name, price) => {
setCurrentName(name);
setCurrentPrice(price);
console.log(name +": "+ price);
history.push(`/edit/${id}`)
}
const deleteProduct = (id) => {
if (window.confirm('Are you sure?')) {
fetch(`products/${id}`, { method: 'DELETE' })
.then(async response => {
const data = await response.json();
if (!response.ok) {
const error = (data && data.message) || response.ok;
return Promise.reject(error);
}
console.log('Deleted succesfully!');
window.location.reload();
})
.catch(error => {
console.error('There was an error: ', error);
})
}
}
useEffect(() => {
fetch("/products")
.then(res => res.json())
.then(
(result) => {
setIsLoaded(true);
setItems(result.products);
},
(error) => {
setIsLoaded(true);
setError(error);
}
)
}, []);
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<Container className={classes.root}>
<ProductsList items={items} editRow={editRow} deleteProduct={deleteProduct} />
<div>
<Button
variant="contained"
color="primary"
href="/addproducts">
<AddBoxIcon/> Add Products
</Button>
</div>
</Container>
)
}
}
export default Home;
My ProductList.js
function ProductsList(props) {
const classes = useStyles();
const capitalise = (word) => {
let res = word.toUpperCase();
return res
}
return (
<>
<TableContainer component={Paper}>
<Table className={classes.table} aria-label="customized table">
<TableHead>
<TableRow>
<StyledTableCell><h2>Item</h2></StyledTableCell>
<StyledTableCell align="right"><h2>Price</h2></StyledTableCell>
<StyledTableCell align="right"><h2>Edit</h2></StyledTableCell>
<StyledTableCell align="right"><h2>Delete</h2></StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{props.items.length > 0 ? (
props.items.map((item, id) => (
<StyledTableRow key={id}>
<StyledTableCell component="th" scope="row">
{capitalise(item.name)}
</StyledTableCell>
<StyledTableCell align="right">{item.price}</StyledTableCell>
<StyledTableCell align="right">
<EditIcon
onClick={() => props.editRow(item._id, item.name,item.price)}
>
</EditIcon>
</StyledTableCell>
<StyledTableCell align="right">
<DeleteForeverIcon
onClick={() => props.deleteProduct(item._id)}
>
</DeleteForeverIcon>
</StyledTableCell>
</StyledTableRow>
))
) : (
<StyledTableCell align="right">No item</StyledTableCell>
)}
</TableBody>
</Table>
</TableContainer>
</>
);
}
export default ProductsList;
My EditUserForm.js
const EditUserForm = (props) => {
const history = useHistory();
const match = useRouteMatch();
let routeId = match.params.id;
console.log(routeId);
const [error, setError] = useState(null);
const [isLoaded, setIsLoaded] = useState(false);
const [item, setItem] = useState([]);
const handleInputChange = (e) => {
console.log(e.target.value)
const { name, value } = e.target;
setItem({ ...item, [name]: value});
};
const handleSubmit = (e) => {
e.preventDefault();
updateProduct();
history.push('/');
}
const updateProduct = (routeId) => {
fetch(`/addproducts/${routeId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: item.name,
price: item.price
}),
})
.then((res) => res.json())
.then((result) => setItem(result))
.catch((err) => console.log('error: ', err))
}
return (
<form onSubmit={handleSubmit} >
<label>Name</label>
<input
type="text"
name="name"
value=""
onChange={handleInputChange}
/>
<label>Price</label>
<input
type="text"
name="price"
value=""
onChange={handleInputChange}
/>
<button>Update</button>
<button
onClick={() => props.setEditing(false)}
>
Cancel
</button>
</form>
)
}
export default EditUserForm
The POST and DELETE are working fine.

React Hooks: How to make a POST request to server

I am a beginner.I am trying to implement a POST request from React.js in a simple form, but I cannot figure out how to send POST request to database. I guess I need <form action="URL"> as well.
Any help will be appreciated.
Below is the code from React.js(frontend)
import GameTestResult from './GameTestResult';
export default function App() {
const[data, setData] = useState([]);
const [formData, setFormData] = useState("");
useEffect (() => {
fetch('http://localhost:3000/game')
.then(res => res.json())
.then(result => setData(result.rows))
.catch(err => console.log("error"))
},[]);
const handleChange = event => {
setFormData(event.target.value)
}
const eventHandler = event => {
event.preventDefault();
setFormData("");
}
return (
<div className="App">
<form method="post" onSubmit = {eventHandler}>
<input value = {formData} onChange = {handleChange} />
<button type="submit">click</button>
</form>
{data && data.map((element, index)=>(
<GameTestResult
name = {element.name}
key={element.index}
/>
))}
</div>
);
}
here is the code from express.js(backend)
var router = express.Router();
const pool = require("../config.js");
var cors = require('cors');
router.get("/game", cors(), (req, res) => {
pool
.query("SELECT * FROM game")
.then((data) => res.json(data))
.catch((e) => {
res.sendStatus(404), console.log(e);
});
});
router.post("/game", (req, res) => {
const { name } = req.body;
pool
.query('INSERT INTO game(name) values($1);', [name])
.then(data => res.status(201).json(data))
.catch(e => res.sendStatus(404));
});
module.exports = router;
Here is what you can do:
Fetch games when component is mounted. And Submit new game when form is submitted.
export default function App() {
const [data, setData] = useState([])
const [formData, setFormData] = useState('')
useEffect(() => {
fetchGames() // Fetch games when component is mounted
}, [])
const fetchGames = () => {
fetch('http://localhost:3000/game', {
method: 'GET',
})
.then((res) => res.json())
.then((result) => setData(result.rows))
.catch((err) => console.log('error'))
}
const saveGames = () => {
fetch('http://localhost:3000/game', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: formData, // Use your own property name / key
}),
})
.then((res) => res.json())
.then((result) => setData(result.rows))
.catch((err) => console.log('error'))
}
const handleSubmit = (event) => {
event.preventDefault()
saveGames() // Save games when form is submitted
}
const handleChange = (event) => {
setFormData(event.target.value)
}
return (
<div className="App">
{/* method="post" not needed here because `fetch` is doing the POST not the `form` */}
{/* Also, note I changed the function name, handleSubmit */}
<form onSubmit={handleSubmit}>
<input type="text" name="name" value={formData} onChange={handleChange} />
<button type="submit">click</button>
</form>
{data &&
data.map((element, index) => (
<GameTestResult name={element.name} key={element.index} />
))}
</div>
)
}
You can read this about how to use fetch and this about how forms work in RecatJS.
Add name as "name" to input
Listen onChange and set data setFormData({[event.target.key]: event.target.value}) the data will be for example {name: "Tony"}
Call POST request on onClick action of button like code below
JSON.stringify(data) is important to convert js object to JSON when sending it to server
import GameTestResult from './GameTestResult'
export default function App() {
const [data, setData] = useState([])
const [formData, setFormData] = useState({})
useEffect(() => {
fetch('http://localhost:3000/game')
.then((res) => res.json())
.then((result) => setData(result.rows))
.catch((err) => console.log('error'))
}, [])
const handleChange = (event) => {
setFormData({ [event.target.name]: event.target.value })
}
const eventHandler = (event) => {
fetch('http://localhost:3000/game', {
method: 'POST',
body: JSON.stringify(formData),
})
.then((res) => res.json())
.then((result) => {
console.log(result)
setFormData('')
})
.catch((err) => console.log('error'))
}
return (
<div className="App">
<form>
<input name="name" value={formData.name || ''} onChange={handleChange} />
<button onClick={eventHandler}>click</button>
</form>
{data &&
data.map((element, index) => (
<GameTestResult name={element.name} key={element.index} />
))}
</div>
)
}

Resources