Redux Forms for Creating and Editing - javascript

I am working with redux-form. I need to create a new user and update the user's information using the same form. I've made the required form to create a new user right now, but I don't know how to make it available for updating.
Form Component code:
class UserRegistrationForm extends PureComponent {
static propTypes = {
handleSubmit: PropTypes.func.isRequired,
reset: PropTypes.func.isRequired,
isLoading: PropTypes.bool,
submitting: PropTypes.bool.isRequired,
};
constructor() {
super();
this.state = {
showPassword: false,
};
}
showPassword = (e) => {
e.preventDefault();
this.setState(prevState => ({ showPassword: !prevState.showPassword }));
};
onSubmit = data => {
console.log(data);
}
render() {
const { handleSubmit, reset } = this.props;
return (
<Col md={12} lg={12}>
<Card>
<CardBody>
<div className="card__title">
<h5 className="bold-text">STUDENT INFORMATION</h5>
</div>
<form className="form form--horizontal" onSubmit={handleSubmit}>
<div className="form__form-group">
<span className="form__form-group-label">First Name*</span>
<div className="form__form-group-field">
<Field
name="name"
component="input"
type="text"
placeholder="Name"
/>
</div>
</div>
<div className="form__form-group">
<span className="form__form-group-label">Last Name*</span>
<div className="form__form-group-field">
<Field
name="surname"
component="input"
type="text"
placeholder="Surname"
/>
</div>
</div>
<div className="form__form-group">
<span className="form__form-group-label">E-mail*</span>
<div className="form__form-group-field">
<Field
name="email"
component="input"
type="email"
placeholder="example#mail.com"
/>
</div>
</div>
<ButtonToolbar className="form__button-toolbar">
<Button color="primary" type="submit" className="icon" size="sm"><SendIcon /> Submit</Button>
<Button type="button" onClick={reset} className="icon" size="sm">
<CloseIcon /> Cancel
</Button>
</ButtonToolbar>
</form>
</CardBody>
</Card>
</Col>
);
}
}
export default reduxForm({
validate,
form: 'User_Registration_Form', // a unique identifier for this form
})(withTranslation('common')(UserRegistrationForm));
How can I make the form both available creating and updating?

To use the same form for the update as well, you would need to give an initialValues state to Form. Where initial values would be the value of the student you want to edit. Initialize values would be empty when you are creating a new student.
UserRegistrationForm = reduxForm({
validate,
form: 'User_Registration_Form', // a unique identifier for this form
})(withTranslation('common')(UserRegistrationForm));
InitializeFromStateForm = connect(
state => ({
initialValues: studentData
}),
)(InitializeFromStateForm)
export default UserRegistrationForm
An example is here https://redux-form.com/6.6.3/examples/initializefromstate/
Also in your react route file, try to maintain two routes with the same form component.
<Route path="/student/create" component={UserRegistrationForm} />
<Route path="/student/update/:id" component={UserRegistrationForm} />
So whenever you need to create, you redirect to create route and when you need to update, redirect it to update route, with an id as params.

Related

How to pass data between components in react?

I have been trying to display the form data submitted but the map is throwing an error.
I have two components
NameForm.js
Here is the form input, handlechange and handlesubmit methods are done
function Nameform() {
const [form, setForm] = useState({firstname: "", lastname: ""});
const handleChange = (e) => {
setForm({
...form,
[e.target.id]: (e.target.value),
});
};
const handleSubmit = (e) => {
e.preventDefault();
console.log("hello from handle submit", form );
}
return (
<section>
<div className='card pa-30'>
<form onSubmit={ handleSubmit }>
<div className='layout-column mb-15'>
<label htmlFor='name' className='mb-3'>First Name</label>
<input
type='text'
id='firstname'
placeholder='Enter Your First Name'
data-testid='nameInput'
value={form.firstname}
onChange={handleChange}
/>
</div>
<div className='layout-column mb-15'>
<label htmlFor='name' className='mb-3'>First Name</label>
<input
type='text'
id='firstname'
placeholder='Enter Your First Name'
data-testid='nameInput'
value={form.firstname}
onChange={handleChange}
/>
</div>
<div className='layout-row justify-content-end'>
<button
type='submit'
className='mx-0'
data-testid='addButton'
>
Add Name
</button>
</div>
</form>
</div>
</section>
)
}
export default Nameform
NameList.js
I want to pass the data in handleSubmit in NameForm.js to NameList.js. But the data is not displayed.
function NameList({form}) {
return (
<section>
{form.map(displayName => {
return (
<ul
className='styled w-100 pl-0'
>
<li
className='flex slide-up-fade-in justify-content-between'
>
<div className='layout-column w-40'>
<h3 className='my-3'>{displayName.firstname}</h3>
<p className='my-0'{displayName.lastname}></p>
</div>
</li>
</ul>
)
})}
</section>
)
}
export default NameList;
App.js
In App.js, I want to display both the form and the data.
import { Nameform, Namelist } from './components'
function App() {
return (
<div>
<div className='layout-row justify-content-center mt-100'>
<div className='w-30 mr-75'>
<Nameform />
</div>
<div className='layout-column w-30'>
<NameList />
</div>
</div>
</div>
)
}
export default App;
Thank you for your help!
Pass the data you want to share between parent and children via props (which stands for properties).
In the parent class, when rendering <NameForm> and <ListForm> add the data like that:
//if you want to share count and name for example:
<NameForm
count={this.state.count}
name={this.state.name}
/>
You can add as many props as you want. Furthermore, you can pass a function and its argument using arrow functions:
<NameForm
aFunction={() => this.myFunction( /* anArgument */ )}
/>
To access props in a child class dynamically wherever you need them:
{this.props.count}
{this.props.name}
{this.props.aFucntion}
You can get rid of this.props using a technique called object destructing:
render(
const {count, name, aFunction} = this.props;
//now you can use {count} and {name} and {aFunction} without this.props
);
There are some bugs in your code, first form is an object not an array, so you can't map it, you need to use form.firstname and form.lastname, Also you set both input ids equal firstname you need to modify it, Also you need to move the form state and handleChange function to the App component.
This is a working code of your example.
https://codesandbox.io/s/upbeat-forest-328bon
You can save the state in the parent component and pass it as props to the child components like so.
Here we make use of an outer state called submittedForm to display only the submitted values. The inner form state is being used for handling the values before submitting.
// App.js
function App() {
const [submittedForm, setSubmittedForm] = useState({
firstname: "",
lastname: "",
});
return (
<div>
<div className="layout-row justify-content-center mt-100">
<div className="w-30 mr-75">
<NameForm setSubmittedForm={setSubmittedForm} />
</div>
<div className="layout-column w-30">
<NameList form={submittedForm} />
</div>
</div>
</div>
);
}
export default App;
// NameForm.js
function NameForm({ setSubmittedForm }) {
const [form, setForm] = useState({
firstname: "",
lastname: "",
});
const handleChange = (e) => {
// setActive(true);
setForm({
...form,
[e.target.id]: e.target.value,
});
};
const handleSubmit = (e) => {
e.preventDefault();
setSubmittedForm(form);
};
return (
<section>
<div className="card pa-30">
<form onSubmit={handleSubmit}>
<div className="layout-column mb-15">
<label htmlFor="name" className="mb-3">
First Name
</label>
<input
type="text"
id="firstname"
placeholder="Enter Your First Name"
data-testid="nameInput"
value={form.firstname}
onChange={handleChange}
/>
</div>
<div className="layout-column mb-15">
<label htmlFor="name" className="mb-3">
Last Name
</label>
<input
type="text"
id="lastname"
placeholder="Enter Your Last Name"
data-testid="nameInput"
value={form.lastname}
onChange={handleChange}
/>
</div>
<div className="layout-row justify-content-end">
<button type="submit" className="mx-0" data-testid="addButton">
Add Name
</button>
</div>
</form>
</div>
</section>
);
}
export default NameForm;
// NameList.js
function NameList({ form }) {
return (
<section>
<ul className="styled w-100 pl-0">
<li className="flex slide-up-fade-in justify-content-between">
<div className="layout-column w-40">
<h3 className="my-3">{form.firstname}</h3>
<p className="my-0">{form.lastname}</p>
</div>
</li>
</ul>
</section>
);
}
export default NameList;

React with Antd Form onFinish not retrieve data

I'm a beginner in React and I was following a tutorial on how to create a React app with Django backend.In the video he uses Ant Design Components v3(that was the latest when the video was made). Now I'm using the latest one v4 and they changed the form onSubmit to onFinish. After some research in the comments, people posted about the update and how to make it work but no luck.The problem is that I'm trying to get the data from the form inputs(title and content) and it shows undefined.Any ideas?
Here is the component:
import React, { Component } from "react";
import { Form, Input, Button } from "antd";
const FormItem = Form.Item;
class CustomForm extends Component {
handleFormSubmit = (values) => {
const title = values.title;
const content = values.content;
console.log(title, content, values);
};
render() {
return (
<div>
<Form onFinish={(values) => this.handleFormSubmit(values)}>
<FormItem label="Title">
<Input name="title" placeholder="Article Content" />
</FormItem>
<FormItem label="Content">
<Input
name="content"
placeholder="Enter Article Content"
/>
</FormItem>
<FormItem>
<Button type="primary" htmlType="submit">
Submit
</Button>
</FormItem>
</Form>
</div>
);
}
}
export default CustomForm;
And the output of the console.log() is:
undefined, undefined, {}
It's because Form.Item or, in your case, FormItem, must have a name prop which is missing so values are not being saved against that key, so e.g.
Change this:
<FormItem label="Title">
<Input name="title" placeholder="Article Content" />
</FormItem>
To
<FormItem label="Title" name="title">
<Input placeholder="Article Content" />
</FormItem>
Here is what i use instead of onSubmit for antd 4.x.x Form:
import React from 'react';
import { Form, Input, Button } from 'antd';
const FormItem = Form.Item;
class CustomForm extends React.Component {
handleFormSubmit = (values) => {
const title = values.title;
const content = values.content;
console.log(title, content);
};
render(){
return (
<div>
<Form onFinish={(values) => this.handleFormSubmit(values)}>
<FormItem label="Title" name="title">
<Input placeholder="Put a title here" />
</FormItem>
<FormItem label="Content" name="content">
<Input placeholder="Enter some content ..." />
</FormItem>
<FormItem >
<Button type="primary" htmlType="submit">Submit</Button>
</FormItem>
</Form>
</div>
);
}
}
export default CustomForm;

Conditionally render empty div or error with React & Bootstrap

I'm setting up a signup form that displays errors below the input fields if the user makes a mistake. The way I have it setup right now, the form will add a div with the error below when the user tries to submit their info. My issue is that when there's an error, it adds the div and messes up the layout of the form because it has to move everything to make space for each error. Is there a way to just have an empty div there if there isn't any errors so that it doesn't mess with the layout when there is one? So like, instead of having margin for spacing between fields, it's an empty div for the errors.
import React, { Component } from "react";
import axios from "axios";
import classnames from "classnames";
import "./Signup.css";
class Signup extends Component {
constructor() {
super();
this.state = {
username: "",
email: "",
password: "",
errors: {}
};
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
onSubmit(e) {
e.preventDefault();
const newUser = {
username: this.state.username,
email: this.state.email,
password: this.state.password
};
axios
.post("api/users/register", newUser)
.then(res => console.log(res.data))
.catch(err => this.setState({ errors: err.response.data }));
}
render() {
const { errors } = this.state;
return (
<div className="signup-form">
<form noValidate onSubmit={this.onSubmit}>
<h2>Sign Up</h2>
<p>It's free and only takes a minute.</p>
<hr />
<div className="form-group">
<label>Username</label>
<input
type="text"
name="username"
className={classnames("form-control form-control-md", {
"is-invalid": errors.username
})}
value={this.state.username}
onChange={this.onChange}
/>
{errors.username && (
<div className="invalid-feedback">{errors.username}</div>
)}
</div>
<div className="form-group">
<label>Email</label>
<input
type="text"
name="email"
className={classnames("form-control form-control-md", {
"is-invalid": errors.email
})}
value={this.state.email}
onChange={this.onChange}
/>
{errors.email && (
<div className="invalid-feedback">{errors.email}</div>
)}
</div>
<div className="form-group">
<label>Password</label>
<input
type="text"
name="password"
className={classnames("form-control form-control-md", {
"is-invalid": errors.password
})}
value={this.state.password}
onChange={this.onChange}
/>
{errors.password && (
<div className="invalid-feedback">{errors.password}</div>
)}
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary btn-block btn-lg">
Sign Up
</button>
</div>
<p className="small text-center">
By clicking the Sign Up button, you agree to our <br />
Terms & Conditions, and{" "}
Privacy Policy
</p>
<div className="text-center">
Already have an account? Login here
</div>
</form>
</div>
);
}
}
export default Signup;
Yes, you can use visibility:hidden property of css.
<div style={{ visibility: error.email? 'visible': 'hidden'}}></div>
since visibility always takes up space, in both cases it is visible as well as hidden. so it won't mess with the layout.

Initialize my form with redux-form

I am facing trouble with initializing my form with redux-form and the 'initialValues' props. I read a lot posts (for instance here) and I don't manage to get it work...
I see that my initialValues is properly set but my fields are not updated with it... Maybe the issue is in my renderBasicField function but I don't know how to fix this.
Otherwise it could also be something like the prop is not populated yet when the component is rendered... But I don't know what to do to make it work as I must rely on mapStateToProps to feed it.
I saw a lot of post about this kind of issues and unfortunately for me I already set up the enableReinitialize property to true :)
Here is my code :
// My component
class ProfileForm extends React.Component {
render () {
console.log(this.props);
const { handleSubmit } = this.props;
const messageClassname = this.props.errorMessage !== undefined ? stylesShared.errorMessage : this.props.confirmationMessage !== undefined ? stylesShared.confirmationMessage : '';
return (
<div>
<div>
<div>
<form onSubmit={handleSubmit(this.props.onSubmitProfileUpdate)}>
<div>
<h4>Votre profil</h4>
</div>
<div className={messageClassname}>
{this.props.errorMessage &&
<span>{this.props.errorMessage}</span>
}
{this.props.confirmationMessage &&
<span>{this.props.confirmationMessage}</span>
}
</div>
<div>
<Field name='firstname' type='text' label='Prénom' component={renderBasicField} />
</div>
<div>
<Field name='lastname' type='text' label='Nom' component={renderBasicField} />
</div>
<div>
<Field name='email' type='email' label='Email' addon='#' component={renderBasicField} />
</div>
<div>
<Field name='telephone' type='text' label='Téléphone' component={renderBasicField} />
</div>
<div>
<Field name='ranking' className='input-row form-group form-control' options={this.getTennisRankingsOptions()} type='select' component={renderSelectField} />
</div>
<div>
<Field name='city' type='text' label='Ville' component={renderBasicField} />
</div>
<div>
<button className='btn btn-info btn-lg center-block' type='submit'>Mettre à jour</button>
</div>
</form>
</div>
</div>
</div>
);
}
}
const reduxFormDecorator = reduxForm({
form: 'profile',
enableReinitialize: true,
validate: validateProfileForm
});
const mapStateToProps = (state) => {
return {
initialValues: state.userConnection.loadProfile.user
};
};
const reduxConnector = connect(
mapStateToProps,
null
);
export default reduxConnector(reduxFormDecorator(ProfileForm));
And the code to render my field :
// My renderFunction
export const renderBasicField = ({input, meta: {touched, error}, label, type='text', id, addon, styleClasses, handleChange, controlledAsyncValue}) => {
const inputStyles = getInputStyles(input.value, touched, error);
if (controlledAsyncValue !== input.value) {
input.value = controlledAsyncValue;
input.onChange(input.value);
}
return (<div className={inputStyles.container}>
{displayInputLabel(inputStyles.input.idInput, label)}
<div className={addon && 'input-group'}>
{addon && <span className='input-group-addon'>{addon}</span>}
<input
{...input}
className={classNames(styles.basicInputField, styleClasses)}
id={id}
value={input.disabled ? '' : input.value}
onChange={getOnChangeAction(input.onChange, handleChange)}
placeholder={label}
type={type}
aria-describedby={inputStyles.input.ariaDescribedBy}
/>
</div>
{touched && error &&
displayErrorMessage(error)}
</div>);
};
I am wondering if I am ignoring the initialValue with my custom renderBasicField function but in that case, I would I retrieve this value to set my input ?
Thanks a lot for your help ! :)
Try to switch connect and form decorator. It should helps.
export default reduxFormDecorator(reduxConnector(ProfileForm));

show last searched items below search form

I have a search form which is developed using redux form. I have used router to route to the form. After i submit the data from search form and revert back to the same form, i want to show a list of data that had been searched before whenever user clicks on search places input box. How can i do so?
Like in the image
Here is my code
const Banner = (props) => (
<Router>
<div className="container banner">
<ServiceType />
<div className="row">
<Match exactly pattern="/" location={props.location} component={Apartamentos} />
<Match pattern="/apartamentos" component={Apartamentos} />
<Match pattern="/coche" component={Coche} />
<Match pattern="/experiencias" component={Experiencias} />
</div>
</div>
</Router>
);
const renderGeoSuggestField = ({
input,
location
}) => (
<Geosuggest
fixtures={fixtures}
initialValue={input.value.label}
inputClassName="form-control destino"
onChange={(value) => input.onChange(value)}
onSuggestSelect={(value) => input.onChange(value)}
radius="20"
/>
);
const renderDateRangePicker = ({
input,
focusedInput,
onFocusChange,
}) => (
<DateRangePicker
onDatesChange={(start, end) => input.onChange(start, end)}
startDate={(input.value && input.value.startDate) || null}
endDate={(input.value && input.value.endDate) || null}
minimumNights={0}
/>
);
class ServiceType extends Component {
render() {
return(
div className="col-xs-12 col-sm-12 col-md-4 serviceImg">
<Link to="/apartamentos">
<img
src={imageUrl}
alt="apartamentos"
className="img-responsive"
/>
<h4>APARTAMENTOS</h4>
</Link>
</div>
);
}
}
class Apartamentos extends Component {
render() {
const { focusedInput } = this.state;
return (
<div className="form-box text-center">
<div className="container">
<form className="form-inline">
<div className="form-group">
<Field
name='geoSuggest'
component={renderGeoSuggestField}
onChange={(value) => this.onChange(value)}
onSuggestSelect={(suggest) => this.onSuggestSelect(suggest)}
location={new google.maps.LatLng(53.558572, 9.9278215)}
/>
</div>
<div className="form-group">
<Field
name="daterange"
onFocusChange={this.onFocusChange}
focusedInput={focusedInput}
component={renderDateRangePicker}
/>
</div>
<div className="form-group">
<Field
name="persona"
component="select"
>
<option>1 persona</option>
<option>2 personas</option>
</Field>
</div>
<button type="submit" className="btn btn-default buscar">BUSCAR</button>
</form>
</div>
</div>
);
}
}
const ApartmentForm = reduxForm({
form: 'ApartmentForm',
destroyOnUnmount: false,
})(Apartamentos);
What you should do is maintain is a redux state variable called say previousSearches which is initialized as an empty array. Everytime you click Submit push the form data into this previousSearches array. So when you click on the input button next just display all information from the previousSearches array (which is a redux variable and can be accessed as a prop).
Something like this I guess
case 'ADD_PREVIOUS_SEARCH':
return Object.assign({}, state, {
previousSearches: state.previousSearches.push(action.search)
})
Then you can just access previousSearches by this.props.previousSearches

Categories

Resources