Adding functionality to a form in a React functional component - reactjs

I have a form component that when submitted uploads a photo to my server using multer and then to an S3 bucket on AWS in a single upload route. I want to add an extra line of functionality that stores the photo key from S3 in state on the front end which I will then store with the user object in the database so the photo is always associated with the user (profile picture).
The form sends properly as currently set up using the action parameter, but I'm not sure how to implement the submitHandler function into the form properly. If I remove the action parameter from the form it breaks, if I use onSubmit it also seems to break. I'm sure this is a really easy one but I can't figure it out. Any help much appreciated.
import React from 'react';
import axios from 'axios';
//setProfilePhoto is the set method for state from props
const UploadForm = ({setProfilePhoto}) => {
const submitHandler = async (e) => {
e.preventDefault();
const result = await axios.post('http://localhost:8000/api/upload');
console.log(result.key)
setProfilePhoto(result.key)
}
return(
<form action="http://localhost:8000/api/upload" encType='multipart/form-data' method='POST'>
<input type='file' name='photo' />
<input type='submit' value="submit" />
</form>
)
}
export default UploadForm;

try to take this line and delete it. <input type='submit' value="submit" />
Replace it with a button.
Then add the onSubmit handler to the form tag, not the button.

Related

How to access state from components to the parent component

I have a form in which all input, select tags are separate components and each component but the submit button is in the form it self like-
<form>
<InputComp1 />
<InputComp2 />
<Select1 />
<Select2 />
<button type='submit' value='Register'/>
</form>
So how do I collect all state from various components and when user clicks on the submit the values get submitted.?
Is this approach while dealing with forms right? or should I manage state of all tags in the same component?
Manage the state of all inputs/selects in this component. You can pass values and handler functions to the inputs using props.
There is no "right" approach, the answer depends on the context.
You can have form as a controlled component, where you manage the state of all tags (while passing callbacks down the tree) as you suggested and as mentioned in docs.
Or, you can have it as uncontrolled component, for example:
const Input = ({ name }) => {
return <input name={name} />;
};
const Component = () => {
return (
<>
<form
onSubmit={(e) => {
e.preventDefault();
const data = new FormData(e.target);
const entries = data.entries();
for (let entry of entries) {
console.log(entry);
}
}}
>
<Input name="username" />
<Input name="email" />
<input type="submit" value="Submit" />
</form>
</>
);
};
See controlled vs uncontrolled components.
Yes you should manage the state in the parent component itself and pass the onchange handler and value of that field as props inside the child components to update the value of the form fields.
Solution #1 would be "manage state react way". With this you should store state you need to share between components somewhere in their common ancestor. In your case it would be component that holds Form
Solution #2 applicable only if you use real form and form controls. Handle 'submit' event from the form and get all you need to submit from form data.
Solution #3 applicable only if you use some sort of "state manager". Follow instructions and best practices of the library you use.
In some cases you can mix and match that solutions. For example I still recommend to handle 'submit' event on form regardless of solution.
there is a concept of state lifting in react:
create a controlled form here and for every child, component pass a function to get the data from child components to parent one. by doing this you can submit all the values once.
here is the example
import React, {useState} from 'react';
const ChildInput = ({onChange, id}) => {
return(
<input
key={id}
type="text"
placeholder="enter name"
onChange={onChange}
/>
)
}
const Parent = () => {
const [name, setName] = useState('');
const onSubmit =(e)=>{
e.preventDefault();
// append your all data here just like child component
data = {name}
}
return(
<form onSubmit={onSubbmit}>
<ChildInput onChange={()=>setName(e.target.value)} id="name" />
<button type="submit" value="submit"/>
</form>
)}
for more information check this one: https://reactjs.org/docs/glossary.html#controlled-vs-uncontrolled-components

form submit re-renders page in react

I have a form in a react component.
When I click the submit button, I call signin(). In signin(), an error occurs as I can see the output in the chrome console, but it flashes by so quickly, I can't read it. Then the page refreshes and the error message is gone from the console.
Why is my form re-rendering the page? And how can I suppress the re-rendering so that I can read the error?
Here is my component:
import React, { Component } from 'react';
import './login.scss';
import axios from '../axios/axiosInstance';
class Login extends Component {
constructor() {
super();
this.usernameRef = React.createRef();
this.passwordRef = React.createRef();
}
signin() {
axios.get('/auth', {headers: {
username: this.usernameRef.current.value,
password: this.passwordRef.current.value}}).then(response => {
console.log('response=', response);
}).catch(err => {
console.log('err=', err);
});
}
render() {
return (
<div className="login-container">
<form onSubmit={this.signin}>
<div className="flex-row-end">
<div className="form-element flex-column-end">
<input type="text"
placeholder="Username or email"
name="username"
ref={this.usernameRef}
required />
</div>
<div className="form-element flex-column-end">
<input type="password"
placeholder="Password"
name="password"
ref={this.passwordRef}
required />
</div>
<div className="login-submit">
<button className="submit-login-button" type="submit"><i className="fas fa-sign-in-alt"> </i>Sign In</button>
</div>
</div>
</form>
</div>
);
}
};
export default Login;
As you can see, in signin(), I am using axios to send a request to my backend with user credentials. On the backend, the logs show no record of receiving the request. Therefore, the error must be happening before the request is sent. But I need a way to suppress the re-rendering of the page so that I can see what the error message is.
Thank you very much.
Change the signature of signin to take the form submission event, you'll need to prevent the default action on it. This keeps the page from reloading.
signin(e) {
e.preventDefault();
...
The error tells you that reference from component is not define yet.
Because method is not bound to this when used as a function:
<form onSubmit={e => this.signin(e)}>
and then put e.preventDefault() inside signin(e) which prevent the blink after you submit the form.
If I'm not mistaken axios will make the http request asynchronous, so you might have to use the event.persist() if this is not the case, preventDefault() should work as they mentioned in the answers above, also it is recommended to call the api in the componentDidMount()

passing down functions correctly for onSubmit and handleChange on a form

I am trying to handle navigation around my application. I want to include a link back to the form component from displayweather using conditional statements. I can get the form to show correctly, however when you press submit on the form it just refreshes back to the Form under App
I am trying to pass the functions down from App to Navbar which holds a second instance of the Form
However, using react dev tools the functions are still showing as undefined?
Here is a gist of the code:
https://gist.github.com/dhuang612/6c683b6ccd0ce6299631db76ed76ccd7
The two functions I am trying to hand down from App
handleChange = e => {
this.setState({
[e.target.name]: e.target.value
});
};
//handles submit on the form and runs the api call
onSubmit = e => {
e.preventDefault();
this.fetchWeatherData();
this.setState({ fetchedweatherdata: !this.state.fetchedweatherdata }, () =>
console.log(this.state.fetchedweatherdata)
);
};
this is how I am trying to pass the functions down to this second instance of form
<DisplayWeather
currentweather={this.state.currentweather}
currentforecast={this.state.currentforecast}
currenttime={this.state.currenttime}
weatherIcon={this.state.weatherIcon}
hourlyWeather={this.state.hourlyWeather}
/>
<Navbar fetchedweatherdata={this.state.fetchedweatherdata}>
<Form
onChange={this.handleChange}
{...this.state}
onSubmit={this.onSubmit}
/>
</Navbar>
and this is how I am trying to call the functions on the form
return (
<div>
{!this.state.fetchedweatherdata ? (
<div>
<button onClick={this.resetState}>return to form</button>
</div>
) : (
<Form onChange={handleChange} {...this.state} onSubmit=
{onSubmit} />
)}
</div>
I am not getting any error messages, the screen refreshes and reloads the origin Form under App, I would like the application to re-send out the api request and show current weather for the new location. Please see gist to see all the code together.
Thanks for any help!
The issue was that you did not pass nothing to Navbar itself.
I did put a codesandbox up for you, please, for the future question if there is more code included try to do the same with codesandbox, this can save lifes!
Here is the mentioned sandbox

Get value from textarea input in event object

I have a form with a textarea where users can put comments, and then trigger a onClick (when the form is submitet via the button).However, I cant get the value of the input, for example if a user writes "test", I want it to get into the handleSubmit function.
My form
<form onSubmit={this.handleSubmit.bind(this)} method="POST">
<label>Skicka</label>
<textarea placeholder="Type in comments (allergis etc.)" name ="name" ref ="name"></textarea>
<button className="btn" type="submit">
Send
</button>
</form>
//my handler
public handleSubmit = event => {
event.preventDefault();
console.log(event.name.value)
}
You have to save the textarea value separately in the onChange method of the textarea like this (for class component):
<form onSubmit={this.handleSubmit.bind(this)}
method="POST"
>
<label>Skicka</label>
<textarea
onChange={this.setComments}
placeholder="Type in comments (allergis etc.)"
name="name"
value={this.state.comment}/>
<button className="btn" type="submit">
Send
</button>
</form>
// The save function
const setComments = e => this.setState({comment: e.target.value});
This will save the textarea input in your local state and you can access it in your submit function with this.state.comment.
Hope this helps. Happy coding.
As you are using Uncontrolled Component. You can make use of ref to get value.
handleSubmit = (event) => {
event.preventDefault();
console.log(this.refs.name.value)
}
Demo
Note: In React you should never add method="POST" and action attribute's on form.
Don't add public keyword to your function (if you are not using typescript).
Better approach to work with form values, is Controlled Component
You can fix it by changing the handleSubmit method. Check below updated method.
public handleSubmit = event => {
event.preventDefault();
console.log(event.target.name.value)
}
But if you are work with React application then update the state variable via onChange event.

ReactJS: How to submit the form values as JSON object without using redux-form.

I am using redux-form to submit form values as JSON object to POST service. But redux-form fields(<Field />) doesn't support events like onChange , onBlur. So I want to use the default components like <select><option></select>, <input type="text" />, but if I use the plain html components, events like onChange will work. But here I am unable to submit the form values as object to service without redux-form. Please guide...
Thanks,
Shash
redux-form does support it with custom components passed to <Field />.
One of the way is this (taken from the docs)
A component
This can be any component class that you have written or have imported
from a third party library.
// MyCustomInput.js
import React, { Component } from 'react'
class MyCustomInput extends Component {
render() {
const { value, onChange } = this.props
return (
<div>
<span>The current value is {value}.</span>
<button type="button" onClick={() => onChange(value + 1)}>Inc</button>
<button type="button" onClick={() => onChange(value - 1)}>Dec</button>
</div>
)
}
}
Then, somewhere in your form...
import MyCustomInput from './MyCustomInput'
<Field component={MyCustomInput}/>
It would be much easier for you to just make redux-form submit your values, since you're already using it.
Docs on Field

Resources