Axios.post keeps sending empty objects to my API - javascript

I am trying to post new information about a cow to my cow API, however, everytime i hit the submit button on my frontend, it seems to be sending an empty object rather than the name of the cow, description of the cow, and image of the cow (via url). What is causing it to send an empty object versus my desired data?
Here is the frontend code:
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import './App.css';
const baseUrl = "http://localhost:3001/api/cows"
function Display({setNameOfCow, setImageOfCow, setDescriptionOfCow, nameOfCow, imageOfCow, descriptionOfCow}) {
axios.get(baseUrl)
.then(res => res.data)
.then(res => {
setNameOfCow(res.name)
setImageOfCow(res.image)
setDescriptionOfCow(res.description)
})
return (
<div>
<p>{nameOfCow}</p>
<img src={imageOfCow}/><p>{descriptionOfCow}</p>
</div>
)
}
function Input({setNameOfCow, setImageOfCow, setDescriptionOfCow, nameOfCow, imageOfCow, descriptionOfCow}) {
function handleSubmit(e) {
e.preventDefault()
let newObject = {
name: nameOfCow,
description: descriptionOfCow,
image: imageOfCow
}
axios.post(baseUrl, newObject)
}
return (
<div>
<form>
<label htmlFor="name">name: </label>
<input type="text" id="name" onChange={(e) => {
const eTarget = e.target.value
setNameOfCow(eTarget)}}/><br></br>
<label htmlFor="description">description: </label>
<input type="text" id="description" onChange={(e) => {
const eTargetDesc = e.target.value
setDescriptionOfCow(eTargetDesc)}}/><br></br>
<label htmlFor="image">image url: </label>
<input type='text' id="image" onChange={(e) => {
const eTargetImage = e.target.value
setImageOfCow(eTargetImage)}}/><br></br>
<button type="submit" onSubmit={handleSubmit}>Add a cow!</button>
</form>
</div>
)
}
function App() {
const [nameOfCow, setNameOfCow] = useState('')
const [descriptionOfCow, setDescriptionOfCow] = useState('')
const [imageOfCow, setImageOfCow] = useState('')
return (
<div className="App">
<Input imageOfCow={imageOfCow} setNameOfCow={setNameOfCow} setDescriptionOfCow={setDescriptionOfCow} setImageOfCow={setImageOfCow} />
<Display setNameOfCow={setNameOfCow} setImageOfCow={setImageOfCow} setDescriptionOfCow={setDescriptionOfCow} nameOfCow={nameOfCow} imageOfCow={imageOfCow} descriptionOfCow={descriptionOfCow} />
</div>
);
}
export default App
and here is the image showing the empty objects being posted:

Looking into your Input component props:
function Input({setNameOfCow, setImageOfCow, setDescriptionOfCow, nameOfCow, imageOfCow, descriptionOfCow}) {...
We can see that you missing to pass this props when using this component:
<Input imageOfCow={imageOfCow} setNameOfCow={setNameOfCow} setDescriptionOfCow={setDescriptionOfCow} setImageOfCow={setImageOfCow} />
The correct way to use is something like:
<Input
imageOfCow={imageOfCow}
nameOfCow={nameOfCow}
descriptionOfCow={descriptionOfCow}
setNameOfCow={setNameOfCow}
setDescriptionOfCow={setDescriptionOfCow}
setImageOfCow={setImageOfCow}
/>
Also the correct way to prevent the form default behavior is setting the onSubmit and the handleSubmit at the form attribute (you can remove from the button):
<form onSubmit={handleSubmit}>
Otherwise a very nice change is to put your axios request inside a useEffect hook to prevent your app from making request every time it re-render.
Using something like this the app will make the request only at the first component render.
const getCow = async (baseUrl) => {
const cow = await axios.get(baseUrl);
setNameOfCow(cow.name);
setImageOfCow(cow.image);
setDescriptionOfCow(cow.description);
};
useEffect(() => {
getCow(baseUrl);
}, []);

Related

Failed to get state by useLocation while it has Object data in state

First.js
import { useState } from "react";
import { Link } from "react-router-dom";
const First = () => {
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
}
return (
<div className="First">
<h1>This is First Input Page</h1>
<form onSubmit={handleSubmit}>
<dd>data 1</dd>
<input
type="text"
value={name}
onChange={(e) =>
setName(e.target.value)
}
required
></input>
<dd>data 2</dd>
<input
type="text"
value={phone}
onChange={(e) =>
setPhone(e.target.value)
}
required
></input><br/>
<Link to={'/second'} state={{ state: { name : name , phone : phone } }}><button>submit</button></Link>
</form>
</div>
);
}
export default First;
I try to send Object data using Link/state to another component.
Second.js
import {useLocation} from 'react-router-dom';
const Second = () => {
const location = useLocation();
console.log(location.state);
console.log(location.state.name);
return (
<div className="Second">
<h1>This is Second Input Page</h1>
<form>
<dd>data 3</dd>
<input></input>
<dd>data 4</dd>
<input></input><br/>
<button>submit</button>
</form>
</div>
);
}
export default Second;
However, while I can access to (location.state), I can not access to (location.state.name). Why is that?
Output
state: {name: 'Myname', phone: 'myphone'}
[[Prototype]]: Object
--------------------
undefined
The output shows that the code line "console.log(location.state);" works, but to the "console.log(location.state.name);", it shows undefined.
It's because you passed an object with state as the root property, i.e.
state={{ state: { name: name, phone: phone } }}
so to access it in the receiving route it is location.state.state.name.
You really don't need to nest the data you want to pass under a state property when using the Link component, it's not quite the same as when using the navigate function.
<Link to="/second" state={{ name, phone }}>
<button>submit</button>
</Link>
It may also be considered semantically incorrect HTML to nest a button element within an anchor tag (via Link) element. Use the useNavigate hook and issue an imperative navigation action from the form's submit handler. In this case the navigate function's second argument, the options object, *does* expect the state to be passed under the state` property.
Example:
import { Link, useNavigate } from "react-router-dom";
const First = () => {
const navigate = useNavigate();
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
navigate("/second", { state: { name, phone } });
};
return (
<div className="First">
<h1>This is First Input Page</h1>
<form onSubmit={handleSubmit}>
<dd>data 1</dd>
<input
type="text"
value={name}
onChange={(e) =>
setName(e.target.value)
}
required
/>
<dd>data 2</dd>
<input
type="text"
value={phone}
onChange={(e) =>
setPhone(e.target.value)
}
required
/>
<br/>
<button>submit</button>
</form>
</div>
);
};
export default First;

Use state is not updating in change event

I am building a social media app with MERN stack. The issue is that the profile picture and cover picture is not changing the state.
The API is working fine but after debugging I found that use state is not accepting the new state only in initial state i.e. null.
The handleChange functionality is working well and yet after updating resulting empty object in the formData
import React, {use State } from 'react';
import { Modal, useMantineTheme } from '#mantine/core';
import { use Dispatch, use Selector } from 'react-redux';
import { useParams } from 'react-router-dom';
import { uploadImage } from '../../actions/uploadAction';
import {updateuser} from '../../actions/userAction';
const ProfileModal = ({modalOpen,setModalOpen,data}) => {
const theme = useMantineTheme();
const {password,...other} = data;
const [formData,setFormData] = useState(other);
const [profileImage,setProfileImage] = useState(null);
const [coverImage,setCoverImage] = useState(null);
const dispatch = useDispatch();
const params = useParams();
const {user} = useSelector((state)=>state.authReducer.authData);
const handleChange = (e) =>{
setFormData({...formData, [e.target.name]:e.target.value})
console.log(formData)
}
const onImageChange = (event)=>{
if (event.target.files && event.target.files[0]) {
let image = event.target.files[0]
console.log(image)
event.target.name==="profilePicture"?setProfileImage(image):setCoverImage(image)
console.log(profileImage)
console.log(coverImage)
}
}
const handleSubmit =(e)=>{
e.preventDefault();
let userData = formData;
console.log(userData);
if(profileImage){
const data = new FormData();
const fileName = Date.now() + profileImage.name;
data.append("name",fileName);
data.append("file",profileImage);
userData.profilePicture = fileName;
console.log("profile",data)
}
try {
dispatch(uploadImage(data))
} catch (error) {
console.log(error)
}
if (coverImage) {
const data = new FormData();
const fileName = Date.now() + coverImage.name;
data.append("name",fileName);
data.append("file",coverImage);
userData.coverPicture = fileName;
}
try {
dispatch(uploadImage(data))
} catch (error) {
console.log(error)
}
dispatch(updateuser(params.id,userData))
console.log(userData)
setModalOpen(false)
}
return (
<Modal
overlayColor={theme.colorScheme === 'dark' ? theme.colors.dark[9] : theme.colors.gray[2]}
overlayOpacity={0.55}
overlayBlur={3}
size = '55%'
opened = {modalOpen}
onClose ={()=>setModalOpen(false)}>
<form className='infoForm'>
<h3>Your Info</h3>
<div>
<input type='text' className='infoInput' name='firstName'
placeholder='First Name' onChange={handleChange} value={formData.firstName} />
<input type='text' className='infoInput' name='lastName'
placeholder='Last Name' onChange={handleChange} value={formData.lastName} />
</div>
<div>
<input type='text' className='infoInput' name='worksAt'
placeholder='Work Location' onChange={handleChange} value={formData.worksAt}/>
</div>
<div>
<input type='text' className='infoInput' name='livesIn'
placeholder='Lives In' onChange={handleChange} value={formData.livesIn} />
<input type='text' className='infoInput' name='country'
placeholder='Country' onChange={handleChange} value={formData.country} />
</div>
<div>
<input type='text' className='infoInput' name='relationship' placeholder='Relationship Status' onChange={handleChange}
value={formData.relationship} />
</div>
<div>
profilePicture
<input type='file' name="profilePicture" onChange={onImageChange} alt=""/>
cover Image
<input type='file' name="coverPicture" onChange={onImageChange} alt="" />
</div>
<button className='button infoButton' onClick={handleSubmit}>Update</button>
</form>
</Modal>
);
}
export default ProfileModal
Setting the state in React acts like an async function.
Meaning that the when you set the state and put a console.log right after it, it will likely run before the state has actually finished updating.
Which is why we have useEffect, a built-in React hook that activates a callback when one of it's dependencies have changed.
Example:
useEffect(() => {
console.log(formData)
// Whatever else we want to do after the state has been updated.
}, [formData])
This console.log will run only after the state has finished changing and a render has occurred.
Note: "formData" in the example is interchangeable with whatever other state piece you're dealing with.
Check the documentation for more info about this.
Change your first import line from {use State} to {useState}...no whitespace import React, {useState } from 'react';

Not able to render api response data into form

i am not able to render api response to form, i am using same form for add and for edit too. i want when i click on Add button it should show initial empty value , but when i click edit button , it should so particular user data , but i am not able to achieve it.
I have tried
import React, { useState, useContext, useEffect } from "react";
import { useParams, useLocation } from "react-router-dom";
import { globalC } from "./context";
export default function DashboardForm() {
const { detail, apiCalling } = useContext(globalC);
useEffect(() => {
apiCalling(id);
}, []);
const { pathname } = useLocation();
const action = pathname.split("/")[1];
const [name, setName] = useState(action === "add" ? "" : detail.name);
const [username, setUsername] = useState("");
const [email, setEmail] = useState("");
const { id } = useParams();
return (
<div style={{ height: "100vh" }}>
<form>
<input
name="name"
type="text"
placeholder="name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<br />
<input
name="username"
type="text"
placeholder="username"
onChange={(e) => setUsername(e.target.value)}
/>
<br />
<input
name="email"
type="text"
placeholder="email"
onChange={(e) => setEmail(e.target.value)}
/>
</form>
</div>
);
}
i am use class based context api , user get api is called in context,
Codesandbox link : link
login and go to dashboard to try the example
Mind that your useState for name will be first called when the API call hasn't succeeded yet, giving you an empty name. Then once you do get the proper name in detail, the state is already created.
One solution would be something like:
if (!name && detail.name) {
setImmediate(() => setName(detail.name));
}
So once the API call gave you the name, and while the name input field is still empty, update it with the received name.
Another solution is to only show the form once the API request succeeded, and show a loading screen in the meantime.

How to access input elements from a React function

I currently have a form that is auto generated based on the amount of "Activities" a current user has. Each activity has a name and a value. My goal is to submit these values to the backend to update them. Yet I can't figure out how to reference these inputs. I was trying to read about using "ref"s, yet they come back with {current:null}
Here is the auto generated list (excuse the placeholder text)
When I inspect console here is what I find from the ref:
Here is my code:
import React, { useEffect } from "react";
import { useDispatch, useStore } from "react-redux";
import { connect } from "react-redux";
import * as actions from "../store/actions/patientSide";
export function ActivityTemplates() {
const dispatch = useDispatch();
const store = useStore();
const ref = React.createRef();
useEffect(() => {
// Update the document title using the browser API
dispatch(actions.getTodaysActivityTemplates());
}, []);
const activities = store.getState().patientSide.todays_activities;
const listedStuff = activities.map((activity) => (
<div>
{activity.activity_name}
<label for="value"> Value: </label>
<input
type="number"
id="value"
defaultValue={activity.value}
min="0"
max="10"
></input>
</div>
));
const saveActivities = () => {
var inputs = ref;
console.log(inputs);
// Insert code to run the call to the backend
};
return (
<div>
<h1> Activity Templates</h1>
<form id="form" onSubmit={saveActivities()} ref={ref}>
{listedStuff}
<input type="submit" name="save" />
</form>
</div>
);
}
export default ActivityTemplates;
I am very new to React and JS and honestly have very little idea of what I'm doing, but if someone could point me in the right direction that would be awesome!
EDIT: After sleeping on it, I've realized I've just been trying to force react into my HTML. I believe I should instead use a React Form Hook, and do it properly from the ground up.
<form onSubmit={handleOnSubmit}>
<label>User Name</label>
<input type="text" name="username" /><br/>
<label>Password</label>
<input type="password" name="password" /><br/>
<input type="submit" value="Submit" />
</form>
const handleOnSubmit = (event) => {
const formData = new FormData(event.target);
const formDetails = {};
event.preventDefault();
for (let entry of formData.entries()) {
formDetails[entry[0]] = entry[1];
};
console.log("formDetails", formDetails);
}
You are getting the input fields value from "FormData" on onSubmit.
const saveActivities = (event) => {
event.preventDefault();
const data = new FormData(event.target);
// Insert code to run the call to the backend
}
You need to store the value
const [value, setValue] = React.useState();
Then give your input a onChange={e => setValue(e.target.value)}
I would change the id though

React-Redux: Insert Data in DB

I'm learning to use react-redux, so sorry if I'm doing confusing!
I'm trying to create a form and insert data into the db.
Then I have created 2 page, one for the form and another one.
RequestForm
handleChange = (e) => {
let meeting = this.state.meeting;
meeting[e.target.name] = e.target.value;
this.setState({ meeting });
};
handleSubmit(event) {
event.preventDefault();
console.log(this.state.meeting);
this.props.addMeeting(this.state)
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<div>
<label>Motivation:</label>
<input
type="text"
name="motivation"
onChange={(event) => this.handleChange(event)}
/>
</div>
<div>
<label>Date:</label>
<input
type="date"
name="date"
onChange={(event) => this.handleChange(event)}
/>
</div>
MeetingRequest.js
import RequestForm from './RequestForm';
import { connect } from 'react-redux';
import {addMeeting} from '../Redux/actions';
class MeetingRequest extends Component {
render() {
const addMeeting = this.props
return (
<div>
<h2>Request Meeting</h2>
<RequestForm
addMeeting={addMeeting}
/>
</div>
);
}
}
function mapDispatchToProps(dispatch) {
return {
addMeeting: (meeting) => dispatch(addMeeting(meeting)),
};
}
export default connect(null, mapDispatchToProps)(MeetingRequest);
EDIT. Thank you to the comments the firt error is resolved, now I have a 500 problem. Could it be a problem about the Actions.js??
And also in your opinion is right how I'm trying to do??
Thank you
Change:
-const addMeeting = this.props
+const { addMeeting } = this.props
in MeetingRequest.js
Regarding your 500 error on API, it may be because you're sending JSON while setting multipart/form-data. Inspect using Network tab in dev tools.
If your backend need multipart/form-data you have to convert your state.meeting object to FormData

Categories