I have a form with some input with a default value and its hidden. I want to submit its value, together with other inputs to the database using an API, I have tried the following method, but it is not working. Kindly help.
const PostWidget = ({ post }) => {
const [loading, setLoading] = useState(false);
const [show, setShow] = useState(false);
const [commentInput, setComment] = useState({
post_id: '',
commentcontent: '',
});
const [errorlist, setError] = useState([]);
const handleInput = (e) => {
e.persist();
setComment({ ...commentInput, [e.target.name]: e.target.value });
}
const submitComment = (e) => {
e.preventDefault();
setLoading(true);
const data = {
post_id: commentInput.post_id,
commentcontent: commentInput.commentcontent,
};
axios.post(`/api/store-comment`, data).then((res) => {
if (res.data.status === 200) {
toast.success(res.data.message, "success");
setLoading(false);
}
else if (res.data.status === 422) {
toast.error("Your Comment is too Long", "", "error");
setError(res.data.errors);
setLoading(false);
}
});
}
return (
<div className="form-group boxed">
<div className="input-wrapper">
<form onSubmit={submitComment}>
<input defaultValue={post.postid} hidden />
<input type="text" name="commentcontent" className="form-control" onChange={handleInput} value={commentInput.commentcontent} placeholder="Write your Comment" />
<button type="submit" className="send-input">
{loading ? <><span className="spinner-border spinner-border-sm spinner-comment" role="status" aria-hidden="true"></span></> : <><i className="fi fi-rr-arrow-circle-right"></i></>}
</button>
</form>
</div>
);
}
export default PostWidget;
I think your issue might be due to the fact that you're setting the initial state of post_id to an empty string and as far as I can tell, it never gets updated.
I don't think you even need to keep post_id in the commentInput state. Just remove it and change your data object to:
const data = {
post_id: post.post_id,
commentcontent: commentInput.commentcontent,
};
Inside of your submitComment you can use:
const postId = e.target["postId"].value
If you add following to your hidden input:
name="postId"
But in your instance you can just use post.postid that you are getting from props instead of getting it from hidden input.
Related
I'm currently converting the logic in my mern (with typescript) project to use React/Tanstack query to learn this tool better.
I want to use useMutation to handle the post request logic from the details inputted in the form, in this login component but can't figure out how to do this. Any tips would be appreciated thanks. Below is the code from my login component
const Login = () => {
const navigate = useNavigate();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [errorMsg, setErrorMsg] = useState("");
const [state, setState] = useContext(UserContext);
const handleSubmit = async (e: { preventDefault: () => void }) => {
e.preventDefault();
let response;
const { data: loginData } = await axios.post("http://localhost:5001/auth/login", {
email,
password,
});
response = loginData;
if (response.errors.length) {
return setErrorMsg(response.errors[0].msg);
}
setState({
data: {
id: response.data.user.id,
email: response.data.user.email,
stripeCustomerId: response.data.user.stripeCustomerId,
},
loading: false,
error: null,
});
localStorage.setItem("token", response.data.token);
axios.defaults.headers.common["authorization"] = `Bearer ${response.data.token}`;
navigate("/dashboard");
};
return (
<div className="login-card">
<div>
<h3>Login</h3>
</div>
<form onSubmit={handleSubmit}>
<div className="login-card-mb">
<label>Email</label>
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
</div>
<div className="login-card-mb">
<label>Password</label>
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
</div>
{errorMsg && <p>{errorMsg}</p>}
<button type="submit">Submit</button>
</form>
</div>
);
};
After setting up your project to use React Query ( Check the docs if you have not). You want to extract your api call to a separate function that takes an object. This object will hold the values you would like to post.
const Login = (dataToPost) => {
let res = await axios.post('url', dataToPost)
return res.data
}
Now that you have that, you can import useMutation from React Query. Once imported you can now use the hook. UseQuery, useMutation both contain a data variable so no need to create state for the data returned from your endpoint. In this example, I'm deconstructing the data and loading state. But most importantly the mutate function. Which allows you to fire off your api call. We add our api call to the hook. I'm renaming the mutate function to doLogin. It's a habit
const {data,isLoading,mutate:doLogin} = useMutation(Login)
Finally we can just call mutate(objectWithValues) wherever you want in your code. The data will initially be null and isLoading will be true once called. To tie it all together. Your handleSubmit could look as follows
const handleSubmit = () => {
e.preventDefault();
doLogin({email,password})
}
You also have the option of running functions on a success or error of the mutation
const {data,isLoading,mutate: doLogin} =
useMutation(Login, {
onError: (err) => console.log("The error",err),
onSuccess:(someStuff)=>console.log("The data being returned",someStuff)
})
How do I add a click handler to refetch data from my API based on my input ON CLICK?
In my console I'm getting back data if I input "Jon Snow" for instance because the onChange set to e.target.value but not sure how to fetch this on button click.
Code Sandbox: https://codesandbox.io/s/pedantic-lichterman-4ev6f?file=/src/game.jsx
import React, { useEffect, useState } from "react";
import axios from "axios";
export default function Game() {
const [error, setError] = useState(null);
const [name, setName] = useState("");
const handleSubmit = e => {
e.preventDefault();
console.log( name );
}
const handleClick = e => {
// ??
}
useEffect(() => {
fetch(`https://anapioficeandfire.com/api/characters?name=${name}`)
.then((res) => res.json())
.then((data) => {
console.log(data[0].name); // the data I want back
})
.catch((error) => {
console.log("Error", error);
setError(error);
});
}, [name]);
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
/>
<input type="submit" value="Submit" onClick={handleClick}/>
</form>
);
}
When the Submit button is clicked it will trigger onSubmit event, no need for you to handle the onClick event separately.
import React, { useEffect, useState } from "react";
import axios from "axios";
export default function Game() {
const [error, setError] = useState(null);
const [name, setName] = useState("");
const handleSubmit = e => {
e.preventDefault();
console.log( name );
fetchData(name);
}
const fetchData = (name) => {
fetch(`https://anapioficeandfire.com/api/characters?name=${name}`)
.then((res) => res.json())
.then((data) => {
console.log(data[0].name); // the data I want back
})
.catch((error) => {
console.log("Error", error);
setError(error);
});
}
useEffect(() => {
fetchData(name);
}, []);
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
/>
<input type="submit" value="Submit" onClick={handleClick}/>
</form>
);
}
Add another stateful variable. You need not only a value and setter for the input value but also a value and setter for the API results you want to be able to use elsewhere. Maybe something like
const [searchText, setSearchText] = useState('');
const [result, setResult] = useState('');
// inside fetch callback:
setResult(data[0]?.name ?? ''); // use optional chaining to not throw an error
// if there is no result
<input
type="text"
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
placeholder="Name"
/>
And then you can use the result where you need.
Live demo:
const App = () => {
const [error, setError] = React.useState(null);
const [searchText, setSearchText] = React.useState('');
const [result, setResult] = React.useState('');
const handleSubmit = e => {
e.preventDefault();
console.log( name );
}
React.useEffect(() => {
fetch(`https://anapioficeandfire.com/api/characters?name=${searchText}`)
.then((res) => res.json())
.then((data) => {
setResult(data[0] ? data[0].name : '');
})
.catch((error) => {
console.log("Error", error);
setError(error);
});
}, [searchText]);
console.log(result);
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
placeholder="Name"
/>
<input type="submit" value="Submit" onClick={e => e.preventDefault()}/>
</form>
);
}
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>
You can use direct state variable [name] in handleClick function.
The other answers are all correct that you should trigger the fetch in your handleSubmit. I just wanted to chime in with some sample code for rendering results since you asked for help with that.
The API returns an array of characters. We want to map through that result and show each character. We also want to tell the user if there were no results (especially since this API seems to only work with an exact name and will return a result for "Arya Stark" but not for "Stark"). We don't want to show that "No Characters Found" message before they have submitted.
I am using a setState hook to store the array of character matches from the API. I am initializing the state to undefined instead of [] so that we only show the no results message if it gets set to [].
My code allows the user to submit multiple times. We keep displaying the previous results until they submit a new search. Once we have an array in our characters state, we display those results.
// an example component to render a result
const RenderCharacter = ({ name, aliases }) => {
return (
<div>
<h2>{name}</h2>
{aliases.length && (
<div>
<h3>Aliases</h3>
<ul>
{aliases.map((a) => (
<li key={a}>{a}</li>
))}
</ul>
</div>
)}
</div>
);
};
export default function Game() {
// current form input
const [name, setName] = useState("");
// save characters returned from the API
// start with undefined instead of empty array
// so we know when to show "no characters found" message
const [characters, setCharacters] = useState();
// store API errors
const [error, setError] = useState(null);
const fetchData = () => {
fetch(`https://anapioficeandfire.com/api/characters?name=${name}`)
.then((res) => res.json())
.then(setCharacters) // store data to state
.then(() => setError(null)) // clear previous errors
.catch((error) => {
console.log("Error", error);
setError(error);
setCharacters(undefined); // clear previous character matches
});
};
const handleSubmit = (e) => {
e.preventDefault();
fetchData();
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
/>
<input type="submit" value="Submit" />
</form>
{characters !== undefined &&
(characters.length === 0 ? (
<div>No Characters Found</div>
) : (
<div>
{characters.map((character) => (
<RenderCharacter key={character.name} {...character} />
))}
</div>
))}
{error !== null && <div>Error: {error.message}</div>}
</div>
);
}
Code Sandbox Demo (with typescript annotations)
user.js
import React, { useState, useEffect } from 'react';
const Users = () => {
const [user, setUser] = useState({
name: '',
email: '',
phoneNumber: '',
role: '',
status: ''
}); ======>>>> I want to sent above fields only however while fetching user it fetchs extra fields that I don't want to send while handle submit
const { name, email, phoneNumber, role, status } = user;
const handleChange = e => {
setUser({ ...user, [e.target.name]: e.target.value });
};
const loadUser = async () => {
const res = await Axios.get(`http://localhost:3001/user/fetch/${id}`)
setUser(res.data.response.info)
};
useEffect(() => {
loadUser();
}, []);
const handleSubmit = async e => {
e.preventDefault();
await Axios.patch(`http://localhost:3001/user/${id}`, user)
}; ====>>> I want to sent limited user field here
return (
<form onSubmit={e => handleSubmit(e)} >
<div className="form-group">
<label htmlFor='name'>Name:</label>
<input type="text" name="name" id="name" className="form-control" value={name || ''} onChange={e => handleChange(e)} />
</div>
)
}
I am updating user it is working fine but what i get error it is fetching extra field in user state how can i handle limited fields to sent in handle submit while submitting form like I don't want extra fields to sent to backend. I want only fields which is in state
Why don't you set the fields what you want to set and ignore the rest.
const loadUser = async () => {
const res = await Axios.get(`http://localhost:3001/user/fetch/${id}`);
const _user = res.data.response.info;
setUser({
email: _user.email,
//// rest,
})
};
Or else just allowKeys which you want before patching
function allowKeys(obj, keysAllowed) {
const newObj = {}
for (const key in obj) {
if (keysAllowed.includes[key]) {
newObj[key] = obj[key]
}
}
return newObj
}
// at the time of patching
const toBePatchedUser = allowKeys(user, ["email"... /** only allowed keys here */])
I need to dynamically add new input fields on button click as well as get the user input of those inputs in an array. This is what I have and Im not sure how to do the array. Also on the screen the components only update when I change the state. Not on button click.
This is what I have:
import React, { useState } from 'react'
const CreatePoll = () => {
const [formData, setFormData] = useState({
question: '',
options: ['hi', 'there']
});
const {
question,
options
} = formData;
const addOption = e => {
e.preventDefault();
options.push([''])
console.log(options.length);
}
const handleQuestionChange = (e) => setFormData({
...formData,
[e.target.name]: e.target.value
})
const handleOptionChange = e => setFormData({
...formData
// options: []
})
const handleSubmit = async e => {
e.preventDefault();
console.log(formData)
}
return (
<form onSubmit={handleSubmit}>
<input
placeholder="enter your question"
type="text"
onChange={handleQuestionChange}
name="question" />
{options.map(() => {
return (
<input
placeholder="option"
type="text"
onChange={handleOptionChange}
name="option" />
)
})}
<input type="button" value="Add new option" onClick={addOption} />
<input type="submit" value="Submit" />
</form>
)
}
export default CreatePoll
I tried when addOption button is clicked, I add to the options state an empty string. The length of the array updates but the components on the screen dont until I type in the input box and the state changes. Also I need to map the values of the input boxes to their respective place in the array. They should also be able to edit at any time. How is this done?
Several things are wrong here :
You don't set your state in addOption, don't modify direcly the state object, prefere to destructure array, modify it and set the state.
Your map function don't take any parameter, so it will be the same input names every time, use parameter and index to now which option to change in handleQuestionChange
Your addOption could be improved by using question property directly in you setFormData (it worked like you did it, but it seems to me more clean with this)
import React, { useState } from 'react';
const CreatePoll = () => {
const [formData, setFormData] = useState({
question: '',
options: ['hi', 'there'],
});
const {
question,
options,
} = formData;
const addOption = e => {
e.preventDefault();
const newOptions = [...options];
newOptions.push('');
setFormData({ ...formData, options: newOptions });
console.log(options.length);
};
const handleQuestionChange = e => {
setFormData({
...formData,
question: e.target.value,
});
};
const handleOptionChange = (e, index) => {
const newOptions = [...options];
newOptions[index] = e.target.value;
setFormData({
...formData,
options: newOptions,
});
};
const handleSubmit = async e => {
e.preventDefault();
console.log(formData);
};
return (
<form onSubmit={handleSubmit}>
<input
placeholder="enter your question"
type="text"
onChange={handleQuestionChange}
name="question"
/>
{options.map((opt, index) => (
<input
value={opt}
key={`option_${index}`}
placeholder="option"
type="text"
onChange={e => handleOptionChange(e, index)}
name={opt}
/>
))}
<input type="button" value="Add new option" onClick={addOption} />
<input type="submit" value="Submit" />
</form>
);
};
export default CreatePoll;
to add new options on button click you need to change this function:
const addOption = e => {
e.preventDefault();
options.push([''])
console.log(options.length);
}
to be
const addOption = e => {
e.preventDefault();
const newOptions = {...formData.options}
newOptions.concat([''])
setFormData({...formatData, options: newOptions)}
}
I must post {input} data to http://localhost:4000/prediction with Axios. But {input} turns undefined.
I am using const instead of class Main extends component. onChange, it sets form data.
const Main = ({ value, suggestions, auth: { user } }) => {
const [formData, setFormData] = useState("");
const [messages, setMessages] = useState([]);
const { input } = formData;
const onChange = e => setFormData(e.target.value);
const onSubmit = event => {
event.preventDefault();
setMessages(prevMsgs => [...prevMsgs, formData]);
console.log({ input });
Axios post.
axios
.post(
`http://localhost:4000/prediction`,
{ input },
{ crossdomain: true }
)
.then(res => {
console.log(res.data);
//setMessages(prevMsgs => [...prevMsgs, formData]);
})
.catch(error => {
console.log(error.message);
});
};
Return (form) with onSubmit, onChange.
return (
<div className="true">
<br />
<form noValidate onSubmit={e => onSubmit(e)}>
<div className="input-group mb-3">
<input
name="input"
type="text"
className="form-control"
placeholder="Type text"
onChange={e => onChange(e)}
/>
)}
<div className="input-group-append">
<button className="btn btn-outline-secondary">Send</button>
</div>
</div>
</form>
</div>
);
};
As I have mentioned in the comment section formData is a string as I see which does not have a property called input what you try to destructure and that's why it is undefined always.
If you really need that format for axios then you can try change the structure of formData with useState as the following first:
const [formData, setFormData] = useState({input: null});
Then maybe you can try updating as:
const onChange = e => setFormData({input: e.target.value});
I hope that helps!