changing Redux state does not affect the render view - reactjs

I am making a generic component and using it in a composite component also i pass some props to the generic component to behave in a certain way.I have used Redux to manage my state but when updating the state in Redux the component didn't re-render with the updated state.
Here are the components:
Generic component
export default class LabeledCheckBox extends Component {
constructor(props) {
super(props);
this.state = {
checked: false,
uncheckable: this.props.disableCheckBox
? this.props.disableCheckBox
: false
};
}
handleChange = (event) => {
this.setState({ checked: event.target.checked });
};
render() {
return (
<Form.Group as={Row}>
<Form.Label column="True" sm={9}>
{this.props.controlLabel}
</Form.Label>
<Col sm={3}>
<Form.Check
type="checkbox"
onChange={this.handleChange}
checked={this.state.checked}
onClick={this.props.clicked}
disabled={this.state.uncheckable}
/>
</Col>
</Form.Group>
);
}
}
LabeledCheckBox.propTypes = {
controlLabel: PropTypes.string.isRequired,
disableCheckBox: PropTypes.bool
};
parent component
export default class Endorsments extends Component {
constructor(props, context) {
super(props, context);
this.state = {
open: false
};
}
render() {
const { open } = this.state;
return (
<React.Fragment>
<LabeledCheckBox
clicked={() => this.setState({ open: !open })}
aria-controls="example-collapse-text"
aria-expanded={open}
controlLabel="Apply Endorsment"
disableCheckBox={!this.props.EndorsementSupported}
/>
<Collapse in={this.state.open}>
<div id="example-collapse-text">
<LabeledTextBoxWithCheckBox
controlLabel="BankName"
controlName="setBankName"
style={{ paddingBottom: '10px' }}
/>
<LabeledDateWithCheckBox controlLabel="Cheque Date" />
<Row>
<Col sm={6}>
<LabeledCheckBox controlLabel="User Name" />
</Col>
<Col sm={6}>
<LabeledCheckBox controlLabel="Cheque Sequence" />
</Col>
</Row>
</div>
</Collapse>
</React.Fragment>
);
}
}
Composit Component
class Preferences extends Component {
constructor(props, context) {
super(props, context);
this.props.fetchSupportedVendors();
}
loadScannersBasedOnVendor = () => {
if (
this.props.lastSelected.name === 'scannersVendors' ||
this.props.supportedScannerModule.length !== 0
) {
let select = this.props.supportedScannerModule.filter(
(element) =>
element.vendor ===
this.props.lastSelected.selectedObject.value
);
return select.map((element) => {
return { value: element.Value, label: element.name };
});
}
};
loadScannerMicrFonts = () => {
let supportedMicrs = [];
this.props.scannerCapabilities.supportedMicrFonts.forEach(
(element, key) => {
if (element.supported)
supportedMicrs.push({ value: key, label: element.value });
}
);
return supportedMicrs;
};
loadScannerBitDepth = () => {
let supportedBitDepth = [];
this.props.scannerCapabilities.supportedBitDepth.forEach(
(element, key) => {
if (element.supported)
supportedBitDepth.push({
value: key,
label: element.value
});
}
);
return supportedBitDepth;
};
afterSelectionEnded = () => {
if (
this.props.lastSelected &&
this.props.lastSelected.name === 'scannersModel' &&
!this.props.scannerCapabilities.supportedBitDepth
) {
this.props.loadScannerCapablilitiesToState(
this.props.lastSelected.selectedObject.value
);
}
};
render() {
return (
<Modal
{...this.props}
aria-labelledby="contained-modal-title-vcenter"
centered
dialogClassName="scanningModal"
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
Preferences
</Modal.Title>
</Modal.Header>
<Modal.Body>
<Row>
<h4>Default Scanner</h4>
</Row>
<Row>
<Col sm={12}>
<LabeledDropDown
controlLabel="Vendor"
controlName="scannersVendors"
placeholder="select a vendor please"
dropdownValues={
this.props.supportedScannerVendor
}
/>
</Col>
</Row>
<Row>
<Col sm={12}>
<LabeledDropDown
controlLabel="Model"
controlName="scannersModel"
placeholder="select model"
dropdownValues={[]}
loadDynamicItems={
this.loadScannersBasedOnVendor
}
afterValueChanged={this.afterSelectionEnded}
/>
</Col>
</Row>
<Row>
<Col sm={6}>
<VerticalLabeledDropDown
controlLabel="MICR Font"
controlName="ScannerMicrFont"
placeholder="select MICR Font"
dropdownValues={[]}
loadDynamicItems={this.loadScannerMicrFonts}
/>
</Col>
<Col sm={6}>
<VerticalLabeledDropDown
controlLabel="Bit Depth"
controlName="scannerBitDipth"
placeholder="select BitDepth"
dropdownValues={[]}
loadDynamicItems={this.loadScannerBitDepth}
/>
</Col>
</Row>
<hr />
<Row>
<h4>Scanner Feature</h4>
</Row>
<Endorsments
EndorsementSupported={
this.props.scannerCapabilities.supportedEndorsement
}
/>
<hr />
<Row>
<h4>General</h4>
</Row>
<Row>
<Col sm={6}>
<LabeledCheckBox controlLabel="Auto Save" />
</Col>
<Col sm={6}>
<LabeledCheckBox controlLabel="View While Scanning" />
</Col>
</Row>
<Row>
<Col sm={6}>
<LabeledCheckBox controlLabel="Use OCR for amount & date" />
</Col>
<Col sm={6}>
<LabeledCheckBox controlLabel="With UV scan" />
</Col>
</Row>
<Row>
<Col sm={12}>
<LabeledCheckBox controlLabel="Dont Show Prining language dialog" />
</Col>
</Row>
</Modal.Body>
<Modal.Footer bsPrefix="internal-modal-footer">
<Button onClick={this.props.onHide}>Close</Button>
<Button onClick={this.props.onHide}>Apply</Button>
</Modal.Footer>
</Modal>
);
}
}
const mapStateToProps = (state) => ({
supportedScannerVendor: state.supportedScanners.supportedVendors,
supportedScannerModule: state.supportedScanners.SupportedScanners,
lastSelected: state.dropdownEvents.dropDownSelectionChanged,
scannerCapabilities: state.supportedScanners.ScannerCapabilities
});
export default connect(mapStateToProps, {
fetchSupportedVendors,
loadScannerCapablilitiesToState
})(Preferences);
Finally
when calling loadScannerCapablilitiesToState the state is changed for this.props.scannerCapabilities.supportedEndorsement but it did not re-render the generic component to be disable or enabled
Am i missing something any help please?

if the Redux state is properly working then you need to change the state in LabeledCheckBox component.
componentDidUpdate() is invoked immediately after updating occurs.
componentDidUpdate = (prevProps, prevState) => {
if (prevProps.disableCheckBox !== this.props.disableCheckBox) {
this.setState({ uncheckable: this.props.disableCheckBox });
}
};
Read more about React lifecycle

Related

Increment or decrement a value in react array while mapping

export default class Cart extends Component {
constructor(props) {
super(props);
this.state = {
selectedForCart: [],
checkOut: true,
priceQuantity: undefined,
total: undefined,
};
}
render() {
return (
<div>
<NavBar />
{this.state.selectedForCart &&
this.state.selectedForCart.map((v, i) => {
return (
<Container className="mt-5" key={i}>
<Row>
<Col lg={2} md={2} sm={12}>
<p>
Price: ₹
{v.priceTaxIncluded}
</p>
<p style={{ fontSize: "10px" }}>Tax inclusive</p>
</Col>
<Col lg={2} md={2} sm={12}>
<Form.Group>
<Form.Label>Quantity</Form.Label>
<Form.Control
type="number"
placeholder="Quantity"
value={1}
onChange={(e) => {
this.setState({
priceQuantity: e.target.value
});
console.log(v.priceTaxIncluded * e.target.value);
}}
/>
</Form.Group>
</Col>
<Col lg={2} md={2} sm={12}>
{this.state.checkOut && (
<>
<p>{this.state.priceQuantity}</p>
</>
)}
</Col>
</Row>
</Container>
);
})}
<Footer />
</div>
);
}
}
Here in the above code, I'm mapping an array of items from the state object "" value. And if I increment a specific item, then that specific item's quantity only should increment. But it is not happening all items are getting incremented. And also the price should be multiplied with the incremented value and should be shown...
Thank You.
As in this image, I'm changing the quantity. But If I change the quantity of a product, then its reflecting the price for all products. As I have only one state object
You can bind the priceQuantity to input value. Then conditionally render in last column. As you are trying to print priceQuantity in last column it will show for all rows.
this.state = {
...
priceQuantity: 0,
..
};
{this.state.selectedForCart &&
this.state.selectedForCart.map((v, i) => {
return (
<Container className="mt-5" key={i}>
<Row>
<Col lg={2} md={2} sm={12}>
<p>
Price: ₹
{v.priceTaxIncluded}
</p>
<p style={{ fontSize: "10px" }}>Tax inclusive</p>
</Col>
<Col lg={2} md={2} sm={12}>
<Form.Group>
<Form.Label>Quantity</Form.Label>
<Form.Control
type="number"
placeholder="Quantity"
value={this.state.priceQuantity}
onChange={(e) => {
this.setState({
priceQuantity: e.target.value
});
console.log(v.priceTaxIncluded * e.target.value);
}}
/>
</Form.Group>
</Col>
<Col lg={2} md={2} sm={12}>
{this.state.checkOut && (
<>
<p>{this.state.priceQuantity ? v.priceTaxIncluded * this.state.priceQuantity : 0}</p>
</>
)}
</Col>
</Row>
</Container>
);
})}

When i change another form element autocomplete value automatically cleared in Formik React js

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;

React component property is being updated by connect and data store but are not displayed

While debugging the following component using React and Redux tools I see that the store is being updated properly as well as the internal relevant property 'meals' is being updated with correct data, nonetheless, the component is not being updated on screen.
class MealEditView extends React.Component {
constructor(props) {
super(props);
this.state = { editedMeal: this.props.editedMeal };
this.resetValuesForDisplay = this.resetValuesForDisplay.bind(this);
}
resetValuesForDisplay(editedMeal) {
editedMeal = editedMeal !== undefined ? editedMeal : new Meal();
let meal = new Meal();
meal.calories = editedMeal.calories !== undefined ? editedMeal.calories : '';
meal.description = editedMeal.description !== undefined ? editedMeal.description : '';
meal.date = editedMeal.date !== undefined ? editedMeal.date : '';
return meal;
}
render() {
return (
<>
<Card className="side-form d-none">
<CardHeader>
<CardTitle tag="h4">Horizontal Form</CardTitle>
</CardHeader>
<CardBody>
<Form className="form-horizontal">
<Row>
<Label md="3">Title</Label>
<Col md="9">
<FormGroup>
<Input
placeholder="Title"
type="text"
value={this.props.editMeal !== undefined ? this.props.editedMeal.title : ''}
/>
</FormGroup>
</Col>
</Row>
<Row>
<Label md="3">Calories</Label>
<Col md="9">
<FormGroup>
<Input
placeholder="Calories"
type="number"
key={this.props.editMeal !== undefined ? this.props.editedMeal.calories : ''}
value={this.props.editMeal !== undefined ? this.props.editedMeal.calories : ''}
/>
</FormGroup>
</Col>
</Row>
<Row>
<Label md="3">Date</Label>
<Col md="9">
<FormGroup>
<Input
type="text"
autoComplete="off"
value={this.props.editMeal !== undefined ? this.props.editedMeal.date : ''}
/>
</FormGroup>
</Col>
</Row>
<Row>
<Col md="3" />
<Col md="9">
<FormGroup check>
<Label check>
<Input type="checkbox" />
<span className="form-check-sign" />
Remember me
</Label>
</FormGroup>
</Col>
</Row>
</Form>
</CardBody>
<CardFooter>
<Row>
<Col md="3" />
<Col md="9">
<Button className="btn-round" color="info" type="submit">
Sign in
</Button>
</Col>
</Row>
</CardFooter>
</Card>
</>
);
}
}
const mapStateToProps = storeData => ({
editedMeal: storeData.meals.editedMeal,
});
const mapDispatchToProps = {};
const connectedMealsTable = connect(
mapStateToProps,
mapDispatchToProps,
)(MealEditView);
export default connectedMealsTable;
And the reducer :
export const DietActionReducer = (storeData, action) => {
switch (action.type) {
// case DIET_ACTION_TYPES.MEAL_ADD: {
// let newStoreData = {...storeData};
// let meals = newStoreData['meals'];
// meals[action.payload.meal.getId()] = action.payload.meal;
// break;
// }
case DIET_ACTION_TYPES.MEAL_EDIT: {
let newStoreData = { ...storeData };
let editedMeal = storeData.dataTable.find(meal => meal.id === action.payload);
newStoreData.editedMeal = editedMeal;
return newStoreData;
}
default:
return storeData || {};
}
};
What can please cause this ?
I apologize there is lots of code -most of it is just HTML ...
What can please cause this ?
Just a typo:
Change editMeal to be editedMeal based on your mapStateToProps()
Replace this.props.editMeal to this.props.editedMeal in your render() method. For this line this.state = { editedMeal: this.props.editedMeal }; use UNSAFE_componentWillreceiveprops() if you want to update state when props get updated but here no need to use it because you are using redux.

How to call OnChange function in react using withformik with antd component?

Here I'm calling onChange function on Formik Field but its not calling? How to call custom function on a Formik field?
This is my custom function under React Component:
onStudentScore = (value, form) => {
alert("called");
const maxScore = value.writtenexammaxscore;
console.log(maxScore);
form.getFieldValue("writtenexammaxscore", maxScore);
if (maxScore > form.getFieldValue("writtenexamstudentsscore")) {
alert("MaxScore is less than StudentScore");
}
};
And my Form is created under render and write a onChange function on a StudentScore field. But it's not called? How to call this function?
render() {
const { values, handleSubmit} = this.props
return (
return (
<div>
<h5 align="left">MidTerm Form</h5>
<Card>
<Form onSubmit={handleSubmit}>
<Row>
<Col span={4}>
<b>Written Exam:</b>
</Col>
<Col span={2}>
<Field
name="writtenexammaxscore"
component={AntInput}
type="text"
style={{ width: 40 }}
/>
</Col>
<Col span={2}>outof</Col>
<Col span={3}>
<Field
name="writtenexamstudentsscore"
component={AntInput}
type="text"
style={{ width: 40 }}
onChange={this.onStudentScore}
/>
// I wrote the function on field this way
</Col>
<Col span={2}>
<Divider type="vertical" />
</Col>
</Row>
<Row>
<Col span={10} />
<Col span={8} push={10}>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Col>
</Row>
</Form>
</Card>
</div>
);
}
const MidTermForm = withFormik({
mapPropsToValues: () => ({
writtenexammaxscore: '',
writtenexamstudentsscore: '',
oralexammaximumscore: '',
oralexamstudentsscore: '',
}),
handleSubmit(values, { resetForm }) {
resetForm();
console.log(values)
}
})(MidTermFormComponent)
export default MidTermForm
I tried by extending yup validation schema. Instead of calling a function in onChange
check this code sandbox

Redux-Form: Change in FieldArray submits parent form

I'm stuck on an issue with redux-form. I have a form which is being populated with initialValues. Inside the top level form I have a FieldArray which renders a list of input fields. Each input field has buttons to remove it (via fields.remove(index)) or insert another input right after it (via fields.insert(index+1)). My problem is: when I call fields.insert() the new field gets inserted and registered correctly, but the parent form gets submitted, which it shouldn't. Same happens on fields.push() when the data array has at least one item. But it does not on e.g. fields.remove() or fields.removeAll(). I cannot figure out why/where the submittal occurs. I've spend hours digging through the source and playing around with the official FieldArray example which does work. I couldn't find the issue online so I guess I have a bug somewhere, since this is independent of the redux-form version in use (tried >=6).
Thanks for your help, here's the code.
Parent Form
import React from 'react';
import { Field, FieldArray, reduxForm } from 'redux-form';
import Row from 'muicss/lib/react/row';
import Col from 'muicss/lib/react/col';
import Button from 'muicss/lib/react/button';
import MaterialInput from '../material/material-input';
import MaterialSelect from '../material/material-select';
import MaterialFieldArray from '../material/material-fieldarray';
import Cover from './cover';
import CheckboxGroup from './checkbox-group';
const MovieDetailsEditForm = props => {
const { handleSubmit, initialValues: { cover: { large: cover }, directors = [], actors = [] } } = props;
const getFSKOptions = () => {
return [
{value: 0, label: 0},
{value: 6, label: 6},
{value: 12, label: 12},
{value: 16, label: 16},
{value: 18, label: 18}
];
};
const getFormats = () => {
return [{value: 'DVD', label: 'DVD'}, {value: 'Blu-ray', label: 'Blu-Ray'}];
}
const getImages = () => props.initialValues.images.map( (image) => ({ value: image.large, checked: false }) );
return (
<Row>
<form className="mui-form" onSubmit={handleSubmit(props.handleFormSubmit)}>
<Col xs="12">
<Button type="submit" color="primary">Speichern</Button>
</Col>
<Col xs="12">
<Row>
<Col xs="12" md="6">
<Row>
<Col xs="12">
<Field name="title" id="title" component={MaterialInput} label="Filmtitel" type="text" />
</Col>
<Col xs="6">
<Field name="duration" id="duration" component={MaterialInput} label={props.initialValues.unit} type="text" />
</Col>
<Col xs="6">
<Field
name="format"
id="format"
options={getFormats()}
component={MaterialSelect}
label="Format"
parse={(value, name) => value ? value : null}
/>
</Col>
<Col xs="12">
<Field
name="fsk"
id="fsk"
options={getFSKOptions()}
component={MaterialSelect}
label="FSK Einstufung"
labelText="Freigegeben ab <<PLACEHOLDER>> Jahren"
parse={(value, name) => value ? value : null}
/>
</Col>
{ directors &&
<Col xs="12">
<h3>Regisseur{directors.length > 1 ? 'e' : ''}</h3>
<FieldArray component={MaterialFieldArray} name="directors" />
</Col>
}
{ actors &&
<Col xs="12">
<h3>Cast</h3>
<FieldArray component={MaterialFieldArray} name="actors" />
</Col>
}
</Row>
</Col>
<Col xs="12" md="6" className="cover">
<Field {...props} name="cover" id="cover" component={Cover} />
</Col>
</Row>
</Col>
<Col xs="12">
<Field {...props} name="images" component={CheckboxGroup} valueProperty="large" />
</Col>
</form>
</Row>
);
}
export default reduxForm({
form: 'MovieDetails',
enableReinitialize: true
})(MovieDetailsEditForm);
material-fieldarray.js
import React from 'react';
import { Field } from 'redux-form';
import Row from 'muicss/lib/react/row';
import Col from 'muicss/lib/react/col';
import Button from 'muicss/lib/react/button';
import MaterialInput from '../material/material-input';
export default props => {
const { fields } = props;
const addEntry = index => fields.insert(index + 1, '');
const removeEntry = index => fields.remove(index);
const renderEntries = () => {
return fields.map((field, index) => {
return (<li key={index}>
<Col xs="7" sm="8" md="7" lg="8">
<Field component={MaterialInput} name={`${field}`} id={`${field}`} />
</Col>
<Col xs="5" sm="4" md="5" lg="4" className="buttons">
<Button variant="fab" size="small" color="primary" onClick={() => addEntry(index)}>+</Button>
<Button variant="fab" size="small" color="danger" onClick={() => removeEntry(index)}>-</Button>
</Col>
</li>);
})
}
return (
fields.length &&
<ul className="inputfield-list">
{ renderEntries() }
</ul>
|| <Button onClick={() => fields.push('')}>Add</Button>
)
}
OK, the problem was a very subtle one and has nothing to do with React or Redux-Form. I forgot to put type="button" on the buttons that add/remove items to the FieldArray.
<Button variant="fab" size="small" color="primary" onClick={() => addEntry(index)}>+</Button>
That's how HTML forms work.

Resources