I have this form that draws some fields dynamically and set the state of the form, the code is working flawlessly check the code here,
this is how the return statement looks like
return (
<div className="patientForm">
<section className="form">
<form onSubmit={onSubmit}>
{patientFormFields.map(({ title, type, values }, index) => (
<div className="form-group w-50" key={index}>
<label htmlFor={title}>{capitalize(title)}</label>
{type === "select" ? (
<select
name={title}
id={title}
value={formValue[title]}
onChange={(e) => handleChange(e, title)}
>
{values.map(({ value, text }, index) => (
<option key={index} value={value}>
{text}
</option>
))}
</select>
) : (
<input
type={type}
name={title}
id={title}
placeholder={capitalize(title)}
value={formValue[title]}
onChange={(e) => handleChange(e, title)}
/>
)}
</div>
))}
</form>
</section>
</div>
);
My problem is I don't like having my return statement be filled with logic so I tried to refactor the logic part into a separate component, check the code here,
this is my return statement now
return (
<div className="patientForm">
<section className="form">
<form onSubmit={onSubmit}>
{patientFormFields.map(({ title, type, values }, index) => (
<div className="form-group w-50" key={index}>
<label htmlFor={title}>{capitalize(title)}</label>
<FormFields type={type} title={title} values={values} />
</div>
))}
</form>
</section>
</div>
);
but it didn't work as expected, the input kept losing focus when I type, I looked up the problem and it seems that when I update my state, the component rerenders, react can't compare the two components/functions and that causes a creating a new component, I guess?! when I tried using a regular function (line-53) that returns JSX it worked correctly, check the code here.
this is my return statement now
return (
<div className="patientForm">
<section className="form">
<form onSubmit={onSubmit}>
{patientFormFields.map(({ title, type, values }, index) => (
<div className="form-group w-50" key={index}>
<label htmlFor={title}>{capitalize(title)}</label>
{FormFields(type, title, values)}
</div>
))}
</form>
</section>
</div>
);
};
The explanation sort of make sense but I don't understand why did my code work when I converted the JSX call in the component return to a regular function, aren't they both functions? and shouldn't my second code also be buggy since functions are reference type, and react shouldn't be able to compare them, I feel there is something missing that I don't understand. Also, what would be a better way to refactor that logic away from the component return?
edit: add code snippet to the problem
Related
I would like to only be able to select a single checkbox and not all or more than one. I would like to use checkbox instead of radio because I can disable it without having to mark another radio
<C.SingleCard>
<h2>Pizza</h2>
<div>
<hr />
<h3>Flavors</h3>
<hr />
</div>
<div>
<h4>Pepperoni</h4>
<input type="checkbox" name='flavor' />
</div>
<div>
<h4>Chicken</h4>
<input type="checkbox" name='flavor' />
</div>
<div>
<h4>Bacon</h4>
<input type="checkbox" name='flavor' />
</div>
<div>
<button>Add</button>
</div>
</C.SingleCard>
Here's the simplest way I can think of:
Put the options in an array and just keep track of the selected index. Set the index to null if the user selected the same index. Use the index parameter of map to set checked=true on the selected box. The onChange callback will trigger when the user selects one of the boxes, you can ignore the event parameter and just pass the index of the box.
const options = ['pepperoni', 'chicken', 'bacon'];
export default function App() {
const [selected, setSelected] = useState(null);
function onChange(i) {
setSelected((prev) => (i === prev ? null : i));
}
function whatDidIPick() {
console.log(options[selected] || 'nothing');
}
return (
<div>
{options.map((o, i) => (
<label key={i}>
{o}
<input
type="checkbox"
checked={i === selected}
onChange={() => onChange(i)}
/>
</label>
))}
<br />
<button onClick={whatDidIPick}>Log what I picked</button>
</div>
);
}
https://stackblitz.com/edit/react-ts-1uwfcq?file=App.tsx
hello i am trying to map items stored in my redux into a form . I created a component called PAYMENT.js that gives a format of how the item should be arranged
THIS IS A REACT PROJECT
PAYMENT.JS
import React from "react";
function Payment({ name, price, count, itemSize }) {
return (
<div>
<p> {name}, {itemSize}, {count} </p>
</div>
);
}
export default Payment;
I want it to be mapped in an input feild , this feild would be readable .
Here is it
<div class="telephone">
<label for="name"></label>
<input
className="payment_form"
type="text"
placeholder="My address"
name="telephone"
value={items.map((item, id) => (
<Payment
id={items.id}
key={id}
name={item.name}
price={item.price}
size={item.size}
image1={item.image1}
itemSize={item.itemSize}
count={item.count}
/>
))}
id="telephone_input"
required
/>
</div>
i get [OBJECT , OBJECT ][OBJECT , OBJECT ]
Instead of the actual object . Putting the map function in a p tag and iget the value of the items in the redux
<p>
{" "}
{items.map((item, id) => (
<Payment
id={items.id}
key={id}
name={item.name}
price={item.price}
size={item.size}
image1={item.image1}
itemSize={item.itemSize}
count={item.count}
/>
))}
</p>
OUTPUT
Von dutch pink, Xtra large, 1
Von dutch camoeo, Xtra large, 2
Von dutch pink, Xtra large, 3
I want this out put to show in the input feild so i can send it over
note that you should not give the attribute "value" an array value because a value attribute must be a string
I'm having some problem to use errors (object provided by React Hook Form) to show a validation message (when a required input field is empty) for the input fields.
{
inputs.map((name) => (
<div key={name}>
<div style={{ marginTop: "3px" }}>
<input
name={`Chamfer Set.${name}`}
ref={register({ required: true })}
/>
</div>
{errors[`Chamfer Set.${name}`] && (
<span>Please enter a value for {name}.</span>
)}
</div>
));
}
Basically I have to link errors to the input name attribute, which in this case I'm using template literals. But it is not working and I guess it is something related to the literals, but I'm not very into it. Do you guys have any idea?
If I use console.log(errors), I have the following structure:
To access the error property just replace:
errors[`Chamfer Set.${name}`]
By:
errors["Chamfer Set"] && errors["Chamfer Set"][`${name}`]
As #Micheal mentioned, Try
{
inputs.map((name) => (
<div key={name}>
<div style={{ marginTop: "3px" }}>
<input
name={`Chamfer Set.${name}`}
ref={register({ required: true })}
/>
</div>
{errors[`Chamfer Set`][`${name}`] && (
<span>Please enter a value for {name}.</span>
)}
</div>
));
}
Is there any other way I can write the following code without resorting to React.Fragment because that's not working.
<FormGroup>
<Input type="select" name="selectMultiPrefs" id="MultiPrefs" multiple>
{this.state.arrayForCategs.map(function (CategName, index) {
return (
<React.Fragment key={keyNum++}>
<option key={keyNum++} value={CategName} disabled>{CategName}</option>
{that.props.general.adprefrec.map(function (AdSingle, index) {
<option key={keyNum++} value={AdSingle.prefid}>{AdSingle.name}</option>
})}
</React.Fragment>
)
})}
</Input>
</FormGroup>
This isn't working because there is no such thing as <Input type="select">.
You want to use <select>.
See https://reactjs.org/docs/forms.html#the-select-tag
I have been trying to develop a dashboard form similiar to airbnb listing form for understanding more deeply about react redux but i am stuck in the middle of my project. I have a multiple form where when user clicks on continue button the user will get another form to fill and so on and if user clicks on back button the user will get form of one step back with previously filled values. I could not decide what should i do for this. Do i have to create a object in action as listingName . summary, name, email etc as empty value and update it with reducer using Object.assign() or what. Till now i could only develop like when user clicks on personal tab a form related to personal information is shown and when user clicks on basic tab, a form related to basic information is shown. I want all form data to be send to server at last submit. What should i do now ? Do i use increment and decrement action for the continue and back button and use submit action on the last form button ? Could you please provide me an idea ?
Here's my code
actions/index.js
export function selectForm(form){
return{
type: 'FORM_SELECTED',
payload: form
};
}
reducers/reducer_active_form.js
export default function(state=null, action){
let newState = Object.assign({},state);
switch(action.type){
case 'FORM_SELECTED':
return action.payload;
}
return state;
}
reducers/reducer_form_option.js
export default function(){
return[
{ option: 'Personal Information', id:1},
{ option: 'Basic Information', id:2 },
{ option: 'Description', id:3},
{ option: 'Location', id:4},
{ option: 'Amenities', id:5},
{ option: 'Gallery', id:6}
]
}
containers/form-details
class FormDetail extends Component{
renderPersonalInfo(){
return(
<div className="personalInfo">
<div className="col-md-4">
<label htmlFor='name'>Owner Name</label>
<input ref="name" type="textbox" className="form-control" id="name" placeholder="Owner name" />
</div>
<div className="col-md-4">
<label htmlFor="email">Email</label>
<input ref="email" type="email" className="form-control" id="email" placeholder="email" />
</div>
<div className="col-md-4">
<label htmlFor="phoneNumber">Phone Number</label>
<input ref="phone" type="textbox" className="form-control" id="phoneNumber" placeholder="phone number" />
</div>
<div className="buttons">
<button className="btn btn-primary">Continue</button>
</div>
</div>
);
}
renderBasicInfo(){
return(
<div>
<h3>Help Rent seekers find the right fit</h3>
<p className="subtitle">People searching on Rental Space can filter by listing basics to find a space that matches their needs.</p>
<hr/>
<div className="col-md-4 basicForm">
<label htmlFor="price">Property Type</label>
<select className="form-control" name="Property Type" ref="property">
<option value="appartment">Appartment</option>
<option value="house">House</option>
</select>
</div>
<div className="col-md-4 basicForm">
<label htmlFor="price">Price</label>
<input type="textbox" ref="price" className="form-control" id="price" placeholder="Enter Price" required />
</div>
<div className="buttons">
<button className="btn btn-primary">Back</button>
<button className="btn btn-primary">Continue</button>
</div>
</div>
);
}
renderDescription(){
return(
<div>
<h3>Tell Rent Seekers about your space</h3>
<hr/>
<div className="col-md-6">
<label htmlFor="listingName">Listing Name</label>
<input ref="name" type="textbox" className="form-control" id="listingName" placeholder="Be clear" />
</div>
<div className="col-sm-6">
<label htmlFor="summary">Summary</label>
<textarea ref="summary" className="form-control" id="summary" rows="3"></textarea>
</div>
<div className="buttons">
<button className="btn btn-primary">Back</button>
<button className="btn btn-primary">Continue</button>
</div>
</div>
);
}
renderLocation(){
return(
<div>
<h3>Help guests find your place</h3>
<p className="subtitle">will use this information to find a place that’s in the right spot.</p>
<hr/>
<div className="col-md-6">
<label htmlFor="city">City</label>
<input ref="city" type="textbox" className="form-control" id="city" placeholder="Biratnagar" />
</div>
<div className="col-md-6">
<label htmlFor="placeName">Name of Place</label>
<input ref="place" type="textbox" className="form-control" id="placeName" placeholder="Ganesh Chowk" />
</div>
<div className="buttons">
<button className="btn btn-primary">Back</button>
<button className="btn btn-primary">Continue</button>
</div>
</div>
);
}
render(){
if ( !this.props.form){
return this.renderPersonalInfo();
}
const type = this.props.form.option;
console.log('type is', type);
if ( type === 'Personal Information'){
return this.renderPersonalInfo();
}
if ( type === 'Basic Information'){
return this.renderBasicInfo();
}
if ( type === 'Description'){
return this.renderDescription();
}
if ( type === 'Location'){
return this.renderLocation();
}
}
}
function mapStateToProps(state){
return{
form: state.activeForm
};
}
export default connect(mapStateToProps)(FormDetail);
The first thing you're missing is controlled components. By giving the inputs a value property, and an onChange function, you will link the input with an external state.
Your components should have access, via react-redux, to the state and actions needed. The value of the form should be your state for that object. So you might have a state like:
location: {
listingName: '123 Main St',
summary: 'The most beautiful place!'
}
Then, you'd just pass each property to inputs. I'm assuming, in this example, that you've passed the location prop in mapStateToProps, and an actions object with all the related actions in mapDispatchToProps:
changeHandler(ev, fieldName) {
const val = ev.target.value;
this.props.actions.updateField(fieldName, val);
},
render() {
return (
<input
value={this.props.location.listingName}
onChange={(ev) => { this.changeHandler(ev, 'listingName'}}
/>
);
}
You provide it an action that can be used to update the state:
function updatefield(field, val) {
return {
type: UPDATE_FIELD,
field,
val
};
}
Then, you just merge it in, in your reducer
switch (action.type) {
case UPDATE_FIELD:
state = { ...state, [action.field]: val };
(using dynamic keys and spread operator for neatness, but it's similar to Object.assign)
All of your form state lives in the Redux store this way. When you are ready to submit that data to the server, you can either use async actions with redux-thunk, or set up some middleware to run the calls. Either way, the strategy is the same; your state lasts locally and populates all your forms, and then is sent to the server when the user submits.
I went through this pretty quick, let me know if you need me to elaborate on anything :)
As you are using react-redux you can use the redux-form. It will greatly help you with the coding as it will simplify your work load and it is also bug-free (as far as I know). In my opinion you would want to use all the libraries/frameworks provided to you as you want to be as agile as possible.
Also the redux-form has a wizard form implementation. I think that is exactly what you are looking for.
http://erikras.github.io/redux-form/#/examples/wizard?_k=yspolv
Just follow the link and you will see a very good tutorial on how to implement it. Should be a piece of cake.