I’m updating an existing code base that calculates the total based on the option selected from the drop down lists. However, it’s currently not making any calculations and hence the total is not changing. From my troubleshooting, the productId is suppose to be changing, and it’s currently not.
Where productId was defined using useState set to null initially. And this is the parent component called JobFormContainer
import React, { useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import Utils from 'lib/utils';
import { JobForm } from 'components/Forms';
import { createJob, getJob, updateJob } from 'redux/slices/jobReducer';
import { getProducts } from 'redux/slices/productReducer';
export const JobFormContainer = ({ notify }) => {
const products = useSelector((state) => state.products.products);
const allPositionTypes = [
{ value: 'Full Time', label: 'Full Time' },
{ value: 'Part Time', label: 'Part Time' },
{ value: 'Contract', label: 'Contract' },
];
const travelTypes = ['none', '0%-25%', '25%-50%', '50%-75%', '75%+'];
const telecomuteOptions = ['No', 'Yes'];
const total = Utils.getProductTotal(productId)
console.log('productId', productId);
console.log('total', total);
useEffect(() => {
if (slug) {
setAction('edit');
dispatch(getJob({ slug }));
} else {
// get store products;
dispatch(getProducts());
setJobDetails({ ...jobDetails });
}
}, [productId]);
const jobFormatter = ({
url = '',
products = [],
travel = false,
jobLength = 0,
payRate = 0,
state = '',
country = '',
city = '',
productId = null,
description = '',
applyEmail = '',
title = '',
jobLoaded = false,
telecomute = false,
areaCode = '',
zipcode = '',
countries = [],
regions = [],
salary = 0,
positionTypes = [],
}) => {
return {
travel: travel?.value,
state: state?.value,
country: country?.value,
city,
productId,
description,
title,
zipcode,
salary,
apply_email: applyEmail,
apply_url: url,
area_code: areaCode,
pay_rate: payRate,
job_length: jobLength,
telecommuting_position: telecomute?.value,
position_types: positionTypes?.map((v) => v.value),
};
};
const handleSubmit = (values) => {
if (hasJobCredits()) {
dispatch(createJob({ job: jobFormatter(values) }))
.then(() => {
notify('success', 'Job successfully created.');
history.push({ pathname: '/dashboard/jobs' });
})
.catch((error) => notify('error', 'Job not created successfully.'));
}
};
const hasJobCredits = () => !!user?.job_credits.length;
const proceedToCheckout = (productId, job) => {
const { quantity } = Utils.getProduct(productId);
const currentCart = Utils.getCart();
const updateCart = {
...currentCart,
job,
productId,
quantity,
};
Utils.setCart(updateCart);
history.push('/employers/store/checkout');
};
const handleUpdate = (values) => {
dispatch(updateJob({ job: values, employer: user }))
.then(() => {
notify('success', 'Job successfully updated.');
history.push({ pathname: '/dashboard/jobs' });
})
.catch((error) => notify('error', 'Job not updated successfully.'));
};
const handleItemClick = (position) => {
const positionTypes = position?.map((pos) => pos.value);
setJobDetails({ positionTypes: positionTypes });
};
return (
<JobForm
user={user}
job={job}
handleItemClick={handleItemClick}
isNew={action === 'new'}
hasJobCredits={!!user?.job_credits.length}
handleOnSumbit={handleSubmit}
handleCancel={handleCancel}
handleUpdate={handleUpdate}
setProductId={setProductId}
total={total}
allPositionTypes={allPositionTypes}
travelTypes={travelTypes}
telecomuteOptions={telecomuteOptions}
createPending={createPending}
updatePending={updatePending}
proceedToCheckout={proceedToCheckout}
products={products}
/>
);
};
export default JobFormContainer;
Component where the select option is defined and state is set to what it’s been selected. It’s a child of the JobFormContainer called JobForm.jsx
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { Link } from 'react-router-dom';
import { Row, Col, FormGroup, ControlLabel } from 'react-bootstrap';
import {
TextField,
NumberField,
SelectField,
RegionsField,
CountriesField,
} from 'components/FormHelpers';
import { Editor } from 'components/Editor';
import { Loader } from 'components/Loader';
import { getRegions } from 'redux/slices/localeReducer';
import Utils from 'lib/utils';
import {
ButtonStyle,
Label,
StyledFormControl,
FieldContainer,
} from 'components/Styles';
import {
Container,
SectionTitle,
DescriptionLabel,
CategoryLabel,
CategoryFieldContainer,
LimitedAccess,
EditorWrapper,
} from './styles';
import { Formik, Field, Form, useField } from 'formik';
const ActionButtons = ({
isNew,
hasJobCredits,
createPending,
handleUpdate,
handleCancel,
updatePending,
values,
}) => (
<>
<CreateJob
isNew={isNew}
hasJobCredits={hasJobCredits}
handleCancel={handleCancel}
createPending={createPending}
/>
<UpdateJob
isNew={isNew}
hasJobCredits={hasJobCredits}
handleUpdate={handleUpdate}
handleCancel={handleCancel}
updatePending={updatePending}
values={values}
/>
</>
);
const CreateJob = ({ isNew, hasJobCredits, handleCancel, createPending }) =>
isNew &&
hasJobCredits && (
<Row style={{ marginBottom: 100 }}>
<Col md={3}>
<ButtonStyle
type='submit'
style={{ marginLeft: 14 }}
className='btn btn-primary'
>
Post Job
</ButtonStyle>
<ButtonStyle
type='button'
style={{ marginLeft: 14 }}
className='btn btn-primary'
onClick={handleCancel}
>
Cancel
</ButtonStyle>
<div
style={{
display: 'flex',
alignItems: 'center',
height: '4rem',
}}
>
<Loader width='25px' height='25px' show={createPending} />
</div>
</Col>
</Row>
);
const UpdateJob = ({
isNew,
values,
handleUpdate,
handleCancel,
updatePending,
}) =>
!isNew && (
<Row style={{ marginBottom: 100 }}>
<Col md={3}>
<ButtonStyle
type='button'
style={{ marginLeft: 14 }}
className='btn btn-primary'
onClick={() => handleUpdate(values)}
>
Update
</ButtonStyle>
<ButtonStyle
type='button'
style={{ marginLeft: 14 }}
className='btn btn-primary'
onClick={handleCancel}
>
Cancel
</ButtonStyle>
<div
style={{
display: 'flex',
alignItems: 'center',
height: '4rem',
}}
>
<Loader width='25px' height='25px' show={updatePending} />
</div>
</Col>
</Row>
);
const CheckOutSection = ({
isNew,
hasJobCredits,
products,
setFieldValue,
values,
total,
handleCancel,
setProductId,
}) =>
isNew &&
!hasJobCredits && (
<section>
<Row>
<Col md={6}>
<SectionTitle>Checkout</SectionTitle>
</Col>
</Row>
<Row>
<Col md={6}>
<FormGroup>
<label>Job Posts</label>
<FieldContainer>
<SelectField
options={
products
.filter((p) => p.category !== 'subscription')
.map((p) => ({ ...p, label: p.name })) || []
}
style={{ background: 'initial', height: 54 }}
name='productId'
onChange={() => {
// setFieldValue('productId', values.productId);
setProductId(values.productId);
}}
value={values.productId}
required
/>
</FieldContainer>
</FormGroup>
</Col>
<Col md={6}>
<Label>Order Total:</Label>
<FieldContainer
style={{
position: 'relative',
top: 14,
marginBottom: 40,
}}
>
{total}
</FieldContainer>
</Col>
</Row>
<Row>
<Col>
<LimitedAccess className='clearfix'>
+Includes LIMITED access to our resume database!
</LimitedAccess>
</Col>
</Row>
<Row>
<Col>
<ButtonStyle
type='submit'
style={{ marginLeft: 14 }}
className='btn btn-primary'
>
Checkout
</ButtonStyle>
<ButtonStyle
type='button'
style={{ marginLeft: 14 }}
className='btn btn-primary'
onClick={handleCancel}
>
Cancel
</ButtonStyle>
</Col>
</Row>
<Row>
<Col md={6}>
<SectionTitle>Pricing Information</SectionTitle>
<div className='main_div'>
<div className='price_table'>
{products &&
products
.filter((p) => p.category !== 'subscription')
.map((option, i) => {
if (option.display === undefined) {
return <p key={i}>{option.name}</p>;
}
return <p key={i}>{option.display}</p>;
})}
</div>
<div className='price_table big'>
<p>
<strong>Need More Jobs?</strong>
<Link className='green_btn' to='/#'>
Contact Us
</Link>
</p>
</div>
</div>
</Col>
<Col md={6}>
<SectionTitle>Special Offer:</SectionTitle>
<p className='left'>
Get Free Limited Access to QuirkyCoders's resume and social profile
database with each job post!
<br />
<span>(Access is restricted to 10 candidate profile views)</span>
</p>
</Col>
</Row>
</section>
);
export const JobForm = ({
user,
job,
travelTypes,
handleItemClick,
telecomuteOptions,
isNew,
hasJobCredits,
handleOnSumbit,
handleCancel,
handleUpdate,
total,
allPositionTypes,
updatePending,
createPending,
products,
productId,
setProductId,
}) => (
<Container>
<SectionTitle>
Job Postings
{user && (
<div className='pull-right'>
job post credits: {user.job_credits.length}
</div>
)}
</SectionTitle>
{
<Formik
initialValues={{
products: job?.products || [],
travel: job?.travel || 'No',
jobLength: job?.jobLength || 0,
payRate: job?.payRate || 0,
state: job?.state || '',
country: job?.country || '',
city: job?.city || '',
productId: job?.productId || '',
description: job?.description || '',
applyEmail: job?.applyEmail || '',
title: job?.title || '',
telecomute: job?.telecomute || 'No',
zipcode: job?.zipcode || undefined,
salary: job?.salary || 0,
positionTypes: job?.positionTypes.map((pt) => pt.name) || [],
slug: job?.slug,
}}
enableReinitialize={true}
onSubmit={(values) => handleOnSumbit(values)}
>
{({
form,
values,
handleChange,
handleBlur,
handleSubmit,
setFieldValue,
setFieldTouched,
errors,
touched,
}) => (
<Form name='jobpostForm' onSubmit={handleSubmit} noValidate>
<section>
<Row>
<Col sm={6}>
<TextField
placeholder='ex: Ruby on Rails Developer'
title='Title'
name='title'
onChange={handleChange}
value={values.title}
/>
</Col>
</Row>
<Row>
<Col md={12}>
<FormGroup>
<DescriptionLabel componentClass={ControlLabel}>
Description
</DescriptionLabel>
<EditorWrapper>
<Editor
onChange={handleChange}
value={values.description}
/>
</EditorWrapper>
</FormGroup>
</Col>
</Row>
</section>
<section>
<Row>
<Col md={12}>
<SectionTitle>Application Method</SectionTitle>
</Col>
</Row>
<Row>
<Col md={12}>
<TextField
placeholder='[email protected]'
title='Email'
name='applyEmail'
onChange={handleChange}
value={values.applyEmail}
required
/>
</Col>
</Row>
</section>
<section>
<Row>
<Col md={12}>
<SectionTitle>Location</SectionTitle>
</Col>
</Row>
<Row>
<Col md={6}>
<CountriesField
title='Country'
name='country'
id='country'
onChange={setFieldValue}
onBlur={setFieldTouched}
value={values.country}
error={errors.country}
touched={touched.country}
label
/>
</Col>
<Col md={6}>
<RegionsField
title='State or Region'
name='state'
onChange={handleChange}
onBlur={handleBlur}
value={values.state}
label
/>
</Col>
</Row>
<Row>
<Col md={6}>
<TextField
placeholder='Charlotte'
title='City'
name='city'
onChange={handleChange}
value={values.city}
/>
</Col>
<Col md={6}>
<NumberField
placeholder='12345'
title='Zipcode'
name='zipcode'
onChange={handleChange}
value={values.zipcode}
required
/>
</Col>
</Row>
</section>
<section>
<Row>
<Col md={12}>
<SectionTitle>Compensation & Travel</SectionTitle>
</Col>
</Row>
<Row>
<Col md={4}>
<FormGroup>
<CategoryLabel>Position Types</CategoryLabel>
<CategoryFieldContainer>
<SelectField
options={allPositionTypes}
name='positionTypes'
placeholder='Select Position Type'
defaultValue={[...new Set(values.positionTypes)].map(
(position) => ({
value: position,
label: position,
})
)}
onChange={() =>
setFieldValue('positionTypes', values.positionTypes)
}
isMulti
isSearchable
/>
</CategoryFieldContainer>
</FormGroup>
</Col>
<Col md={4}>
<FormGroup>
<CategoryLabel>Pay Rate</CategoryLabel>
<CategoryFieldContainer>
<StyledFormControl
title='Pay Rate'
type='number'
placeholder='55'
className='form-control'
name='payRate'
onChange={handleChange}
value={values.payRate}
required
/>
</CategoryFieldContainer>
</FormGroup>
</Col>
<Col md={4}>
<FormGroup>
<CategoryLabel>Length (Months)</CategoryLabel>
<CategoryFieldContainer>
<StyledFormControl
type='number'
placeholder='12'
className='form-control'
name='jobLength'
onChange={handleChange}
value={values.jobLength}
/>
</CategoryFieldContainer>
</FormGroup>
</Col>
</Row>
<Row>
<Col md={4}>
<TextField
style={{ background: 'initial', height: 54 }}
placeholder='70000'
name='salary'
title='Salary(p.a)'
onChange={handleChange}
value={values.salary}
label
required
/>
</Col>
<Col md={4}>
<SelectField
options={travelTypes.map((v) => ({ label: v, value: v }))}
name='travel'
title='Travel'
value={values.travel}
label
required
/>
</Col>
<Col md={4}>
<SelectField
options={telecomuteOptions.map((v) => ({
label: v,
value: v,
}))}
name='telecomute'
title='Telecomute'
value={values.telecomute}
label
required
/>
</Col>
</Row>
<ActionButtons
isNew={isNew}
hasJobCredits={hasJobCredits}
createPending={createPending}
updatePending={updatePending}
handleUpdate={handleUpdate}
handleCancel={handleCancel}
values={values}
/>
</section>
<CheckOutSection
isNew={isNew}
hasJobCredits={hasJobCredits}
products={products}
total={total}
setFieldValue={setFieldValue}
values={values}
handleCancel={handleCancel}
setProductId={setProductId}
/>
</Form>
)}
</Formik>
}
</Container>
);
export default JobForm;
File where the calculation for total is been done called Utils.js. Since this is not a React component, I really don’t understand how to pass the productId to the required function
export default class Utils {
static getDefaultProductOption = () => {
return [{ display: 'Purchase in Bulk and Save!', value: 0 }];
};
static getProducts = () => {
var products = [{ display: 'Purchase in Bulk and Save!', value: 0 }];
let cachedProducts = localStorage.getItem('p_data') || '[]';
JSON.parse(cachedProducts).map((product, index) =>
products.push({
display: product.name,
id: product.id,
quantity: product.quantity,
value: product.cost,
details: product,
created_at: product.created_at,
description: product.description,
sku: product.sku,
updated_at: product.updated_at,
})
);
products.push({ total: localStorage.getItem('_total'), value: 0 });
return products;
};
static getProductTotal = (productId, quantity = null) => {
console.log('productId', productId);
let total;
const product = this.getProduct(productId);
if (product.quantity) {
total = product
? parseInt(product.quantity, 10) * parseInt(product.value, 10)
: 0;
} else {
total = product
? parseInt(product.quantity, 10) * parseInt(product.value, 10)
: 0;
}
return Utils.currencyFormatter(total, 2);
};
static getProduct = (productId) => {
return this.getProducts().filter(
(product) => product.id === parseInt(product.quantity, 10)
)[0];
};
static getProductByQuantity = (quantity) => {
return this.getProducts().filter((product) => {
return (
product.quantity === parseInt(quantity, 10) ||
(product.quantity >= 5 && parseInt(quantity, 10) >= 5)
);
})[0];
};
}
When I console.log(productId) in the above Utils.js file, it’s given null instead of changing to the option selected value. Any hint on how I can make progress will be deeply appreciated.