My class is calculating the hours between two places given the hour and minute at each place. I would like to change the <Input> part such that I don't have to repeat myself. The function time is taking two parameters the "hour" and the "minute". I tried but could not figure out. Please help.
updateTime(time, value){
let clock = this.state.time;
clock[time] = value;
this.setState(clock);
}
render(){
return(
<div>
<Card>
<CardBody>
<InputGroup>
<InputGroupAddon addonType={"prepend"}> Hour Origin</InputGroupAddon>
<Input placeholder=“1” onChange={(event)=> this.updateTime(‘hour’,event.target.value)}/>
<InputGroupAddon addonType={"prepend"}>Minute Origin</InputGroupAddon>
<Input placeholder=“1” onChange={(event)=> this.updateTime(‘minute’, event.target.value)}/>
</InputGroup>
<InputGroup>
<InputGroupAddon addonType={"prepend"}> Hour Destination</InputGroupAddon>
<Input placeholder=“1” onChange={(event)=> this.updateTime(‘hour’,event.target.value)}/>
<InputGroupAddon addonType={"prepend"}>Hour Destination</InputGroupAddon>
<Input placeholder=“1” onChange={(event)=> this.updateTime(‘minute’, event.target.value)}/>
</InputGroup>
</CardBody>
</Card>
</div>
)
}
You can package the InputGroups in components and then render the components.
Here's an example:
const HourMinuteInputGroup = props => {
const { locationName, updateTime } = props;
return (
<InputGroup>
<InputGroupAddon addonType={"prepend"}>Hour { locationName }</InputGroupAddon>
<Input placeholder="1" onChange={ event => updateTime('hour',event.target.value) }/>
<InputGroupAddon addonType={"prepend"}>Minute { locationName }</InputGroupAddon>
<Input placeholder="1" onChange={ event => updateTime('minute', event.target.value) }/>
</InputGroup>
)
}
Then your original render becomes:
render(){
return(
<div>
<Card>
<CardBody>
<HourMinuteInputGroup locationName="Origin" updateTime={ this.updateTime }/>
<HourMinuteInputGroup locationName="Destination" updateTime={ this.updateTime }/>
</CardBody>
</Card>
</div>
)
}
Related
I have this code where sets state on text field change.
and it also may get it's values from the props. It all works fine when I set state from the props value but I can not delete the text field values or change them.
componentDidUpdate(){
if (this.search.nid && this.state.nid !== this.search.nid) this.setState({ ...this.state, nid: this.search.nid })
if (this.search.cif && this.state.cif !== this.search.cif) this.setState({ ...this.state, cif: this.search.cif })
if (this.search.deposit && this.state.deposit !== this.search.deposit)
this.setState({ ...this.state, deposit: this.search.deposit })
}
render() {
this.deposits = this.props.permissions.permissions ? this.props.permissions.permissions : this.props.permissions
this.search = this.props.permissions.search ? this.props.permissions.search : {}
const features = this.props.config.features
return (
<div className='container'>
<div className={'shapeSection'}>
<i style={{ margin: '6px', fontSize: '25px' }} className='fa fa-check-circle' />
<h2>مدیریت دسترسی</h2>
</div>
<Form className={Style.formWrapper} onSubmit={e => {
e.preventDefault()
}}>
<FormGroup className='row'>
<Col sm={10}>
<Row>
<div className='floating-label col-4'>
<input className='floating-input floating-input-height1' type='text' placeholder=' ' name='nid'
onKeyPress={this.handleKeyPress}
onClick={this.handleClick}
onChange={(e) => this.setState({ ...this.state, nid: e.target.value })}
maxLength='15' value={this.state.nid} />
<label className='cust-label'>شناسه مشتری(کدملی،کداتباع،کدسیستم)</label>
</div>
{!features.hideCifInputInPermission.enable && <div className='floating-label col-4'>
<input className='floating-input floating-input-height1' type='text' placeholder=' ' name='cif'
onKeyPress={this.handleKeyPress}
onClick={this.handleClick}
value={this.state.cif}
onChange={(e) => this.setState({ ...this.state, cif: e.target.value })}
maxLength='15' />
<label className='cust-label'>شماره مشتری</label>
</div>}
<div className='floating-label col-4'>
<input className='floating-input floating-input-height1' type='text' placeholder=' ' name='deposit'
onKeyPress={this.handleKeyPress}
onClick={this.handleClick}
onChange={(e) => this.setState({ ...this.state, deposit: e.target.value })}
maxLength='18' value={this.state.deposit} />
<label className='cust-label'>{`شماره${strings.deposit || 'حساب'}`}</label>
</div>
</Row>
<Col sm={12}>
<FormText color="muted">
<span>{this.state.hint}</span>
</FormText>
</Col>
</Col>
<Col sm={2}>
<Button className={`${Style.searchBtn} col-sm-8`} color='info' onClick={this.findAccess}
type='button'>جستجو</Button>
<button onClick={this.handleHintClick} className={Style.hintBTN}>
<FontAwesomeIcon icon='question' />
</button>
</Col>
</FormGroup>
</Form>
{this.deposits.length && (this.state.nid.length || this.state.deposit.length || this.state.cif) ?
<div>
<PermissionsTable alert={this.alert} nid={this.state.nid} deposit={this.state.deposit} cif={this.state.cif} />
</div>
: (
<div>
<PermissionsTable alert={this.alert} />
</div>
)
}
{this.state.modal &&
<PermissionHintModal close={() => {
this.setState({ modal: false })
}} features={features} />
}
</div>
)
}
As you can see, there are no easily noticeable errors but I don't see how I can edit my states.
How can I change the text field values?
If you want to change the values of your text fields then you should only have to change the states of the text fields.
You might also want to look into using hooks as they are recommended by React themselves and they are generally considered much easier to manage.
using hooks can help you on this faster [https://reactjs.org/docs/hooks-intro.html]
Child component renders even though props are not altered of it.
Following is the parent component
import Child from "./Child";
function Parent({
selected,
show,
setShow,
}) {
const [isRunNow, setIsRunNow] = useState(true);
const [isNotifyMe, setIsNotifyMe] = useState(false);
const handleNotify = useCallback(() => {
setIsNotifyMe(!isNotifyMe);
}, [isNotifyMe]);
const handleSchedule = useCallback(() => {
setIsRunNow(!isRunNow);
}, [isRunNow]);
const WindowForm = () => {
return (
<div>
<Row>
<Col span={12}>
<label className={styles.labelWeight}> Name : </label>
<input
type="text"
value={selected.name}
readOnly={true}
className={styles.input}
/>
<br></br>
<br></br>
<label className={styles.labelWeight}>Description : </label>
<input
type="text"
value={selected.description}
readOnly={true}
className={styles.input}
></input>
<br></br>
<br></br>
<input
type="checkbox"
name="runImm"
id="runImm"
checked={isRunNow}
onChange={handleSchedule}
></input>
<label> Schedule as soon as possible</label>
</Col>
<Col span={12}>
<input
type="checkbox"
name="notifyProcess"
id="notifyProcess"
checked={isNotifyMe}
onChange={handleNotify}
></input>
<label> Notify me when this process ends</label>
<br></br>
<br></br>
<label>Submission Notes : </label>
<textarea
name="notes"
id="notes"
rows="4"
cols="50"
disabled={!isNotifyMe}
></textarea>
</Col>
</Row>
<Row>
<Col span={24}>
<Child isRunNow={isRunNow} />
</Col>
</Row>
</div>
);
};
return (
<Modal
visible={show}
width={800}
centered
maskClosable={false}
onCancel={() => setShow(false)}
title="JKM"
>
<WindowForm />
</Modal>
);
}
Child component is as follows:
import Uploader from "./Uploader";
import Downloader from "./Downloader";
const { TabPane } = Tabs;
const areEqual = (prevProps, nextProps) => {
console.log("passed here"); // THIS IS NEVER LOGGED!!
return true;
};
function Child({ isRunNow }) {
console.log(
`Rendering Childe component...isRunNow value : ${isRunNow}`
);
return (
<div>
<Tabs defaultActiveKey="ka">
<TabPane tab="ka" key="ka">
Panel
</TabPane>
<TabPane tab="sa" key="sa" disabled={isRunNow}>
<Downloader />
</TabPane>
<TabPane tab="da" key="da">
<Uploader />
</TabPane>
</Tabs>
</div>
);
}
export default React.memo(Child, areEqual);
When I check or uncheck check box Notify me, the child component Child re-renders every time. It seems props are not equal and hence its re-rending. I could not figure out where its going wrong.
Please suggest where i m doing wrong.
I suggest to you to separate the WindowForm to a component it's seems that this is the problem.
when I use it as a component the memo start to work
I think is due to this is a function that return component so it's render it no matter what.
the solution:
move WindowForm out side of the component and create a new one with it and then call it and it will work fine
const WindowForm = () => {
return (
<div>
<Row>
<Col span={12}>
<label className={styles.labelWeight}> Name : </label>
<input
type="text"
value={selected.name}
readOnly={true}
className={styles.input}
/>
<br></br>
<br></br>
<label className={styles.labelWeight}>Description : </label>
<input
type="text"
value={selected.description}
readOnly={true}
className={styles.input}
></input>
<br></br>
<br></br>
<input
type="checkbox"
name="runImm"
id="runImm"
checked={isRunNow}
onChange={handleSchedule}
></input>
<label> Schedule as soon as possible</label>
</Col>
<Col span={12}>
<input
type="checkbox"
name="notifyProcess"
id="notifyProcess"
checked={isNotifyMe}
onChange={handleNotify}
></input>
<label> Notify me when this process ends</label>
<br></br>
<br></br>
<label>Submission Notes : </label>
<textarea
name="notes"
id="notes"
rows="4"
cols="50"
disabled={!isNotifyMe}
></textarea>
</Col>
</Row>
<Row>
<Col span={24}>
<Child isRunNow={isRunNow} />
</Col>
</Row>
</div>
);
};
this component and his state to a new component and everything will start to work
The form has two elements.
When I select autocomplete value from emal element and then try to input a text value to titl field, the autocomplete value is automatically changed to empty.
What should I do to fix this issue?
I have tried to change formik initialValues using states but it's not working.
sorry for the language issue. Thanks in advance!
class TicketNew extends React.Component{
state = {
clearForm:false,
spinner:false,
closeForm:false,
emailsugges:[],
}
loadAlldata() {
this.setState({
spinner:false,
})
axios.post(baseUrl+'/api/load_company_list')
.then(res => {
const comanyList = res.data;
const emls = comanyList.emls.map(function(item, i){
return {
value:item.tci, title:item.tcc
}
})
this.setState({
emailsugges:emls
})
})
this.setState({
spinner:false,
})
};
componentDidMount(){
this.loadAlldata();
};
render(){
return(
<React.Fragment>
<Formik
initialValues={{ emal: "", titl: "" }}
validationSchema={formSchema}
>
{
({ errors,
touched,
handleSubmit,
isSubmitting,
handleBlur,
values,
resetForm
}) => (
<div>
<Form onSubmit={handleSubmit}>
<Card>
<CardHeader></CardHeader>
<CardBody>
<Row>
<Col md="5" sm="12">
<FormGroup row className="position-relative">
<Col md="4">
<span>Title</span>
</Col>
<Col md="8">
<Field
type="text"
name="titl"
id="titl"
className={`
form-control ${errors.titl && touched.titl && "is-invalid"}
`}
onBlur={handleBlur('titl')}
/>
{errors.titl &&
touched.titl ? (
<div className="invalid-tooltip mt-25">
{errors.titl}
</div>
) : null}
</Col>
</FormGroup>
</Col>
<Col md="2" sm="12"></Col>
<Col md="5" sm="12">
<FormGroup row className="position-relative"
style={{display:rqst!="1"?'none':''}}
>
<Col md="4">
<span>Email Address</span>
</Col>
<Col md="8">
<Field name="emal"
component={ ({field, form}) =>
<AutoComplete
type="email"
name="emal"
id="emal"
suggestions={this.state.emailsugges}
value={
this.state.emailsugges ?
this.state.emailsugges.find(option =>
option.value === field.value)
: ''}
className={`
form-control ${errors.emal && touched.emal && "is-invalid"}
`}
filterKey="title"
suggestionLimit={4}
/>}
/>
{errors.emal &&
touched.emal ? (
<div className="invalid-tooltip mt-25">
{errors.emal}
</div>
) : null}
</Col>
</FormGroup>
</Col>
</Row>
</CardBody>
</Card>
</Form>
</div>
)}
</Formik>
</React.Fragment>
)
}
};
export default TicketNew;
I like how Reactstrap handles Modal so I want to keep using it, but I can't figure out how to get the data out of a form and capture it in state.
const handleSubmit = (evt) => {
evt.preventDefault();
alert(`Submitting Name ${name}`);
};
With Reactstrap
<Form onSubmit={handleSubmit}>
<FormGroup>
<Label for="firstname">First Name</Label>{' '}
<Input name="speakername"></Input>
</FormGroup>
</Form>
When I use standard form and input elements, I'm able to capture what I need in handleSubmit, but I can't figure out how to do the same thing with the Form and Input tags of Reactstrap
Regular form and input elements
<form onSubmit={handleSubmit}>
<label>
First Name:
<input
type="text"
value={name}
onChange={e => setName(e.target.value)}
/>
</label>
<input type="submit" value="Submit" />
</form>
I was having exactly the same problem. Seemed to have fixed it as follows...
(I believe all you're missing are the value & onChange props for the Input component, and possibly the useState hooks for setName()... )
--- Set state ---
const currentDate = findDate();
function findDate() {
let d = new Date(),
month = "" + (d.getMonth() + 1),
day = "" + d.getDate(),
year = d.getFullYear();
if (month.length < 2) month = "0" + month;
if (day.length < 2) day = "0" + day;
return [year, month, day].join("-");
}
console.log(typeof currentDate);
const UpdateCount = () => {
const [date, setDate] = useState(currentDate);
const [hactCount, setHactCount] = useState("");
--- Handle Submit function ---
const handleSubmit = (e) => {
e.preventDefault();
alert(`${hactCount} hacts on ${date}`);
};
--- Return from functional component ---
return (
<div>
<Card>
<CardTitle className="border-bottom p-3 mb-0">
<i className="mdi mdi-priority-low mr-2"></i>Update your Hact Count
</CardTitle>
<CardBody>
<CardSubtitle className="py-2">
Insert your day's count and we'll do the magic
</CardSubtitle>
<Form onSubmit={handleSubmit}>
<FormGroup>
Date:
<Input
className="mt-2 mb-4"
type="date"
value={date}
onChange={(e) => {
setDate(e.target.value);
console.log(typeof e.target.value);
}}
/>
Count:
<Input
className="my-2 mb-4"
type="number"
placeholder="0"
value={hactCount}
onChange={(e) => {
setHactCount(e.target.value);
console.log(e.target.value);
}}
/>
<br />
<InputGroup className="text-center">
<Button className="text-center" color="primary" type="submit">
Update
</Button>
</InputGroup>
</FormGroup>
</Form>
</CardBody>
</Card>
</div>
);
Add a Button component with type=submit to your reactstrap form the same way you had an <input> with type=submit so that React know's to fire the onSubmit handler when the Button is clicked.
import { Form, FormGroup, Input, Label, Button } from "reactstrap";
<Form onSubmit={handleSubmit}>
<FormGroup>
<Label for="firstname">First Name</Label>{' '}
<Input name="speakername"></Input>
</FormGroup>
<Button type="submit">Submit</Button>
</Form>
you should be able using innerRef
onFormSubmit = (e) => {
e.preventDefault()
console.log(this.emailInputValue)
}
<Form onSubmit={this.onFormSubmit}>
<FormGroup>
<Label >Email:</Label>
<Input innerRef={(node) => this.emailInputValue = node} type="email" name="email" " placeholder="Email" />
<Button type="submit" color="primary" >Submit</Button>
</Form>
You can get the value by referring to the name key and ID of the input.
Exmaple
onFormSubmit = (e) => {
e.preventDefault()
console.log(e.target.company.value)
console.log(e.target.name.value)
}
<Modal isOpen={isModalVisible} toggle={handleModal}>
<ModalHeader toggle={handleModal}>add modal</ModalHeader>
<ModalBody>
<Form onSubmit={onFinish}>
<FormGroup>
<Label for="company">company</Label>
<Input type={"text"} name={"company"} id={"company"} placeholder={"company"} />
</FormGroup>
<FormGroup>
<Label for="name">name</Label>
<Input type={"text"} name={"name"} id={"name"} placeholder={"name"} />
</FormGroup>
<Button type="submit" color={"primary"}>
save
</Button>
</Form>
</ModalBody>
</Modal>
I have created a Formik form that contains a field array, form and fieldArray is in two separate classes as separate components.
My form:
<Formik onSubmit = {(values, { setSubmitting }) => { setSubmitting(false);}}
enableReinitialize>
{({handleSubmit, errors})=> (
<Form onSubmit= { handleSubmit }>
<Form.Group as= { Row } controlId= "cpFormGroupTitle" className="required">
<Form.Label className="post-create-label" column sm={ 2 } >
Title
</Form.Label>
<Col sm={ 10 }>
<Field name="title" component={ renderTextField } type="text"
isinvalid={ !!errors.title ? "true": "false" }
placeholder="Title *" />
</Col>
</Form.Group>
<Form.Group as= { Row } controlId= "cpFrmGroupShortDesc" className="required">
<Form.Label className="post-create-label" column sm={ 2 } >
Short Description
</Form.Label>
<Col sm={ 10 }>
<Field name="short-desc" component={ renderTextArea } type="text"
isinvalid={ !!errors.shortDescription ? "true": "false" }
placeholder="Short Description *" />
</Col>
</Form.Group>
<Form.Group as= { Row } controlId= "cpFormGroupFeatures">
<Form.Label className="post-create-label" column sm={ 2 }>
Features
</Form.Label>
<Col sm={ 10 }>
<TextFieldArray initialValues={{ features: [] } } name="features"/>
</Col>
</Form.Group>
<Form.Group as={ Row }>
<Col sm= { { span: 2, offset:2 } }>
<Button type="submit" variant="primary">Submit</Button>
</Col>
<Col sm={ 2 }>
<Button variant="secondary">Save as draft</Button>
</Col>
</Form.Group>
</Form>
)}
</Formik>
Here, <TextFieldArray> is field array , I need to get values from field array when form is submitted.
TextFieldArray:
export const TextFieldArray = (props) => (
<React.Fragment>
<Formik initialValues= { props.initialValues } render={({ values }) => (
<Form>
<FieldArray name= { props.name } render={arrayHelper => (
<div>
{ values[props.name] && values[props.name].length > 0 ?
(
values[props.name].map((item, index) => (
<div key={index}>
<Form.Group as= { Row }>
<div className="col-md-8">
<Field name={`${props.name}.${index}`}
className="form-control"/>
</div>
<div className="col-md-2">
<Button type="button" variant="outline-secondary"
onClick={() => arrayHelper.remove(index)}>
Remove
</Button>
</div>
<div className="col-md-2">
<Button type="button" variant="outline-secondary"
onClick={() => arrayHelper.insert(index, '')}>
Add
</Button>
</div>
</Form.Group>
</div>
))
) : (
<Button type="button" variant="outline-secondary"
onClick={() => arrayHelper.push('')} >
{`Add ${ props.name }`}
</Button>
)
}
</div>
)} />
</Form>
)} />
</React.Fragment>
);
I'm a beginner to ReactJS, so someone help me please, that will be huge help from you all.
Thanks.
To have field array be part of same form as other fields, only have one <Formik> and one <Form>. Then make initialValues on Formik that describes all the fields:
<Formik
initialValues={{ friends: someFriends, random: randomText }}
As seen in the following code from Formik FieldArray docs, with another form field added that is not part of the array:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import { Formik, Form, Field, useField, FieldArray } from "formik";
const someFriends = ["jared", "ian", "brent"];
const randomText = "Four score and seven years ago...";
function MyTextInput({ label, ...props }) {
// useField() returns [formik.getFieldProps(), formik.getFieldMeta()]
// which we can spread on <input> and alse replace ErrorMessage entirely.
const [field, meta] = useField(props);
return (
<>
<label
htmlFor={props.id || props.name}
css={{ backgroundColor: props.backgroundColor }}
>
{label}
</label>
<input className="text-input" {...field} type="text" {...props} />
{meta.touched && meta.error ? (
<div className="error">{meta.error}</div>
) : null}
</>
);
}
// Here is an example of a form with an editable list.
// Next to each input are buttons for insert and remove.
// If the list is empty, there is a button to add an item.
export const FriendList = () => (
<div>
<h1>Friend List</h1>
<Formik
initialValues={{ friends: someFriends, random: randomText }}
onSubmit={values =>
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
}, 500)
}
render={({ values }) => (
<Form>
<MyTextInput label="Random comment" name="random" />
<FieldArray
name="friends"
render={arrayHelpers => (
<div>
{values.friends &&
values.friends.length > 0 &&
values.friends.map((friend, index) => (
<div key={index}>
<Field name={`friends.${index}`} />
<button
type="button"
onClick={() => arrayHelpers.remove(index)} // remove a friend from the list
>
-
</button>
<button
type="button"
onClick={() => arrayHelpers.insert(index, "")} // insert an empty string at a position
>
+
</button>
</div>
))}
{/* Add a new empty item at the end of the list */}
<button type="button" onClick={() => arrayHelpers.push("")}>
Add Friend
</button>
<div>
<button type="submit">Submit</button>
</div>
</div>
)}
/>
</Form>
)}
/>
</div>
);
ReactDOM.render(<FriendList />, document.getElementById("root"));
Code in codesandbox.
I don't think you need to create second form for child component.
You need to just pass the values from the parent to the TextFieldArray
<TextFieldArray values={values.myArr} name="features"/>
And the child component just receive the values and render them (as if it was in the parent component)
export const TextFieldArray = (props) => {
return (
<React.Fragment>
<FieldArray
name= { props.name }
render={arrayHelper => (
<div>
{
props.values[props.name] && props.values[props.name].length > 0 ?
(
props.values[props.name].map((item, index) => (
<div key={index}>
<Form.Group as= { Row }>
<div className="col-md-8">
<Field name={`${props.name}.${index}`} className="form-control"/>
</div>
<div className="col-md-2">
<Button type="button" variant="outline-secondary"
onClick={() => arrayHelper.remove(index)}>
Remove
</Button>
</div>
<div className="col-md-2">
<Button type="button" variant="outline-secondary"
onClick={() => arrayHelper.insert(index, '')}
>
Add
</Button>
</div>
</Form.Group>
</div>
))
) : (
<Button type="button" variant="outline-secondary" onClick={() => arrayHelper.push('')} >
{`Add ${ props.name }`}
</Button>
)
}
</div>
)}
/>
</React.Fragment>
)
Of course don't forget to add the initial values of the array to the parent component.
And finally when you click on the submit button it would give you the values.