reactjs componentWillUnmount is not being called - reactjs

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

Related

DataSearch component executes search every time a key is pressed

So I'm trying to make the DataSearch component behave but the component calls the search/triggerquery for every key stroke and I don't understand why. Also javascript throws an exception because it want's the dataField attribute defined even though I'm making use of a customQuery. I only want the query to be called, when the user presses enter. Can anyone help?
class DocumentSearchComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "",
corpus: "xxxx",
url: "http://localhost:7777",
};
this.handleKey = this.handleKey.bind(this);
this.onChange = this.onChange.bind(this);
}
handleKey(e, triggerQuery) {
if (e.key === "Enter") {
triggerQuery();
}
};
onChange(value) {
this.setState({
value,
});
};
customQuery(test) {
return {
query: {...}
},
_source: ["title", "publicationDate"],
size: 10,
highlight: {
pre_tags: ['<em>'],
post_tags: ['</em>'],
fields: {
fullText: {}
}
}
}
}
render() {
return (
<div className="App">
<ReactiveBase app={this.state.corpus} url={this.state.url}>
<DataSearch
componentId="documentSearch"
className="search-bar"
placeholder="Search for documents"
autosuggest={false}
customQuery={this.customQuery}
value={this.state.value}
onChange={this.onChange}
onKeyPress={this.handleKey}
/>
<ReactiveList
className="result-list"
componentId="SearchResult"
react={{
and: "documentSearch",
}}
dataField="label"
sortOptions={[
{ label: "Relevance", dataField: "_score", sortBy: "desc" },
]}
>
{({ data }) =>
data.map((item) => (
<div key={item.id} className="vertical-margin">
<DocumentResult
corpus={this.state.corpus}
document={item}
></DocumentResult>
</div>
))
}
</ReactiveList>
</ReactiveBase>
</div>
);
}

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?

Getting error on React Component on Render

As I an New to ReactJS.
What i am doing is when i type is any field State should be update in particular field -
As This is my LoginComponet and Setting small Form -
import React, { Component } from 'react';
import '../css/style.css';
export class LoginCompoent extends Component {
constructor(props) {
super(props);
this.state = {
field: {
phone: {
value: '',
validations: [],
errors: []
},
password: {
value: '',
validations: [],
errors: []
}
}
};
this.handelChangeEvent = this.handelChangeEvent.bind(this);
}
componentDidMount() {
}
handelChangeEvent(event) {
this.setState({
field: {
[event.target.id]: {
'value': event.target.value
}
}
});
}
render() {
console.log(this.state);
return (
<div className="loginMainDiv" >
<div className="">
<input className="login_inp" placeholder="Mobile Number"
value={this.state.field.phone.value}
onChange={this.handelChangeEvent}
type="text" name="phone" id="phone"
/>
<input className="login_inp" placeholder="Password"
value={this.state.field.password.value}
onChange={this.handelChangeEvent}
type="password" name="password" id="password"
/>
<button className="login_btn" >Login Safely</button>
</div>
</div>
);
}
}
Expected Result - on console.log(this.state);
when I type 9 in phone field -
field: {
phone: {
value: '9',
validations: [],
errors: []
},
password: {
value: '',
validations: [],
errors: []
}
}
Getting Result -
field: {
phone: {
value: '9'
}
}
I don't know why all fields are suddenly hidden when i update only phone field. ?
Because of this password is not setting in the form. ERROR - this.state.field.password is undefined ???
Deep merging issues, as you set a name property to your input, this should work:
this.setState(prevState => ({
field: {
...prevState.field,
[event.target.name]: {
'value': event.target.value
}
}
}));
In your handleChangeEvent function, you are updating the value of the field in the state:
this.setState({field: {
[event.target.id]: {
'value': event.target.value
}
}})
This will obviously overwrite the existing value of the field.
In your case, I would recommend using the callback function inside the setState. Please see the docs.
For example, if you want to update the value of the phone but also you want the value of the password to remain unchanged, You could do something like this:
handleChangeEvent = (event) => {
this.setState((prevState) => {
return {field: {
[event.target.id]: event.target.value,
...prevState.field
}
};
});
}
As I have tried all the Above answers but facing the error.
ERROR- Uncaught TypeError: Cannot read property 'id' of null on this.setState( prevState => ( {} ) ).
The Problem was - the reference of event are not maintain in async call that's why event.target is becoming null and getting above Error.
I got the concept of event.persist() which helps to maintain all the references of the event and make the Wrapper to the browser event with async calls.
You can go to this Article Reference

ReactJS - setting an inline style equal to a property on state is not working. What's going on?

I'm trying to get a FormWarning to display when users input incorrect information, but it seems to have disappeared on me. I'm trying to control whether or not it displays with this.state.formWarning.display - when the validateInputs function runs, if it determines an input is invalid, it should change the value of display from 'none' to 'block'. I'm trying to set the style for the Component to having a display that matches this.state.formWarning.display, but I am getting an error. Is my belief that you can set the styles for a component inline via an object not correct? Getting bugs regardless. ie
export default class FormOne extends React.Component {
constructor(props) {
super(props)
this.state = {
formOne: {
shippingAddress: {
firstName: '',
lastName: '',
address1: '',
city: '',
state: '',
zip: '',
country: 'US'
},
phone: '',
email: ''
},
formWarning: {
text: '',
invalidInputID: '',
display: 'block'
},
isSubmitted: false,
submitting: false
}
this.styles = this.props.styles || {}
}
componentWillReceiveProps(nextProps) {
if(nextProps.state.stepOne &&
nextProps.state.stepOne.formOneResponse) {
let formOneResponse = nextProps.state.stepOne.formOneResponse
formOneResponse.status === "delayed" || formOneResponse.status === "success"
? this.setState({isSubmitted: true})
: alert(formOneResponse.errorMessage)
this.setState(state => ({submitting: false}))
}
}
validateInputs = (inputs) => {
let { email, phone, shippingAddress } = inputs,
shippingKeys = Object.keys(shippingAddress)
console.log('validate inputs is firing')
for(let i = 0; i < Object.keys(shippingAddress).length; i++) {
let key = shippingKeys[i], input = shippingAddress[key]
if(!input) {
return this.showFormWarning(key)
}
}
if(!phone) return this.showFormWarning('phone')
if(/\S+#\S+\.\S+/.test(email)) return
this.showFormWarning('email')
return true
}
showFormWarning = key => {
clearTimeout(this.warningTimeout)
console.log('showformwarnign is firing')
this.setState(state => ({
formWarning: {
...state.formWarning,
text: 'Please fill out this field',
invalidInputID: key,
display: 'block'
}
}))
this.warningTimeout = setTimeout(() => {
this.setState(state => ({
formWarning: {
...state.formWarning,
display: 'none'
}
}))
}, 5000)
return false
}
saveInputVal = (event) => {
let { formOne: tempFormOne } = this.state,
input = event.currentTarget
console.log('saveinputvals is firing')
if(input.name === 'phone' || input.name === 'email') {
this.setState(state => ({
formOne: {
...state.formOne,
[input.name]: input.value
}
}))
} else {
this.setState(state => ({
formOne: {
...state.formOne,
shippingAddress: {
...state.formOne.shippingAddress,
[input.name]: input.value
}
}
}))
}
}
submit = (event) => {
event.preventDefault()
if(!this.validateInputs(this.state.formOne)) return
this.setState(state => ({submitting: true}))
this.props.saveShippingData(this.state.formOne)
this.props.stepOneSubmit(this.state.formOne)
}
render() {
if (this.state.isSubmitted) return <Redirect to="/order" />
let CustomTag = this.props.labels ? 'label' : 'span',
{ inputs, saveInputVal, styles, state } = this,
{ formWarning, submitting } = state,
{ invalidInputID, text, display } = formWarning
return (
<div style={this.styles.formWrapper}>
{
typeof this.props.headerText === 'string'
? ( <h2 style={this.styles.formHeader}>
{this.props.headerText}</h2> )
: this.props.headerText.map((text) => {
return <h2 key={text} style={this.styles.formHeader}
className={'header'+this.props.headerText.indexOf(text)}>{text}</h2>
})
}
<form onSubmit={this.submit} style={this.styles.form}>
<FormOneInputs inputs={inputs} saveInputVal={saveInputVal}
CustomTag={CustomTag} styles={styles} />
<button style={this.styles.button}>{this.props.buttonText}
</button>
</form>
<Throbber throbberText='Reserving your order...' showThrobber=
{submitting} />
<FormWarning style={display: {this.state.formWarning.display}} invalidInputID={invalidInputID} text={text}/>
</div>
)
}
}
You don't need to set any CSS class. The approach is as follows:
(1) Given a component you want to render or not render depending on a variable
(2) Make a helper method that checks for the condition and returns the actual component if you want it rendered. Otherwise, do nothing (basically returns undefined)
(3) Call that method from wherever you want the component to possibly appear.
Concrete example:
class FormOne extends React.Component {
// (...) all other things omitted to focus on the problem at hand
renderFormWarning() {
if (formIsInvalid) {
return <FormWarning ... />;
}
// else won't do anything (won't show)
}
render() {
return (
{/* ... */}
{this.renderFormWarning()}
);
}
}
In the above example, replace formIsInvalid with some statement that will tell you if the form is invalid. Then, if that condition is true, it will return the FormWarning component. Otherwise, no form warning will be shown. From the render() method, all you need do is call that helper method.

react update screen after changing values in the state

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
}

Resources