I am using react-select for select dropdown. I got the selected option value in onchange. While submitting the data on the signup form, the selected option value is always empty. How to get the selected option value in form handle submit submit like name and password.
class addAtribute extends Component {
handleFormSubmit({ name, password,role }) {
this.props.signup({ name, password, role});
}
constructor(props){
super(props);
this.state={value:''};
// this.state={};
}
render(){
const { handleSubmit, fields: {name, password,role }} = this.props;
return(
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
<div className="login" id="login" >
<div className="col-md-12">
<input placeholder="Password" id="pwd" className="login-password form-control" type="password" {...password} />
<label for="dropdown-custom-1" >Select the Role</label>
<Select value={this.state.value} options={options}
onChange={event => this.setState({value:event.value})} {...role} /> </div>
<button action="submit " className="btn btn-block"><div action="submit" className="login-submit ">Update</div></button>
);}}
var options = [
{ value: '1', label: 'Admin'},
{ value: '2', label: 'Manager' },
{ value: '3', label: 'Editor' }];
function mapStateToProps(state){
return {auth: state.auth}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({ signup }, dispatch);
}
export default reduxForm({
form: 'signup',
fields: ['name', 'password','role'},mapStateToProps,mapDispatchToProps)(addAtribute);
You can simply write
<form onSubmit={this.handleFormSubmit.bind(this)}>
Related
My store.js file
import commentsReducer from "./stateSlices/commentsSlice";
export default configureStore({
reducer: {
dishes: dishesReducer,
comments: commentsReducer,
leaders: leadersReducer,
promotions: promotionsReducer,
},
});
My commentsSlice.js file
import { COMMENTS } from "../../shared/comments";
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
comments: COMMENTS,
};
export const stateSlice = createSlice({
name: "comments",
initialState,
// Updated the reducer file as below
reducers: {
addComment: {
reducer: (state = initialState.comments, action) => {
state.comments = state.comments.concat(action.payload);
},
prepare: (value) => {
return {
payload: {
...value,
date: new Date().toISOString(),
id: Math.random(),
},
};
},
},
},
});
export default stateSlice.reducer;
My dishdetail.js file
const Dishdetail = (props) => {
if (props.dish != null) {
return (
<div className="container ">
<div className="row ">
<div className="col-12 col-sm-5 col-md-5 m-1">
<RenderComments comments={props.comments} />
</div>
</div>
</div>
);
} else {
return <div></div>;
}
};
export default Dishdetail;
RenderComments component implementation
function RenderComments({ comments, commentsName }) {
// created dishId constant to get the dishId of selected dish and
//need only first index dishId and passed it as prop to
//SubmitComment component as file like this
//changed below line
const dishId = comments.map((x) => x.dishId)[0];
if (comments != null) {
return (
<>
<div>
{comments.map((comment) => {
const options = { year: "numeric", month: "short", day: "2-digit" };
return (
<div key={comment.id}>
<ul className="list-unstyled">
<li>{comment.comment}</li>
<li>
--{comment.author} {new Date(comment.date).toLocaleDateString("en-us", options)}
</li>
</ul>
</div>
);
})}
<SubmitComment dishId={dishId} />
</div>
</>
);
} else {
return <div></div>;
}
}
and My SubmitComment.js component
function SubmitComment() {
const [modal, setModal] = useState(false);
const toggle = () => setModal(!modal);
const {
register,
formState: { errors },
handleSubmit,
reset,
} = useForm();
const onSubmit = (data) => {
console.log(data);
// to get the data from input fields
const { rating, author, comment } = data;
//dispatched the addComment action, here dishId is from props
dispatch(addComment({ dishId, rating, author, comment }));
// Finally subscribed to the store by useSelector method in main
// component which is parent component of renderComments component
//which in turn is parent component of SubmitComments component
reset();
};
return (
<>
<div>
<Button color="primary" onClick={toggle}>
Submit
</Button>
<Modal isOpen={modal} toggle={toggle} fade={false}>
<ModalHeader toggle={toggle}>Submit Comment</ModalHeader>
<ModalBody>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="mb-3">
<label htmlFor="rating" className="form-label">
Rating
</label>
<select
name="rating"
className="form-select "
aria-label="Default select example"
{...register("rating")}
>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</div>
<div className="mb-3">
<label htmlFor="name" className="form-label">
Your Name
</label>
<input
type="text"
className="form-control"
id="name"
placeholder="Your Name"
name="name"
{...register("name", { required: true, maxLength: "15" })}
/>
<small className="form-text text-danger">
{errors.name?.type === "required" && "This field is required"}
{errors.name?.type === "maxLength" && "Maximum 15 characters are allowed"}
</small>
</div>
<div className="mb-3">
<label htmlFor="comment" className="form-label">
Comment
</label>
<textarea
type="text"
name="comment"
className="form-control"
id="comment"
aria-describedby="comment"
rows="6"
{...register("comment")}
/>
</div>
<button type="submit" className="btn btn-primary">
Submit
</button>
</form>
</ModalBody>
</Modal>
</div>
</>
);
}
export default SubmitComment;
I am expecting that once I click on the submitcomment form, all the values that I entered in the fields namely rating, author and comments should get added in the renderComment component.
like this
I have tried to add dispatch action after clicking on submit in form and tried to use it in rendercomments component file using useSelector but i am unable to do it
So if anyone can describe the redux flow and basic working flow from here so I could implement this functionality
1 Created reducer in commentSlice.js file
2 In Dishdetail component made following changes:
In renderComments function
created dishId constant to get the dishId of selected dish and
need only first index dishId and passed it as prop to
SubmitComment component which is child component of renderComments
3 Received dishId as prop in SubmitComment and passed it as payload along with other valued received from the user input fields to the action addComment using dispatch
4 Finally subscribed the new state in Main component using useSelectore and passed as prop to Dishdetail component which is child component of main component
Successfully achieved the desired functionality.
I have updated the question with the changes.
I have have 2 inputs from different components and need to get data from them.
Moreover, place should be default, different in both components ('Place1', 'Place2').
The result should be written in to objects:
[{name: 'Tom', surname: 'Smith', place: 'Place1'},
{name: 'John', surname: 'Johnes', place: 'Place2'}]
const { register, handleSubmit } = useForm({
mode: 'onChange',
defaultValues: {
name: '',
surname: '',
place: '',
},
});
const saveAdress = (data) => {
console.log(data);
};
<form onSubmit={handleSubmit(saveAdress)}>
<div >
<GetName1 register={register} />
<GetName2 register={register} />
</div>
<button type="submit">
Save
</button>
</form>
And components:
const getName1 = () => {
return (
<form>
<input placeholder="name" {...register(0.name'}>
<input placeholder="surname" {...register(0.surname'}>
</form>
)
}
const getName2 = () => {
return (
<form>
<input placeholder="name" {...register(1.name'}>
<input placeholder="surname" {...register(1.surname'}>
</form>
)
}
So, the user fills any forms (null, one or both), press the button and the data should be saved like an array of objects.
You can use useFieldArray from react-hook-form.
Here's an example mentioned in the official documentation.
import React from "react";
import { useForm, useFieldArray } from "react-hook-form";
function App() {
const { register, control, handleSubmit, reset, trigger, setError } = useForm({
// defaultValues: {}; you can populate the fields by this attribute
});
const { fields, append } = useFieldArray({
control,
name: "test"
});
return (
<form onSubmit={handleSubmit(data => console.log(data))}>
<ul>
{fields.map((item, index) => (
<li key={item.id}>
<input {...register(`test.${index}.firstName`)} />
<Controller
render={({ field }) => <input {...field} />}
name={`test.${index}.lastName`}
control={control}
/>
<button type="button" onClick={() => remove(index)}>Delete</button>
</li>
))}
</ul>
<button
type="button"
onClick={() => append({ firstName: "bill", lastName: "luo" })}
>
append
</button>
<input type="submit" />
</form>
);
}
Here's a working codesandbox link.
If it's possible for you then kindly share the codesandbox link of your working example. We can implement this in your current example.
I am having some issues with setting the inital form field values using redux-form.
Here is the code I tried
import { Field, FieldArray, reduxForm, getFormValues, change } from 'redux-form'
const renderField = ({
input,
label,
type,
meta: { asyncValidating, touched, error }
}) => (
<div>
<label>{label}</label>
<div className={asyncValidating ? 'async-validating' : ''}>
<input {...input} type={type} placeholder={label}/>
{touched && error && <span>{error}</span>}
</div>
</div>
)
class Profile extends Component {
constructor(props) {
super(props);
this.state = {
firstName: null,
lastName: null,
}
}
componentDidMount() {
this.props.fetchProfile();
}
async handleChange(e) {
await this.setState({ 'initialValues': { [e.target.name] : e.target.value }});
await this.setState({ [e.target.name] : e.target.value });
}
onSubmit = (e) => {
this.props.saveProfile({
firstName: this.state.auth.firstName,
lastName: this.state.auth.lastName,
});
}
componentWillReceiveProps(nextProps) {
this.setState({
firstName : nextProps.initialValues.firstName,
lastName : nextProps.initialValues.LastName,
});
this.setState({ 'initialValues': {
firstName : nextProps.initialValues.firstName,
lastName : nextProps.initialValues.LastName,
}});
}
render() {
return (
<>
<form onSubmit={handleSubmit(this.onSubmit)}>
<div>
<Field
name="firstName"
type="text"
component={renderField}
label="firstName"
onChange={this.handleChange.bind(this)}
/>
</div>
<div>
<Field
name="lastName"
type="text"
component={renderField}
label="lastName"
onChange={this.handleChange.bind(this)}
/>
</div>
<div>
<button type="submit" disabled={pristine || submitting}>
Update Info
</button>
</div>
</form>
);
}
}
Profile = reduxForm({
form: 'Profile' ,
// fields,
validate,
asyncValidate,
enableReinitialize: true,
})(Profile);
function mapStateToProps(state, props){
let firstName = '';
let lastName = '';
return {
userData: state.auth.userData,
initialValues:{
firstName: state.auth.firstName,
lastName: state.auth.lastName,
}
}
}
const mapDispatchToProps = (dispatch) => {
return {
fetchProfile: () => dispatch(auth.actions.fetchProfile()),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Profile);
But it is not setting value in field when load. field is just empty
I guess Redux Form works little different.
You don't need to set explicit onChange handler or declare any state to hold form fields data in Redux Form. Update your code similar to below
import { Field, FieldArray, reduxForm, getFormValues, change } from 'redux-form'
const renderField = ({
input,
label,
type,
meta: { asyncValidating, touched, error }
}) => (
<div>
<label>{label}</label>
<div className={asyncValidating ? 'async-validating' : ''}>
<input {...input} type={type} placeholder={label}/>
{touched && error && <span>{error}</span>}
</div>
</div>
)
class Profile extends Component {
// No need constructor/to explicitly declare the state
componentDidMount() {
this.props.fetchProfile();
}
render() {
const { handleSubmit, pristine, submitting} = props; // You can have custom handleSubmit too
return (
<>
<form onSubmit={handleSubmit}>
<div>
<Field
name="firstName"
type="text"
component={renderField}
label="firstName"
/>
</div>
<div>
<Field
name="lastName"
type="text"
component={renderField}
label="lastName"
/>
</div>
<div>
<button type="submit" disabled={pristine || submitting}>
Update Info
</button>
</div>
</form>
);
}
}
Profile = reduxForm({
form: 'Profile' ,
// fields,
validate,
asyncValidate,
enableReinitialize: true,
})(Profile);
function mapStateToProps(state, props) {
return {
userData: state.auth.userData,
initialValues:{ // These are the initial values. You can give any name here for testing to verify the ReduxForm working
firstName: state.auth.firstName,
lastName: state.auth.lastName,
}
}
}
const mapDispatchToProps = (dispatch) => {
return {
fetchProfile: () => dispatch(auth.actions.fetchProfile()),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Profile);
The example in question and the answer would definitely help you to understand things better.
I have a component that represents a form for entering book data, e.g. title/author/etc.
If an ID is present, the component will make an API call to the API server, and get the book data.
What I'm trying to accomplish basically, is getting the record from the API server, and then setting the form fields to those values, so that the form is populated with data for the user to edit.
I have a method called loadBook which makes an API call to get the book data. The method works, it gets the record, it sets the state, but the form inputs do not seem to pick up that the state has changed.
What do I need to do to get the form populated with the record that was just fetched?
import React from "react";
import Axios from "axios";
import {
Redirect
} from "react-router-dom";
import FormBase from "../FormBase";
export default class BookForm extends FormBase {
constructor(props) {
super(props);
this.state = {
formFields: {
title: '',
author_id: null,
cover_image: null
},
authors: [],
};
}
componentDidMount() {
this.loadAuthors();
if (this.props.id) {
this.loadBook()
}
}
loadBook = () => {
Axios.get(`${process.env.REACT_APP_API_URL}/books/${this.props.id}`).then(response => {
this.setState(prevState => {
let formFields = Object.assign({}, prevState.formFields)
formFields['title'] = response.data['title']
formFields['author_id'] = response.data['author_id']
return {formFields}
})
})
}
loadAuthors = () => {
Axios.get(`${process.env.REACT_APP_API_URL}/authors`).then(response => {
this.setState({authors: response.data})
})
}
render() {
let authors = this.state.authors.map(author => {
return <option key={author.id} value={author.id}>{author.last_name}, {author.first_name}</option>
})
return (
<form onSubmit={(e) => {e.preventDefault(); this.props.handleSubmit(e, this.state.formFields, true)}}>
{this.state.redirect ? <Redirect to="/admin/books" /> : null}
<div className="form-group">
<label htmlFor="title">Title</label>
<input name="title" value={this.state.title} onChange={this.handleFieldChange} type="text" className="form-control" />
</div>
<div className="form-group">
<label htmlFor="author">Author</label>
<select name="author_id" onChange={this.handleFieldChange} className="custom-select" size="5">
{authors}
</select>
</div>
<div className="custom-file form-group">
<input name="cover_image" type="file" onChange={this.handleFieldChange} className="custom-file-input" id="customFile" />
<label className="custom-file-label" htmlFor="customFile">Cover Image</label>
</div>
<button style={{marginTop: '1rem'}} type="submit" className="btn btn-primary">Submit</button>
</form>
)
}
}
Try setting your state simply like so:
this.setState({formFields:
{
...this.state.formFields,
title: response.data['title'],
author_id: response.data['author_id']
}
})
I essentially followed this guide on uncontrolled components.
I added attributes for each form field using React.createRef(), and then on the form inputs you link the ref object like ref={this.author_id}. Then, you can do this.author_id.current.value = response.data.author_id and the input's value will then be set. This won't trigger onChange though, so you'll need to update the state too.
import React from "react";
import Axios from "axios";
import {
Redirect
} from "react-router-dom";
import FormBase from "../FormBase";
export default class BookForm extends FormBase {
constructor(props) {
super(props);
this.state = {
formFields: {
title: '',
author_id: null,
cover_image: null
},
authors: [],
};
this.title = React.createRef();
this.author_id = React.createRef();
}
componentDidMount() {
this.loadAuthors();
if (this.props.id) {
this.loadBook()
}
}
loadBook = () => {
Axios.get(`${process.env.REACT_APP_API_URL}/books/${this.props.id}`).then(response => {
console.log(this.author_id)
this.author_id.current.value = response.data.author_id
this.title.current.value = response.data.title
this.setState(prevState => {
let formFields = Object.assign({}, prevState.formFields)
formFields['title'] = response.data['title']
formFields['author_id'] = response.data['author_id']
return {formFields}
})
})
}
loadAuthors = () => {
Axios.get(`${process.env.REACT_APP_API_URL}/authors`).then(response => {
this.setState({authors: response.data})
})
}
render() {
let authors = this.state.authors.map(author => {
return <option key={author.id} value={author.id}>{author.last_name}, {author.first_name}</option>
})
return (
<form onSubmit={(e) => {e.preventDefault(); this.props.handleSubmit(e, this.state.formFields, true)}}>
{this.state.redirect ? <Redirect to="/admin/books" /> : null}
<div className="form-group">
<label htmlFor="title">Title</label>
<input name="title" ref={this.title} value={this.state.title} onChange={this.handleFieldChange} type="text" className="form-control" />
</div>
<div className="form-group">
<label htmlFor="author">Author</label>
<select name="author_id" ref={this.author_id} onChange={this.handleFieldChange} className="custom-select" size="5">
{authors}
</select>
</div>
<div className="custom-file form-group">
<input name="cover_image" type="file" onChange={this.handleFieldChange} className="custom-file-input" id="customFile" />
I am new to react JS and material UI.
If i am typing something in the text field, its capturing the value(debug and checked) but not displaying in the form.
Please have a look at the code. I am getting the form values and errors also perfectly but cant see the values on the page.
Some thing stopping to see what i am typing on the text fields.
class Header extends React.Component{
constructor(props) {
super(props);
this.state={
projectName: '',
projectNameError :'',
description: '',
descriptionError:'',
module: '',
moduleError:'',
AssetValue:'',
AssetValueError:''
}
}
change (event){
this.setState({
[event.target.name]: event.target.value
});
};
validate = () => {
let isError = false;
const errors = {
AssetValueError :'',
descriptionError :'',
projectNameError : '',
descriptionError :'',
moduleError : ''
};
if(this.state.projectName.length < 5){
isError = 'true';
errors.projectNameError = "projectName is mandatory";
}
if(this.state.description.length < 5){
isError = 'true';
errors.descriptionError = "description is mandatory";
}
if(isError){
this.setState({
...this.state,
...errors
});
}
return isError;
}
onSubmit (evnt){
evnt.preventDefault();
const error = this.validate();
console.log("the values are",this.state);
if(!error){
this.setState({
projectName: '',
projectNameError :'',
description: '',
descriptionError:'',
module: '',
moduleError:'',
AssetValue:'',
AssetValueError:'',
})
}
};
render(){
return (
<div className = "container">
<form>
<div className="form-group">
<label>Project Name:</label>
<MuiThemeProvider muiTheme={muiTheme}>
<TextField
name="projectName" hintText="projectName" floatingLabelText="projectName"
errorText={this.state.projectNameError}
value ={this.state.projectName}
onChange={event => this.change(event)} floatingLabelFixed />
</MuiThemeProvider>
</div>
<div className="form-group">
<label>Description:</label>
<MuiThemeProvider muiTheme={muiTheme}>
<TextField
name="description" hintText="description" floatingLabelText="description"
errorText={this.state.descriptionError}
value ={this.state.description}
onChange={event => this.change(event)} floatingLabelFixed />
</MuiThemeProvider>
</div>
<div className="form-group">
<label>Module:</label>
<MuiThemeProvider muiTheme={muiTheme}>
<TextField
name="module"
className="form-control"
placeholder="Module"
errorText={this.state.moduleError}
value ={this.state.module}
onChange={event => this.change(event)}/>
</MuiThemeProvider>
</div>
<div className="form-group">
<label>Choose Asset Name:</label>
<select name="AssetValue" onChange={event => this.change(event)}>
<option value="">Select</option>
<option value="BMI">BMI</option>
<option value="GYPSY">GYPSY</option>
<option value="PRS">PRS</option>
</select>
</div>
<button onClick={(evnt) => this.onSubmit(evnt)}>Submit</button>
<div>
<button onClick={(event) => this._onButtonClick(event)}>Button</button>
{this.state.showComponent ?<NewComponent /> : null}
</div>
</form>
</div>
);
}
}
export default Header;
I spotted little mistake from your code.
You are updating your state based on your target's name.
change (event){
this.setState({
[event.target.name]: event.target.value
});
};
But your "real" input field which is not "TextField" doesn't have name as prop.
Actually TextField is just a style wrapper for actual input field.
You can check this out by opening chrome react dev tools.
So in order to update your value based on name, you should pass "inputProps" to your TextField component.
I recommend you to read this document first.
TextField API