I'm trying to make my own custom input[type='text'] on React, with my own effects.
To do that, I created my component:
Textbox.js
import React from "react";
class Textbox extends React.Component {
constructor(props) {
super();
this.state = {
inputValue: props.value,
fieldActive: false,
label: props.label,
placeholder: props.placeholder,
type: props.type,
maxLength: props.maxLength,
error: false,
required: props.required,
inputChange: props.onChange,
id: props.id,
valid: !props.required,
submitted: props.submitted,
_value: props.value,
updated: false
};
//console.log(this.props);
this.updateInputValue = this.updateInputValue.bind(this);
this.activateField = this.activateField.bind(this);
this.disableFocus = this.disableFocus.bind(this);
}
componentWillReceiveProps(nextProps) {
this.setState({
fieldActive: (nextProps.value != ''),
inputValue: nextProps.value
});
}
componentWillUpdate(nextProps, nextState) {
//console.log(nextState.inputValue);
if(/*this.state.inputValue != this.props.value*/ this.props.value != '' && !this.state.updated ){
this.setState({
inputValue: nextProps.value,//this.props.value,
updated: true,
fieldActive: true
});
}
}
componentDidUpdate(prevProps) {
//console.log('updating value: ' + prevProps.submitted);
if (this.props.submitted !== undefined) {
if (this.props.submitted !== prevProps.submitted) {
if(!this.state.required) return;
this.setState({
error: this.state.required && this.state.inputValue == ""
});
}
}
}
activateField() {
this.setState({
fieldActive: true
});
}
disableFocus(e) {
if (e.target.value == "") {
this.setState({
fieldActive: false,
error: this.state.required,
valid: !this.state.required
});
} else {
this.setState({
error: false
});
if (this.state.type == "email") {
this.setState({
error: !/^[a-zA-Z0-9]+#[a-zA-Z0-9]+\.[A-Za-z]+$/.test(e.target.value)
});
}
}
}
updateInputValue(e) {
//console.log('writing: ' + e.target.value);
this.setState({
inputValue: e.target.value,
submitted: false,
});
//this.props.value = e.target.value
if (this.state.inputChange != undefined && this.state.inputChange != null)
this.state.inputChange(e.target.id, e.target.value, this.state.valid);
this.activateField(e);
e.preventDefault();
}
render() {
return (
<div className="form-group field-group">
<label
htmlFor=""
className={
this.state.fieldActive
? this.state.error
? "field-active form-label floating error"
: "field-active form-label floating"
: "form-label floating hide"
}
>
{this.props.label}
</label>
<input
className={
this.state.error
? "form-input floating-label error"
: "form-input floating-label"
}
type={this.props.type}
placeholder={this.props.placeholder}
maxLength={this.props.maxLength}
value={this.state.inputValue}
name={this.props.id}
id={this.props.id}
autoComplete="off"
onFocus={this.activateField}
onBlur={this.disableFocus}
onChange={this.updateInputValue}
/>
<label
className={this.state.error ? "error" : "error hide"}
style={{ fontSize: 14, fontWeight: 400 }}
>
You must complete this field
</label>
</div>
);
}
}
export default Textbox;
myjsfile.js
constructor(props) {
super(props);
this.state = {
clients: [],
client: {
name: "",
},
};
//... More methods and properties
<Textbox
label="Client name"
placeholder="Client name / Customer"
id="name"
type="text"
required={true}
value={this.state.client.name}
maxLength="20"
//onChange={this.handleChange}
submitted={this.state.submitted}
/>
In my file, I'm using Textbox as a component, but each time that I want to retrieve the value inside the Textbox I get empty.
What am I doing wrong? Why the value is not updating? How can I solve it?
Related
i'm new at React, i want to ask about contidional recndering, i'm making project about sending message, and i use validation to check all field is not empty, when one field is empty it should show error message that message not send. I using if-else to render the error message, but it doesn't work.
Here i add my code
....
export default class Message extends React.Component {
constructor(props){
super(props);
this.state = {
isLoggedIn: SystemStore.isLoggedIn(),
profile: ProfileStore.getProfile(),
fullName: SystemStore.systemUser().fullName,
site: '',
email: '',
phone: '',
subject: '',
description: '',
type: '',
errorMessage: '',
errorDialog: '',
isSubmited: false,
successMessage: '',
submitting: false
};
this.clearForm = this.clearForm.bind(this);
this.handleProfileChange = this.handleProfileChange.bind(this);
this.handleSubjectChange = this.handleSubjectChange.bind(this);
this.handleMessageChange = this.handleMessageChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleSubmitComplete = this.handleSubmitComplete.bind(this);
this.handleSubmitError = this.handleSubmitError.bind(this);
}
componentDidMount(){
ProfileStore.addProfileChangeListener(this.handleProfileChange);
if(!this.state.profile){
ProfileActions.reload()
}
MessageStore.addSubmitMessageChangeListener(this.handleSubmitComplete);
MessageStore.addSubmitMessageFailChangeListener(this.handleSubmitError);
}
componentWillUnmount(){
ProfileStore.removeProfileChangeListener(this.handleProfileChange);
MessageStore.removeSubmitMessageChangeListener(this.handleSubmitComplete);
MessageStore.removeSubmitMessageFailChangeListener(this.handleSubmitError);
}
render(){
return(
<Layout>
<div className='hs-dashboard row'>
<div className='col-md-12'>
<div className='col-xs-12 col-sm-6 col-md-5'>
<div className='col-xs-12 hs-message-form'>
<div className='row hs-message-form-head'>
<div className='hs-message-form-logo-container'>
<img className='col hs-message-form-logo' src='../../images/gii-logo-black.png'/>
<text className='hs-message-form-logo-label'>{T.translate('gii')}</text>
</div>
<div className='hs-message-form-label'>
{ T.translate('message.title') }
</div>
<div className='hs-message-form-label-1'>
{ T.translate('message.subtitle') }
</div>
</div>
<div className='row hs-message-form-body'>
<form className='hs-message-form-body-content'>
<label>
{ T.translate('message.type') }
</label>
<select
id="subject"
value={ this.state.subject }
onChange={ this.handleSubjectChange }
className="form-control"
required="true"
>
<option value="">{ T.translate('placeholder.selectSubject') }</option>
<option value="PRAYER">{ T.translate('message.pray') }</option>
<option value="ADDRESS">{ T.translate('message.address') }</option>
<option value="VISIT">{ T.translate('message.visit') }</option>
</select>
<label>
{T.translate('message.message')}
</label>
<textarea
type="text"
id="description"
className="form-control"
width=''
placeholder={ T.translate('placeholder.message') }
onChange={ this.handleMessageChange }
value={ this.state.description }
required
/>
{ this.state.errorDialog &&
<div className='hs-message-empty'>
{ this.state.errorDialog }
</div>
}
{ this.state.isSubmited === true ?
<div className='hs-message-success'>
{ this.state.successMessage }
</div> :
<div className='hs-message-error'>
{ this.state.errorMessage }
</div>
}
<LaddaButton
loading={ this.state.submitting }
onClick={ this.handleSubmit }
data-spinner-size={ 30 }
data-style={ SLIDE_RIGHT }
>
{ T.translate('action.send') }
</LaddaButton>
</form>
</div>
</div>
</div>
</div>
</div>
</Layout>
)
}
clearForm(){
this.setState({ subject: '', description: '' });
}
handleProfileChange(){
this.setState({
site: ProfileStore.getProfile().primarySite.name,
email: ProfileStore.getProfile().emailAddresses[0].email,
phone: ProfileStore.getProfile().contactNumbers[0].countryCode + ProfileStore.getProfile().contactNumbers[0].number
});
}
handleSubjectChange(evt){
this.setState({ subject: evt.target.value }, () => {
if(this.state.subject === 'PRAYER') {
this.setState({ type: 'REQUEST' });
} else if(this.state.subject === 'ADDRESS') {
this.setState({ type: 'INFORMATION' });
} else if(this.state.subject === 'VISIT'){
this.setState({ type: 'REQUEST' });
}
});
}
handleMessageChange(evt){
this.setState({ description: evt.target.value });
}
handleSubmit(evt) {
evt.preventDefault();
var errorDialog;
if(this.state.subject === ''){
errorDialog = 'Error:' + T.translate('msg.subjectRequired');
} else if(this.state.description === ''){
errorDialog = 'Error:' + T.translate('msg.mailDescriptionRequired');
}
this.setState({errorDialog: errorDialog});
this.handleProfileChange();
this.handleSubjectChange(evt);
this.handleMessageChange(evt);
var messageInfo = {
fullName: this.state.fullName,
site: this.state.site,
email: this.state.email,
phone: this.state.phone,
subject: this.state.subject,
description: this.state.description,
type: this.state.type
};
this.setState({ submitting: true }, () => {
MessageActions.sendMessage(messageInfo);
});
console.log(this.state.isSubmited);
}
handleSubmitComplete(){
this.setState({
submitting: false,
isSubmited: true,
errorMessage: null,
successMessage: T.translate('msg.mailSent')
});
this.clearForm();
}
handleSubmitError(){
this.setState({
submitting: false,
isSubmited: false,
errorMessage: T.translate('msg.mailSentFailed'),
successMessage: null
});
}
}
//updated code
This is my store.js
....
var MessageStore = assign({}, EventEmitter.prototype, {
emitSubmitMessageChange: function(){
this.emit(SUBMIT_MESSAGE);
},
addSubmitMessageChangeListener: function(callback){
this.on(SUBMIT_MESSAGE, callback);
},
removeSubmitMessageChangeListener: function(callback){
this.removeListener(SUBMIT_MESSAGE, callback);
},
emitSubmitMessageFailChange: function() {
this.emit(SUBMIT_MESSAGE_FAILED);
},
addSubmitMessageFailChangeListener: function(callback) {
this.on(SUBMIT_MESSAGE_FAILED, callback);
},
removeSubmitMessageFailChangeListener: function(callback) {
this.removeListener(SUBMIT_MESSAGE_FAILED, callback);
}
});
Dispatcher.register(function(action) {
switch(action.actionType){
case MessageConstants.PERFORM_SEND_MESSAGE:
MessageStore.emitSubmitMessageChange();
break;
case MessageConstants.PERFORM_SEND_MESSAGE_FAIL:
MessageStore.emitSubmitMessageFailChange();
break;
default:
//noop
}
})
export default MessageStore;
And this is my action.js
.....
function _sendMessage(messageInfo, callback) {
jQuery.ajax({
method: 'POST',
url: api('supportTickets'),
contentType: 'application/json',
data: JSON.stringify(messageInfo)
})
.done(function(messageInfo) {
callback(null, messageInfo);
})
.fail(function(err){
console.error('Failed to send message : ' + JSON.stringify(err));
callback(err, null);
});
}
var MessageActions = {
sendMessage: function(messageInfo) {
_sendMessage(messageInfo, function(err, messageInfo) {
if(err) {
Dispatcher.dispatch({
actionType: MessageConstants.PERFOM_SEND_MESSAGE_FAIL
});
}
Dispatcher.dispatch({
actionType: MessageConstants.PERFORM_SEND_MESSAGE,
messageInfo
});
});
}
};
module.exports = MessageActions;
Your form should be responsible for handling errors. You should know your errors at the time of submission of the form.
handleSubmit(evt) {
evt.preventDefault();
var errorDialog;
if(this.state.subject === ''){
errorDialog = 'Error:' + T.translate('msg.subjectRequired');
} else if(this.state.description === ''){
errorDialog = 'Error:' + T.translate('msg.mailDescriptionRequired');
}
if (errorDialog) {
this.setState({
// set your error here
})
return;
}
this.handleProfileChange();
this.handleSubjectChange(evt);
this.handleMessageChange(evt);
var messageInfo = {
fullName: this.state.fullName,
site: this.state.site,
email: this.state.email,
phone: this.state.phone,
subject: this.state.subject,
description: this.state.description,
type: this.state.type
};
this.setState({ submitting: true }, () => {
MessageActions.sendMessage(messageInfo);
});
console.log(this.state.isSubmited);
}
Your component seems to be a little complex. Think about breaking your component into smaller components. Using functional components I would do this task in a way something like that.
function validateForm() {
// validate form logic here. If it's ok, returns true, otherwise, returns false
}
function Message({ form }) {
if (!validateForm(form)) {
return (
<div>Please, fill the empty fields!!</div>
)
}
return (
<div>All good. Submit the form!!</div>
)
}
NewBie in React so bear any non-technical words. I have to work on form with given code below the code is somewhat incomplete in which I have to achieve validation on each input. If any error occurs displaying it below the input
element inside the span element. on onChange handler state gets updated whereas respective validation function is called
import React from "react";
class Form extends React.component{
state = {
firstName: { value: "" },
LastName: { value: "" }
};
handleInputChange = (e, validationFun) => {
e.preventDefault();
const target = e.target;
const inputName = target.name;
const inputValue = target.value;
this.setState({
[inputName]: {
value: inputValue,
...validationFun(inputValue)
}
});
};
render() {
return (
<div>
<div>
<label>First Name</label>
<input
type="text"
name="firstName"
value={this.state.firstName.value}
onChange={event =>
this.handleInputChange(event, this.validatefirstName)
}
/>
</div>
<div>
<label>First Name</label>
<input
type="text"
name="LastName"
value={this.state.LastName.value}
onChange={event =>
this.handleInputChange(event, this.validateLastName)
}
/>
</div>
</div>
);
}
validatefirstName = firstName => {
if (!firstName) {
return {
validateStatus: "error",
errorMsg: "firstName may not be empty"
};
} else if (firstName.length > 12) {
return {
validationStatus: "error",
errorMsg: `firstName is too long 11 characters allowed.)`
};
} else {
return {
validateStatus: "success",
errorMsg: null
};
}
};
validateLastName = LastName => {
if (!LastName) {
return {
validateStatus: "error",
errorMsg: "LastName may not be empty"
};
} else if (LastName.length > 12) {
return {
validationStatus: "error",
errorMsg: `LastName is too long 11 characters allowed.)`
};
} else {
return {
validateStatus: "success",
errorMsg: null
};
}
};
};
Yep after lot work, I was able to find Solution
import React from "react";
class Form extends React.component{
state = {
firstName: { value: "" },
LastName: { value: "" }
};
handleInputChange = (e, validationFun) => {
e.preventDefault();
const target = e.target;
const inputName = target.name;
const inputValue = target.value;
this.setState({
[inputName]: {
value: inputValue,
...validationFun(inputValue)
}
});
};
render() {
return (
<div>
<div>
<label>First Name</label>
<input
type="text"
name="firstName"
value={this.state.firstName.value}
onChange={event =>
this.handleInputChange(event, this.validatefirstName)
}
/>
<span style={{ color: "red" }}>
{this.state.firstName.errorMsg}
</span>
</div>
<div>
<label>Last Name</label>
<input
type="text"
name="LastName"
value={this.state.LastName.value}
onChange={event =>
this.handleInputChange(event, this.validateLastName)
}
/>
<span style={{ color: "red" }}>
{this.state.LastName.errorMsg}
</span>
</div>
</div>
);
}
validatefirstName = firstName => {
if (!firstName) {
return {
validateStatus: "error",
errorMsg: "firstName may not be empty"
};
} else if (firstName.length > 12) {
return {
validationStatus: "error",
errorMsg: `firstName is too long 11 characters allowed.)`
};
} else {
return {
validateStatus: "success",
errorMsg: null
};
}
};
validateLastName = LastName => {
if (!LastName) {
return {
validateStatus: "error",
errorMsg: "LastName may not be empty"
};
} else if (LastName.length > 12) {
return {
validationStatus: "error",
errorMsg: `LastName is too long 11 characters allowed.)`
};
} else {
return {
validateStatus: "success",
errorMsg: null
}}
};
I tried a lot to find the answer but failed. please help me.
First, a working example
this.state = {
module:'',
description: '',
status: '',
redirectToModule: false,
error: ''
}
-----------------
handleChange = name => event => {
this.setState({[name]: event.target.value})
}
Render section has
<TextField id="name" label="Module Name" className={classes.textField} value={this.state.module} onChange={this.handleChange('module')} margin="normal"/><br/>
<TextField id="description" type="text" label="Description" className={classes.textField} value={this.state.description} onChange={this.handleChange('description')} margin="normal"/><br/>
<TextField id="status" type="text" label="Status" className={classes.textField} value={this.state.status} onChange={this.handleChange('status')} margin="normal"/>
The above works successfully.
However, I require to set the state object like this
this.state = {
module: {
module: '',
description: '',
status: ''
},
redirectToModule: false,
error: ''
}
I thought I should mention for example 'module.status' instead of 'module' in onChange={this.handleChange('')} BUT THEN
Should I change the function handleChange, something like this..?
this.setState({module.[name]: event.target.value});
What should be the correct way...?
please help me. I am quite new to react but I grasped a lot in a short while. To be frank, I am still a novice.
The whole file is as below...without import and style sections
class EditModule extends Component {
constructor({match}) {
super();
// this.state = {
// module: '',
// description: '',
// status: '',
// roles:[],
// redirectToModule: false,
// error: ''
// }
this.state = {
module: {
module: '',
description: '',
status: '',
roles:[]
},
redirectToModule: false,
error: ''
}
this.match = match
}
componentDidMount = () => {
read({
moduleId: this.match.params.moduleId
}).then((data) => {
if (data.error) {
this.setState({error: data.error});
} else {
this.setState({
module: {
module: data.module,
description: data.description,
status: data.status,
roles: data.roles
}
});
}
})
};
clickSubmit = () => {
const module = {
module: this.state.module || undefined,
description: this.state.description || undefined,
status: this.state.status || undefined,
roles: this.state.roles || undefined
}
update({moduleId: this.match.params.moduleId}, module)
.then((data) => {
if (data.error) {
this.setState({error: data.error})
} else {
this.setState({'moduleId': data._id, 'redirectToModule': true});
}
}
);
};
handleChange = name => event => {
this.setState( { module: JSON.stringify( { [name]: event.target.value } ) } )
};
render() {
const {classes} = this.props
if (this.state.redirectToModule) {
return (<Redirect to={'/module/' + this.state.moduleId}/>)
}
return (
<Card className={classes.card}>
<CardContent>
<Typography type="headline" component="h2" className={classes.title}>
Edit Module
</Typography>
<TextField id="name" label="Module Name" className={classes.textField} value={this.state.module.module} onChange={this.handleChange('module')} margin="normal"/><br/>
<TextField id="description" type="text" label="Description" className={classes.textField} value={this.state.module.description} onChange={this.handleChange('description')} margin="normal"/><br/>
<TextField id="status" type="text" label="Status" className={classes.textField} value={this.state.module.status} onChange={this.handleChange('status')} margin="normal"/>
<br/> {
this.state.error && (<Typography component="p" color="error">
<Icon color="error" className={classes.error}>error</Icon>
{this.state.error}
</Typography>)
}
</CardContent>
<CardActions>
<Button color="primary" variant="raised" onClick={this.clickSubmit} className={classes.submit}>Submit</Button>
</CardActions>
</Card>
)
}
}
EditModule.propTypes = {
classes: PropTypes.object.isRequired
}
export default withStyles(styles)(EditModule)
#Avinash
Please change your handleChange function like this.
handleChange = name => event => {
this.setState({module[name]: event.target.value})
}
I am new to react and I am working on a project where I was ask to reset a form to its defaults.
I created a function that gets call after I click the reset button
<input id="reset_button"
type="button"
name="reset"
value="Reset"
onClick={this.resetSearch}/>
This is my function:
resetSearch: function() {
this.setState({ID: 'Moo'});
},
I do see the ID change value in the console but it does not update on the screen.
Other things that I have tried
# when I do this the element despairs from then screen
resetSearch: function() {
var values = this.fields.state.values;
this.setState({
defaultValues: {
values
},
ignoreDefault: false
});
}
#render function
render: function() {
return (
<div className="card-body with-padding-bottom-0">
<form id={this.formId}>
<div id="sn-fields" className="usa-grid-full sn-search">
<SNFields ref={(fields) => { this.fields = fields; }} ddl_id='sn_search_card_type' snOptions={ this.getProp('snOptions')} fields={this.getProp('fields')} updateParentState={this.updateStateByField} defaultFieldValues={this.getProp('defaultValues')} ignoreDefault={this.state.ignoreDefault}></SNFields>
</div>
<div className="usa-grid-full with-margin-top-10 validation-div">
<div id="sn_search_card_search_button_container" className="usa-width-one-whole">
<label htmlFor="system_validator"></label>
<input hidden name="system_validator" id="system_validator"/>
<input id="search_button" type="button" name="search" value="Search" onClick={this.personSearch}/>
<input id="reset_button" type="button" name="reset" value="Reset" onClick={this.resetSearch}/>
</div>
</div>
</form>
</div>
);
}
I was able to find a class SNFields
var SNFields = React.createClass({
filterFields: function(searchVal) {
console.log('PCQSFields - filterFields ')
var filterLabels = [];
//filter in this component since the filtering can't be done on the ruby side
switch(searchVal) {
case 'APPLICATION_ID':
case 'ENUMERATOR':
case 'ENCOUNTER_ID': {
filterLabels = ['ID'];
break;
}
case 'NAME_AND_DOB': {
filterLabels = ['Date of Birth', 'Last Name', 'Date Range', 'First Name'];
break;
}
default: {
break;
}
}
var fields = this.props.fields.slice();
for (var i = fields.length - 1; i > -1; i--) {
if (filterLabels.indexOf(fields[i].label) < 0) {
fields.splice(i, 1);
}
}
return fields;
},
render: function() {
console.log('NSFields - render ')
return (
<div>
<div className="usa-width-one-third">
<label htmlFor={this.props.ddl_id} className="card-label bold">Search Type</label>
<Dropdown id={this.props.ddl_id} onChange={this.updateFields} selectableArray={this.props.nsOptions} classes="" selectedOption={this.state.ddl}/>
</div>
<div className="flex-container" style={{'flexWrap': 'row'}}>
{this.nsFieldsHelper(this.state.fields)}
</div>
</div>
);
}
});
I guess what I really want to do is when I press the reset to call
SNFields.filterFields('NAME_AND_DOB')
but when I try that I get a message in the console that reads: Uncaught TypeError: NSFields.filterFields is not a function
How does your componentDidMount() and componentWillReceiveProps(newProps) look like?
This is how I have done an Input component:
import React, { Component } from 'react';
export default class Input extends Component {
displayName: 'Input';
constructor(props) {
super(props);
this.state = {
value: this.props.value,
disabled: this.props.disabled,
checked: this.props.checked,
className:this.props.className,
maxLength:this.props.maxLength,
placeholder:this.props.placeholder,
id:this.props.id,
name:this.props.name,
type:this.props.name,
oldValue:this.props.value,
backgroundColor:''
};
this.handleBlur = this.handleBlur.bind(this);
this.handleChange = this.handleChange.bind(this);
};
componentWillReceiveProps(nextProps) {
if (this.state.value !== nextProps.value) {
this.setState({ value: nextProps.value});
};
if (this.state.disabled !== nextProps.disabled) {
this.setState({ disabled: nextProps.disabled});
};
if (this.state.checked !== nextProps.checked) {
this.setState({ checked: nextProps.checked});
};
if (this.state.className !== nextProps.className) {
this.setState({ className: nextProps.className});
};
if (this.state.maxLength !== nextProps.maxLength) {
this.setState({ maxLength: nextProps.maxLength});
};
if (this.state.placeholder !== nextProps.placeholder) {
this.setState({ placeholder: nextProps.placeholder});
};
};
componentDidMount() {
this.setState({ value: this.props.value,
disabled: this.props.disabled,
checked: this.props.checked,
className:this.props.className,
maxLength:this.props.maxLength,
placeholder:this.props.placeholder
});
};
handleBlur(event) {
if ((this.props.checkError===null)||(this.props.checkError(event,false) === true)) {
this.setState({ value: event.target.value,
oldValue: event.target.value
})
}
else
{
this.setState({ value: this.state.oldValue })
}
this.setState({ backgroundColor: ''})
};
handleChange(event) {
if (this.state.value !== event.target.value) {
this.setState({ value: event.target.value })
if ((this.props.checkError!==null)&&(this.props.checkError(event,true) === false)) {
this.setState({ backgroundColor: 'red'})
}
else
{
this.setState({ backgroundColor: ''})
}
}
if (this.props.onClick!==null) {
this.props.onClick();
}
};
render() {
return <input value={this.state.value}
maxLength={this.state.maxLength}
placeholder={this.state.placeholder}
className={this.state.className}
id={this.props.id}
name={this.props.name}
type={this.props.type}
disabled={this.state.disabled}
checked={this.state.checked}
onBlur={this.handleBlur}
onChange={this.handleChange}
style={{background:this.state.backgroundColor}}/>
}
};
Input.propTypes=
{
value:React.PropTypes.string,
placeholder:React.PropTypes.string,
maxLength: React.PropTypes.number,
disabled:React.PropTypes.bool,
checked:React.PropTypes.bool,
className:React.PropTypes.string,
id:React.PropTypes.string,
name:React.PropTypes.string,
type:React.PropTypes.string,
checkError: React.PropTypes.func,
onClick: React.PropTypes.func
}
Input.defaultProps =
{
placeholder:'',
maxLength:100,
disabled:false,
checked:false,
value:'',
className:'',
id:'',
name:'',
type:'text',
checkError:null,
onClick:null
}
I have build a form, where there is dropdown on which selection below fields will be displayed. So say, first optionSet1 was selected for which there was 3 field to be shown. If user changes dropdown to select optionSet2, different set of options will be shown.
But when optionSet2 is being rendered and optionSet1 is removed it should have called componentWillUnmount for the each InputFields rendered previously, which is not the case. This function is never called.
class LeadUpdate extends React.Component {
constructor(props, context) {
super(props, context);
}
_getUpdateFields() {
let fields = this.props.inputFields[this.state.updateType];
return _.map(fields, f => {
_.assignIn(f, {
fieldParentClass: 'form-group col-lg-6',
eventName: this.state.eventName
});
return <InputField config={f} />
});
}
_onChange(id, value) {
this.setState({
optionSet: value
});
}
render() {
return (<div>
<div className="col-lg-5">
<form role="form" className="vymo-form">
<InputField values={this.props.values} onChange={this._onChange.bind(this)} />
</form>
</div>
<div className="row">
<form role="form" className="vymo-form">
{this._getUpdateFields()}
</form>
</div>
</div>)
}
}
Update: I have just realised that componentWillUnmount is being called but the actual problem is with eventListner. I am pasting codes here.
Problem -- I am using nodejs events for getting values from different input fields populated. But when optionSet changes all previous unmounted options are also listening to event.
InputField --
import eventsService from '../../../services/events-service';
class InputField extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
id: this.props.id,
value: this._getInputFieldValue() || '',
valid: true,
errorVisible: false,
errorMessage: ''
};
}
componentWillMount() {
if(this.props.eventName) {
this._subscription = eventsService.emitter.addListener(this.props.eventName, this._validate.bind(this));
}
}
componentWillUnmount() {
if(this.props.eventName) {
eventsService.emitter.removeListener(this.props.eventName, this._validate.bind(this));
}
}
_handleChange(event) {
if(this.props.onChange) {
this.props.onChange.call(null, this.state.id, event.target.value);
}
this.setState({
value: event.target.value
});
}
_getClasses(classes) {
if (classes) {
return classes.join(' ');
}
}
_getInputFieldProps() {
let inputProps = {
value: this.state.value,
type: this.props.type,
placeholder: this.props.placeholder || '',
id: this.props.id,
onChange: this._handleChange.bind(this),
className: this._getClasses(this.props.classes) ? this._getClasses(this.props.classes) + 'form-control' : 'form-control',
maxlength: this.props.maxLength,
disabled: this.props.disabled ? "true" : null,
min: this.props.min,
max: this.props.max,
readOnly: this.props.readonly ? "true" : null,
required: this.props.required
};
return inputProps;
}
_validate(result) {
if (this.props.required && !this.state.value) {
valid = false;
this.setState({
errorVisible: true,
errorMessage: 'this is required field',
valid: false
});
}
if(valid) {
this.setState({
errorVisible: false,
errorMessage: 'this is not a valid phone number',
valid: true
});
}
result.valid &= valid;
result.values.push({
type: this.props.type,
code: this.state.id,
value: this.state.value,
name: this.props.label
});
}
_getInputFieldValue() {
switch (this.props.type) {
case Types.NUMBER:
case Types.EMAIL:
case Types.DECIMAL:
case Types.PHONE:
case Types.TEXT:
return this.props.value;
}
}
render() {
let props = this._getInputFieldProps();
return (<div className={this.props.fieldParentClass}>
<label for={this.props.id}><span>{this.props.label}</span><span>{props.required ? '*' : ''}</span></label>
<input {...props}/>
<span className={this.state.errorVisible ? 'show' : 'hide'}>{this.state.errorMessage}</span>
</div>)
}
}
event service:--
import {EventEmitter} from 'events';
//TODO make this as constant
var emmiter = new EventEmitter();
export default {
emitter: emmiter,
}
I understand this event service is bad, its just was for quickly test this functionality.
The Field component remains mounted, in this case you need to use keys to identify which component has changed, added, or removed:
{this.state.mode === 'custom' ?
<Field
label="A"
name="requested_completes"
type="number"
key="a"
/>
:
<Field
label="B"
name="requested_completes"
type="dropdown"
key="b"
/>
}