react update screen after changing values in the state - reactjs

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
}

Related

Condtional Renderin React using If-else not working

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>
)
}

Validate the Input for the react form

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
}}
};

How to retrieve a react component value

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?

React JS: How to make this delete button work

I have a simple ToDo application written in React. I cannot figure out how to remove the ToDos.
I have two separate files App.js
import React, { Component } from 'react';
import './App.css';
import ToDo from './components/ToDo.js';
class App extends Component {
constructor(props) {
super(props);
this.state = {
todos: [
{ id: 1, description: 'Walk the cat', isCompleted: true },
{ id: 2, description: 'Throw the dishes away', isCompleted: false },
{ id: 3, description: 'Buy new dishes', isCompleted: false}
],
newTodoDescription: ''
};
this.deleteTodo = this.deleteTodo.bind(this);
}
handleChange(e) {
this.setState({ newTodoDescription: e.target.value })
}
handleSubmit(e) {
e.preventDefault();
if (!this.state.newTodoDescription) { return }
const newTodo = { description: this.state.newTodoDescription, isCompleted: false };
this.setState({ todos: [...this.state.todos, newTodo], newTodoDescription: '' });
}
toggleComplete(index) {
const todos = this.state.todos.slice();
const todo = todos[index];
todo.isCompleted = todo.isCompleted ? false : true;
this.setState({ todos: todos });
}
deleteTodo(id) {
this.setState((prevState) => ({
items: prevState.items.filter(item => item.id !== id),
}))
}
render() {
return (
<div className="App">
<form onSubmit={ (e) => this.handleSubmit(e)}>
<input type="text"
value={ this.state.newTodoDescription }
onChange={ (e) => this.handleChange(e) }
/>
<input type="submit" />
</form>
<ul>
{ this.state.todos.map( (todo, index) =>
<ToDo key={ index }
description={ todo.description }
isCompleted={ todo.isCompleted }
toggleComplete={ () => this.toggleComplete(index) }
onDelete={ this.deleteTodo }
/>
)}
</ul>
</div>
);
}
}
export default App;
The second file is ToDo.js
import React, { Component } from 'react';
class ToDo extends Component {
render() {
return (
<li>
<input type="checkbox" checked={ this.props.isCompleted } onChange={ this.props.toggleComplete } />
<button onClick={() => this.props.deleteTodo(this.props.id)}>Delete</button>
<span>{ this.props.description }</span>
</li>
);
}
}
export default ToDo;
When I click on the button I am presented with an error: TypeError: _this2.props.deleteTodo is not a function
How can I get my current code to work?
The name of your prop is onDelete,
so in your component ToDo.js, you have to call your function like below
<button onClick={() => this.props.onDelete(this.props.id)}>Delete</button>
as answer to your comment
your state is define like
this.state = {
todos: [
{ id: 1, description: 'Walk the cat', isCompleted: true },
{ id: 2, description: 'Throw the dishes away', isCompleted: false },
{ id: 3, description: 'Buy new dishes', isCompleted: false}
],
newTodoDescription: ''
};
So, your function deleteTodo should be like
deleteTodo(id) {
this.setState((prevState) => ({
todos: prevState.todos.filter(item => item.id !== id),
}))
};
I'll suggest you should use the splice method.

reactjs componentWillUnmount is not being called

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"
/>
}

Resources