Goal:
Retrieve data 'smartphones', by API, and apply it as a default selection in the input radio component for the react hook form.
Problem:
The code do not work that makes smartphones as a preselection in the input radio button.
However, if I make a change by using hard coding, it works but hard coding do not solve the case.
I don't know how to solve it.
Info:
*Using React TS and React hook form.
*Newbie in react TS and hook form.
Stackblitz:
https://stackblitz.com/edit/react-ts-z9cnzl
Thank you!
import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
import './style.css';
type FormValues = {
lastName: string;
favShow: string;
};
export default function App() {
const [category, setCategory] = useState('');
React.useEffect(() => {
async function FetchData() {
var data = await fetch('https://dummyjson.com/products/1').then((res) => {
return res.json();
});
console.log(data.category);
setCategory(data.category);
}
FetchData();
}, []);
const [data, setData] = useState(null);
const { register, handleSubmit } = useForm<FormValues>({
defaultValues: {
lastName: 'asaaaaaaf',
favShow: category,
//favShow: 'smartphones',
},
});
const onSubmit = (data) => {
setData(data);
console.log('asdf');
};
return (
<React.Fragment>
<form onSubmit={handleSubmit(onSubmit)} className="form-container">
<h1 className="form-heading">React Hook Form Example</h1>
<input
{...register('lastName', { required: true })}
className="form-control"
type="text"
placeholder="Last name"
maxLength={15}
name="lastName"
/>
<br />
<br />
<label htmlFor="ted-lasso">
<input
{...register('favShow', { required: true })}
type="radio"
name="favShow"
value="smartphones"
id="smartphones"
/>{' '}
smartphones
</label>
<label htmlFor="got">
<input
{...register('favShow', { required: true })}
type="radio"
name="favShow"
value="GOT"
id="got"
/>
GOT
</label>
<br />
<br />
<button className="submit-btn" type="submit">
Submit
</button>
</form>
{data && <p className="submit-result">{JSON.stringify(data)}</p>}
</React.Fragment>
);
}
I got some help and the solution is:
Stackblitz:
https://stackblitz.com/edit/react-ts-m2s6ev?file=index.tsx
import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
import './style.css';
type FormValues = {
lastName: string;
favShow: string;
};
export default function App() {
const [category2, setCategory2] = useState();
const [data, setData] = useState(null);
const { register, handleSubmit, resetField } = useForm<FormValues>({
defaultValues: {
lastName: '',
favShow: '',
},
});
React.useEffect(() => {
async function FetchData() {
var data = await fetch('https://dummyjson.com/products/1').then((res) => {
return res.json();
});
setCategory2(data);
}
FetchData();
}, []);
React.useEffect(() => {
if (category2) {
const obj = JSON.parse(JSON.stringify(category2));
console.log(obj);
resetField('lastName', { defaultValue: obj.discountPercentage });
resetField('favShow', { defaultValue: obj.category });
}
}, [resetField, category2]);
const onSubmit = (data) => {
setData(data);
};
return (
<React.Fragment>
<form onSubmit={handleSubmit(onSubmit)} className="form-container">
<h1 className="form-heading">React Hook Form Example</h1>
<input
{...register('lastName', { required: true })}
className="form-control"
type="text"
placeholder="Last name"
maxLength={15}
name="lastName"
/>
<br />
<br />
<label htmlFor="ted-lasso">
<input
{...register('favShow', { required: true })}
type="radio"
name="favShow"
value="smartphones"
id="smartphones"
/>{' '}
smartphones
</label>
<label htmlFor="got">
<input
{...register('favShow', { required: true })}
type="radio"
name="favShow"
value="GOT"
id="got"
/>
GOT
</label>
<br />
<br />
<button className="submit-btn" type="submit">
Submit
</button>
</form>
{data && <p className="submit-result">{JSON.stringify(data)}</p>}
</React.Fragment>
);
}
Related
I have a form that has an array input and an image, when sending the request without the image it's getting stored correctly in the database, when the image input and the "Content-Type": "multipart/form-data" header both added, it send the request with the correct data but it outputs that the array data is invalid.
the code is written in Vue 3 and the backend is a laravel api.
<script>
import axios from "axios";
export default {
name: "AddPayloadModel",
data() {
return {
drone_id: [],
drone_models: [],
image: null,
previewImage: null,
};
},
created() {
this.getDroneModels();
},
methods: {
getDroneModels() {
axios.get("http://127.0.0.1:8000/api/drone-models").then((response) => {
this.drone_models = response.data;
});
},
imgUpload(e) {
this.image = e.target.files[0];
const reader = new FileReader();
reader.readAsDataURL(this.image);
reader.onload = (e) => {
this.previewImage = e.target.result;
};
},
submit(e) {
const form = new FormData(e.target);
const inputs = Object.fromEntries(form.entries());
inputs.drone_id = Object.values(this.drone_id);
console.log(inputs.drone_id);
axios
.post("http://127.0.0.1:8000/api/payload-model/add", inputs, {
headers: {
"Content-Type": "multipart/form-data",
},
})
.then((res) => {
if (res.data.isUnattachableDrones) {
console.log(res);
alert(res.data.unattachableDrones);
} else {
this.$router.push("/home");
}
})
.catch((e) => {
console.error(e);
});
},
},
};
</script>
<template>
<main class="form-signin">
<form #submit.prevent="submit">
<h1 class="h3 mb-3 fw-normal">Add Payload Model</h1>
<div class="form-floating">
<input
class="form-control"
autocomplete="off"
name="brand_name"
placeholder="Brand Name"
/>
<label>Brand Name</label>
</div>
<div class="form-floating">
<input
class="form-control"
autocomplete="off"
name="model_name"
placeholder="Model name"
/>
<label>Model Name</label>
</div>
<div class="form-floating">
<input
class="form-control"
autocomplete="off"
name="type"
placeholder="Type"
/>
<label>Type</label>
</div>
<select
v-model="drone_id"
multiple="multiple"
name="drone_id"
style="height: auto"
class="form-select last-input"
>
<template v-for="drone in drone_models">
<option :value="drone.id">
{{ drone.brand_name }}
</option>
</template>
</select>
<input
name="image"
ref="fileInput"
accept="image/*"
type="file"
#input="imgUpload"
/>
<button class="w-100 btn btn-lg btn-form" type="submit">Submit</button>
</form>
</main>
</template>
here is the response when submitting the form.
and here is the payload.
It got solved by changing the way to push data to the post request to a formData and append it manually like this.
submit() {
const formData = new FormData();
formData.append(
"brand_name",
document.getElementById("brand_name").value
);
formData.append(
"model_name",
document.getElementById("model_name").value
);
formData.append("type", document.getElementById("type").value);
formData.append("image", document.getElementById("image").files[0]);
this.drone_id.forEach((drone_id) => {
formData.append("drone_id[]", drone_id);
});
const headers = { "Content-Type": "multipart/form-data" };
axios
.post("http://127.0.0.1:8000/api/payload-model/add", formData, headers)
.then((res) => {
console.log(res);
if (res.data.isUnattachableDrones) {
console.log(res);
alert(res.data.unattachableDrones);
} else {
this.$router.push("/home");
}
})
.catch((e) => {
console.error(e);
});
},
to update the complete object properties by using useState
pass textbox target value onchange event and change object properties using hooks
function ExampleObjectHooks() {
const [name, setname] = useState({ first: '', last: '' });
const onInputNameChange = (e) => {
setname({ first: e.target.value, last: e.target.value });
console.log(name);
}
return (
<div>
<form>
<input type="text" onChange={(e) => onInputNameChange(e)} ></input>
<input type="text" onChange={e => onInputNameChange(e)} ></input>
</form>
{name.first}{name.last}
</div>
);
}
Add an name attribute to each input field, and update the state using the name (key in the code). Node that you need to merge the state of each field by yourself. To so spread the previous state, and override the field you've just updated.
const { useState } = React;
function ExampleObjectHooks() {
const [name, setname] = useState({ first: '', last: '' });
const onInputNameChange = ({ target }) => {
const { name: key, value } = target;
setname(state => ({
...state,
[key]: value
}));
};
return (
<div>
<form>
<input name="first" type="text" onChange={onInputNameChange} ></input>
<input name="last" type="text" onChange={onInputNameChange} ></input>
</form>
{name.first} {name.last}
</div>
);
}
ReactDOM.render(
<ExampleObjectHooks />,
root
);
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>
Here I am getting an error of handleSubmit function because ,here I am using this syntax 'function PostForm () ' instead of 'class PostForm extends Component {}',so what needs to be changed in order to work with handlesubmit function.
function PostForm () {
handleSubmit = (e) => {
e.preventDefault();
const title = this.getTitle.value;
const message = this.getMessage.value;
const data = {
id: new Date(),
title,
message
}
console.log(data)
}
return (
<div>
<h1>Create Post</h1>
<form onSubmit={this.handleSubmit}>
<input required type="text" ref={(input)=>this.getTitle = input}
placeholder="Enter Post Title"/>
<br /><br />
<textarea required rows="5" ref={(input)=>this.getMessage = input} cols="28"
placeholder="Enter Post" />
<br /><br />
<button>Post</button>
</form>
</div>
);
}
export default PostForm;
Since it's the function component you need to define handleSubmit as a function.
For example
const handleSubmit = (e) => {
...
}
I am trying to populate a select menu in redux forms dynamically.
I've been using the debugging tools in chrome and can see that the 'departments' variable sees the array list
({departments.map(department => <option key={department} value={department}>{department}</option>)}
but the final choice list isn't populating. I'm guessing it has something to do with the renderSelectField function, but I'm not sure what I am overlooking?
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import isValidEmail from 'sane-email-validation';
class SimpleReduxForm extends Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
this.renderSelectField = this.renderSelectField.bind(this);
}
onSubmit = async (data) => {
try {
let response = await fetch('/api/getRecords', {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-type': 'application/json'
}
});
let responseJson = await response.json();
//display success message to user
alert('Form successfully submitted')
return responseJson;
//reset form
} catch (error) {
alert(error);
}
}
renderInputField(field) {
return (
<div className="form-group">
<label htmlFor={field.input.name}>{field.label}</label>
<div className="field">
<input placeholder={field.label} {...field.input} className="form-control" type={field.input.type} />
</div>
</div>
)
}
renderSelectField(field) {
return (
<div className="form-group">
<label htmlFor={field.input.name}>{field.label}</label>
<div className="field">
<select {...field.input}
className="form-control"
defaultselection={field.defaultSelection}
><option>{field.defaultselection}</option></select>
</div>
</div>
)
}
render() {
const { handleSubmit, pristine, reset, submitting, invalid } = this.props;
//Options for select - this should be an AJAX call to a table to get options list
const departments = ["Dept 1", "Dept 2", "Dept 3"]
return (
<form onSubmit={handleSubmit(this.onSubmit)}>
<Field
label="Username"
name="username"
component={this.renderInputField}
type="text"
/>
<Field
label="Email"
name="email"
component={this.renderInputField}
type="email"
/>
<Field
label="Age"
name="num_field"
component={this.renderInputField}
type="text"
/>
<Field
label="Department"
name="department"
defaultselection="Select..."
component={this.renderSelectField}>
{departments.map(department => <option key={department} value={department}>{department}</option>)}
</Field>
<div>
<button type="submit" className="btn btn-primary" disabled={pristine || submitting}>Submit</button>
<button type="button" className="btn btn-warning" disabled={pristine || submitting} onClick={reset}> Clear Values </button>
</div>
</form >
)
}
}
//Validate Errors Before Submission
const validate = (values) => {
//create errors object
const errors = {}
/*Example showing to check is a field contains data
* if no, submission == invalid*/
if (!values.username) {
errors.username = 'Required!'
}
/*check to see if email is provided and that submission is an actual email address*/
if (!values.email) {
errors.email = 'Required!'
} else if (!isValidEmail(values.email)) {
errors.email = 'Invalid Email!'
}
/* Example to show that the length of a field
* can be checked as part of validation */
if (values.num_field < 2) {
errors.num_field = "Must be at least 10 years old"
}
return errors
}
const mapStateToProps = state => ({
SimpleReduxForm: state.form.SimpleReduxForm
});
export default reduxForm({
validate,
form: 'SimpleReduxForm',
enableReinitialize: true,
keepDirtyOnReinitialize: true,
})(connect(mapStateToProps)(SimpleReduxForm));
I figured it out. Just in case anyone else runs into this issue. I needed to add {field.children} into the renderSelectField function. So the final function looks like:
renderSelectField(field) {
return (
<div className="form-group">
<label htmlFor={field.input.name}>{field.label}</label>
<select {...field.input}
className="form-control"
defaultselection={field.defaultSelection}
><option>{field.defaultselection}</option>{field.children}</select>
</div>
)
}
I have this form, and I would like to send these values. I know we have to use setState() to store data but how does it work for input type="hidden"?
First question: How to store input hidden to setState ?
Second question: How to serialize data like form.serialize() ?
Third question: How to send these serialize values? Ajax or Axios, who is the better?
Here is the code:
handleSubmit(e) {
e.preventDefault();
/**
$.ajax({
url: "post.php",
type: "POST",
data: DATA,
success:function(data) {
}
});
**/
}
<form onSubmit={this.handleSubmit}>
<input type="hidden" name="action" value="login" />
<input type="email" name="email_user" placeholder="Email" />
<input type="password" name="password_user" placeholder="Mot de passe" />
<button type="submit">Login</button>
</form>
The answer is complex for all your questions.
First of all, it depends on the task: if you just want to send asynchonous request to server on form submit, you don't need to use Component state. Here is a link to the relevant section of the documentation. And use refs to access inputs data.
class FormComponent extends React.Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit(e) {
e.preventDefault();
// Send your ajax query via jQuery or Axios (I prefer Axios)
axios.get('your_url', {
params: {
action: this.actionInput.value,
email: this.emailInput.value,
password: this.passwordInput.value,
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
render() {
return (
<form onSubmit={this.onSubmit}>
<input type="hidden" name="action" value="login"
ref={(input) => { this.actionInput = input }} />
<input type="email" name="email_user" placeholder="Email"
ref={(input) => { this.emailInput = input }}/>
<input type="password" name="password_user" placeholder="Mot de passe"
ref={(input) => { this.passwordInput = input }}/>
<button type="submit">Login</button>
</form>
);
}
}
All data can be stored on React's state, but if you still need to have inputs on your form you can do something like this:
const handleSubmit = e => {
e.preventDefault();
const inputs = Object.values(e.target)
.filter(c => typeof c.tagName === 'string' && c.tagName.toLowerCase() === 'input')
.reduce((acc, curr) => ({ ...acc, [curr.name]: curr.value }), {});
setFormVals({ ...formVals, ...inputs });
}
See the demo below:
const Demo = () => {
const [formValues] = React.useState({});
const handleSubmit = e => {
e.preventDefault();
const inputs = Object.values(e.target)
.filter(c => typeof c.tagName === 'string' && c.tagName.toLowerCase() === 'input')
.reduce((acc, curr) => ({ ...acc, [curr.name]: curr.value }), {});
console.log(inputs);
}
return (
<form onSubmit={handleSubmit}>
<input name="name" placeholder="Name" value={formValues.name} />
<input name="email" placeholder="Email" value={formValues.email} />
<input name="hiddenInput" value="hiddenValue" type="hidden" />
<button type="submit">Submit</button>
</form>
);
}
ReactDOM.render(<Demo />, document.getElementById('demo'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
<div id="demo"></div>
If you know what the inputs that you need you can do something like this:
const Demo = () => {
const formRef = React.useRef(null);
const [formValues, setFormValues] = React.useState({});
const handleChange = e => {
setFormValues({
...formValues,
[e.target.name]: e.target.value,
});
}
const handleSubmit = e => {
e.preventDefault();
setFormValues({ ...formValues, hiddenInput: formRef.current.hiddenInput.value });
}
return (
<form onSubmit={handleSubmit} ref={formRef}>
<input name="name" placeholder="Name" value={formValues.name} onChange={handleChange} />
<input name="email" placeholder="Email" value={formValues.email} onChange={handleChange} />
<input name="hiddenInput" value="hiddenValue" type="hidden" />
<button type="submit">Submit</button>
<pre>{JSON.stringify(formValues, null, 2)}</pre>
</form>
);
}
ReactDOM.render(<Demo />, document.getElementById('demo'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
<div id="demo"></div>
Answering your questions:
Since you know how to use component's state you may set the value as : <input type='text' value={this.state.foo} /> or even via props passing <input type='hidden' value={this.props.foo} />
You don't need to serialise anything at all. Use your component's local state or even a state container like Redux or Flux in order to pick the appropriate data. Take a look at this fairly simple example:
var SuperForm = React.createClass({
getInitialState() {
return {
name: 'foo',
email: 'baz#example.com'
};
},
submit(e){
e.preventDefault();
console.log("send via AJAX", this.state)
},
change(e,key){
const newState = {};
newState[key] = e.currentTarget.value;
this.setState(newState)
},
render: function() {
return (
<div>
<label>Name</label>
<input
onChange={(e) => this.change(e,'name')}
type="text"
value={this.state.name} />
<label>Email</label>
<input
onChange={(e) => this.change(e,'email')}
type="text"
value={this.state.email} />
<button onClick={this.submit}>Submit</button>
</div>
);
}});
Demo
AJAX is a set of web development techniques while Axios is a JavaScript framework. You may use jQuery, Axios or even vanilla JavaScript.