How can I resolve this promise from external function in React JSX? - async-await

I need some help understanding what and why this does not work when in React JSX, but does in standard JS.
First off, what is it called when you reference a var as a function?
var promiseResolve;
promiseResolve();
And second why does React return: "Uncaught TypeError: promiseResolve is not a function" when trying to call it?
I know this sounds like a intro question, but I've been doing pretty complex JS and now React web builds for five years now, and I embarrassingly don't know what it means to call an undefined function like that.
This all fits into a bigger question of trying to resolve a promise from an await in an external function via a modal, but without trying to go into detail, if the below would work in JSX, it would solve my problems.
var promiseResolve, promiseReject;
var promise = new Promise(function(resolve, reject){
promiseResolve = resolve;
promiseReject = reject;
});
promiseResolve();
For more context, I'm trying to get this to work (EDIT: request to show full code after this blurb. Everything else works, it's only recently I was asked to add a process to warn if an email was already sent):
let promiseResolve, promiseReject;
const resendAttempt = () => new Promise((resolve,reject) => {
promiseResolve = resolve;
promiseReject = reject;
});
const resolvePromise = () => {
promiseResolve();
}
return (
<Modal... <button onclick={resolvePromise} ... /> ... />
<Form onSubmit={async (values ...
await resendAttempt();
)}
/>
)
FULL CODE, look for comments on where issue is:
import React, { useState } from 'react';
import { Formik, Form, useField, FieldArray, Field } from 'formik';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import Button from 'components/Elements/Button';
import H3 from 'components/Elements/H2';
import Modal from 'react-bootstrap/Modal';
import BootstrapButton from 'react-bootstrap/Button';
import Wrapper from './Wrapper';
const TeamRecruitmentForm = props => {
const [showForm, setShowForm] = useState(true);
const [customFormError, setCustomFormError] = useState(false);
const [customFormSuccess, setCustomFormSuccess] = useState(false);
const [recruitRow, setRecruitRow] = useState({
recruitFirstName: '',
recruitLastName: '',
recruitEmail: ''
});
const captFirstName = props.userData.name ? props.userData.name.first : '';
const captLastName = props.userData.name ? props.userData.name.last : '';
const cityName = props.customGreetingFields.eventCity ? props.customGreetingFields.eventCity : '';
let messageSubject = `Join ${captFirstName} ${captLastName}\'s Leaders for Life Team`;
let promiseResolve, promiseReject;
const resendAttempt = () => new Promise((resolve,reject) => {
promiseResolve = resolve;
promiseReject = reject;
});
// this is called with a modaal button click
const resolvePromise = () => {
promiseResolve(); // JS errror, console says it is not a function
}
const [showTeammember, setShowTeammember] = useState(false);
const [showResend, setShowResend] = useState(false);
const handleClose = () => {
console.log('handleClose');
setShowTeammember(false);
setShowResend(false);
};
const handleShow = (modal) => {
console.log('handleShow');
if (modal == 'teammember') {
setShowTeammember(true);
} else if (modal == 'resend') {
setShowResend(true);
}
};
function validateEmail(value,x) {
let error;
const filterTeamRoster = props.teamRosterActivity.filter(user => {
return user.email == value;
});
if (!value) {
error = 'Required';
} else if (!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)) {
error = 'Invalid email address';
}
else if (filterTeamRoster.length > 0) {
handleShow('teammember');
error = 'Current Teammember';
}
return error;
}
function validateFirstName(value) {
let error;
if (!value) {
error = 'Required';
}
return error;
}
function validateLastName(value) {
let error;
if (!value) {
error = 'Required';
}
return error;
}
return (
<>
<Modal
show={showTeammember}
id={props.id}
size={props.size}
onHide={handleClose}
className={`mx-auto ${props.modalClass}`}
centered
>
<Modal.Header closeButton closeLabel="Back" className="bg-primary py-1">
<BootstrapButton
variant="link"
onClick={handleClose}
className={`text-white btn-modal-back btn-back-${props.modalClass}`}
>
<span className="fas fa-caret-left" alt="" />
Back
</BootstrapButton>
</Modal.Header>
<Modal.Body>
<H3>Current Teammember</H3>
<p>The person associated with the email address has already joined your team</p>
<Button variant="primary" className="btn-block" onClick={handleClose}>
OK
</Button>
</Modal.Body>
</Modal>
<Modal
show={showResend}
id={props.id}
size={props.size}
onHide={handleClose}
className={`mx-auto ${props.modalClass}`}
centered
>
<Modal.Header closeButton closeLabel="Back" className="bg-primary py-1">
<BootstrapButton
variant="link"
onClick={handleClose}
className={`text-white btn-modal-back btn-back-${props.modalClass}`}
>
<span className="fas fa-caret-left" alt="" />
Back
</BootstrapButton>
</Modal.Header>
<Modal.Body>
<H3>You already sent an invite to this email address.</H3>
<p>Would you like to send it again"</p>
{/* This is the button I'm using to test allowing the dupe email
form to be send */}
<Button variant="primary" className="btn-block" onClick={resolvePromise}>
OK
</Button>
</Modal.Body>
</Modal>
<Wrapper key={props.formNumber} {...props}>
{customFormError === false && customFormSuccess === true ? (
<div className="my-1">
<Row>
<Col xs={6} md={3}
className="pr-1 pr-md-2 pb-2 email-sent-container"><span className="email-sent">{recruitRow.recruitFirstName}</span></Col>
<Col xs={6} md={3}
className="pr-1 pr-md-2 pb-2 email-sent-container"><span className="email-sent">{recruitRow.recruitLastName}</span></Col>
<Col md={4}
className="pl-md-2 pb-2 email-sent-container"><span className="email-sent">{recruitRow.recruitEmail}</span></Col>
<Col xs={6} md={2}
className="pb-2">
<Button variant="primary" type="submit" disabled>
Sent!
</Button>
</Col>
</Row>
</div>
) :
(<Formik
{...props}
initialValues={{ recruits: [{firstName: "", lastName: "", emailAddress: ""}] }}
validateOnChange={false}
validateOnBlur={false}
onSubmit={ async (values, { setSubmitting, setFieldError, setStatus }) => {
// this is the array it checks in order to kick bcak option to continue
// or cancel the submissions process
const filterSentMessages = props.sentMessages.filter(user => {
return user.recipients == values['emailAddress'+props.formNumber];
});
console.log("before sleep");
if (filterSentMessages.length > 0) {
handleShow('resend');
}
// this works, but the button click in the modal
// to resolve this is the issue where I get undefined
await resendAttempt();
let recipient = `${values['firstName'+props.formNumber]} ${values['lastName'+props.formNumber]} <${encodeURIComponent(values['emailAddress'+props.formNumber])}>,`;
console.info('recipient:', recipient);
let messageBody = `<p>Dear ${values['firstName'+props.formNumber]},</p><p>... message content ...</p><p>Thank you in advance for your commitment to making an impact!</p><p>${captFirstName}!</p>`
messageBody = encodeURIComponent(messageBody);
let x = 2; // this is here to just stop sending so many test emails
if( x == 1 ) { // this is here to just stop sending so many test emails
try {
props.sendMessage(
localStorage.getItem('frId'),
messageSubject,
messageBody,
recipient,
props.eventConfig.defaultStationeryId,
);
} catch (error) {
console.log('form submit error:', error);
setShowForm(!showForm);
setSubmitting(false);
}
}
setRecruitRow({...recruitRow, recruitFirstName: values['firstName'+props.formNumber], recruitLastName: values['lastName'+props.formNumber], recruitEmail: values['emailAddress'+props.formNumber]})
setCustomFormError(false);
setCustomFormSuccess(true);
setSubmitting(false);
}}
render={({ values,errors }) => (
<Form className="my-1" id={`recruitForm${props.formNumber}`}>
<FieldArray
name="recruits"
render={(arrayHelpers) => (
<>
{(values.recruits && values.recruits.length > 0) && (
values.recruits.map((recruit, index) => (
<Row key={props.formNumber}>
<Col xs={6} md={3}
className="pr-1 pr-md-2 pb-2"
>
<label htmlFor={`firstName${props.formNumber}`} className="sr-only">
First Name
</label>
<Field
className="form-control"
type="text"
name={`firstName${props.formNumber}`}
placeholder="First Name"
id={`firstName${props.formNumber}`}
validate={validateFirstName}
/>
{errors['firstName'+props.formNumber] && <div className="text-danger" role="alert">{errors['firstName'+props.formNumber]}</div>}
</Col>
<Col xs={6} md={3}
className="pl-1 px-md-2 pb-2"
>
<label htmlFor={`lastName${props.formNumber}`} className="sr-only">
Last Name
</label>
<Field
className="form-control"
type="text"
name={`lastName${props.formNumber}`}
placeholder="Last Name"
id={`lastName${props.formNumber}`}
validate={validateLastName}
/>
{errors['lastName'+props.formNumber] && <div className="text-danger" role="alert">{errors['lastName'+props.formNumber]}</div>}
</Col>
<Col md={4}
className="pl-md-2 pb-2"
>
<label htmlFor={`emailAddress${props.formNumber}`} className="sr-only">
Email Address
</label>
<Field
className="form-control"
type="text"
name={`emailAddress${props.formNumber}`}
placeholder="Email Address"
id={`emailAddress${props.formNumber}`}
validate={validateEmail}
/>
{errors['emailAddress'+props.formNumber] && <div className="text-danger" role="alert">{errors['emailAddress'+props.formNumber]}</div>}
</Col>
<Col xs={6} md={2}
className="pb-2"
>
<Button variant="primary" type="submit">
Send
</Button>
</Col>
</Row>
))
)}
</>
)}
/>
</Form>
)}
/>
)}
</Wrapper>
</>
);
};
export default TeamRecruitmentForm;

Related

Retriving data for input radio won't make a default selection

Goal:
Retrieve data 'smartphones', by API, and apply it as a default selection in the input radio component for the react hook form.
Problem:
The code do not work that makes smartphones as a preselection in the input radio button.
However, if I make a change by using hard coding, it works but hard coding do not solve the case.
I don't know how to solve it.
Info:
*Using React TS and React hook form.
*Newbie in react TS and hook form.
Stackblitz:
https://stackblitz.com/edit/react-ts-z9cnzl
Thank you!
import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
import './style.css';
type FormValues = {
lastName: string;
favShow: string;
};
export default function App() {
const [category, setCategory] = useState('');
React.useEffect(() => {
async function FetchData() {
var data = await fetch('https://dummyjson.com/products/1').then((res) => {
return res.json();
});
console.log(data.category);
setCategory(data.category);
}
FetchData();
}, []);
const [data, setData] = useState(null);
const { register, handleSubmit } = useForm<FormValues>({
defaultValues: {
lastName: 'asaaaaaaf',
favShow: category,
//favShow: 'smartphones',
},
});
const onSubmit = (data) => {
setData(data);
console.log('asdf');
};
return (
<React.Fragment>
<form onSubmit={handleSubmit(onSubmit)} className="form-container">
<h1 className="form-heading">React Hook Form Example</h1>
<input
{...register('lastName', { required: true })}
className="form-control"
type="text"
placeholder="Last name"
maxLength={15}
name="lastName"
/>
<br />
<br />
<label htmlFor="ted-lasso">
<input
{...register('favShow', { required: true })}
type="radio"
name="favShow"
value="smartphones"
id="smartphones"
/>{' '}
smartphones
</label>
<label htmlFor="got">
<input
{...register('favShow', { required: true })}
type="radio"
name="favShow"
value="GOT"
id="got"
/>
GOT
</label>
<br />
<br />
<button className="submit-btn" type="submit">
Submit
</button>
</form>
{data && <p className="submit-result">{JSON.stringify(data)}</p>}
</React.Fragment>
);
}
I got some help and the solution is:
Stackblitz:
https://stackblitz.com/edit/react-ts-m2s6ev?file=index.tsx
import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
import './style.css';
type FormValues = {
lastName: string;
favShow: string;
};
export default function App() {
const [category2, setCategory2] = useState();
const [data, setData] = useState(null);
const { register, handleSubmit, resetField } = useForm<FormValues>({
defaultValues: {
lastName: '',
favShow: '',
},
});
React.useEffect(() => {
async function FetchData() {
var data = await fetch('https://dummyjson.com/products/1').then((res) => {
return res.json();
});
setCategory2(data);
}
FetchData();
}, []);
React.useEffect(() => {
if (category2) {
const obj = JSON.parse(JSON.stringify(category2));
console.log(obj);
resetField('lastName', { defaultValue: obj.discountPercentage });
resetField('favShow', { defaultValue: obj.category });
}
}, [resetField, category2]);
const onSubmit = (data) => {
setData(data);
};
return (
<React.Fragment>
<form onSubmit={handleSubmit(onSubmit)} className="form-container">
<h1 className="form-heading">React Hook Form Example</h1>
<input
{...register('lastName', { required: true })}
className="form-control"
type="text"
placeholder="Last name"
maxLength={15}
name="lastName"
/>
<br />
<br />
<label htmlFor="ted-lasso">
<input
{...register('favShow', { required: true })}
type="radio"
name="favShow"
value="smartphones"
id="smartphones"
/>{' '}
smartphones
</label>
<label htmlFor="got">
<input
{...register('favShow', { required: true })}
type="radio"
name="favShow"
value="GOT"
id="got"
/>
GOT
</label>
<br />
<br />
<button className="submit-btn" type="submit">
Submit
</button>
</form>
{data && <p className="submit-result">{JSON.stringify(data)}</p>}
</React.Fragment>
);
}

GraphQL mutation on Form Click not triggering

I am trying to call Apollo Mutation Hook on form submit to create a new record in the database.
export default function NewJobForm() {
const { loading, error, data } = useQuery(GET_LOC_CAT_TYPE_QUERY);
const [newJob] = useMutation(NEW_JOB_MUTATION);
const [remote, setRemote] = useState(false);
const editorRef = useRef(null);
const log = () => {
if (editorRef.current) {
console.log(editorRef.current.getContent());
}
};
let initialFormValues = {
companyName: '',
title: '',
};
const JobSchema = yup.object({
companyName: yup.string().required('Please enter the company name.'),
title: yup.string().required('Please enter the job title.'),
});
let formValidate = (values) => {
console.log(values);
const errors = {};
return errors;
};
let handleError = (field, actions) => {
field.forEach((data) => {
let field = data.param;
switch (field) {
// case 'name':
// actions.setFieldError('name', data.msg);
// break;
default:
break;
}
});
};
let formSubmit = async (values, actions) => {
console.log(values);
newJob({ variables: values });
};
return (
<>
<Formik
validationSchema={JobSchema}
validate={(values) => formValidate(values)}
onSubmit={(values, actions) => formSubmit(values, actions)}
enableReinitialize={true}
initialValues={initialFormValues}
validateOnChange={true}
validateOnBlur={false}
>
{({ handleSubmit, setFieldValue, values }) => (
<form noValidate onSubmit={handleSubmit}>
<div className="space-y-8 divide-y divide-gray-200">
<div>
<div>
<h3 className="text-lg font-medium leading-6 text-gray-900">
Job Details
</h3>
<p className="mt-1 text-sm text-gray-500">
This information will be displayed
publicly so be careful what you share.
</p>
</div>
<div className="grid grid-cols-1 mt-6 gap-y-6 gap-x-4 sm:grid-cols-6">
<div className="sm:col-span-4">
<TextInput
label="Company Name"
id="companyName"
name="companyName"
type="text"
placeholder="eg: Google"
helpText="Please Enter the name of the company"
/>
</div>
<div className="sm:col-span-4">
<TextInput
label="Job Title"
id="title"
name="title"
type="text"
/>
</div>
</div>
</div>
</div>
</form>
)}
</Formik>
</>
);
}
On Form Submit the console.log displays the message but the mutation network call does not seem to get triggered.

Change the options passed to formik (Field) form after selecting the value from other dropdown

Below is the code which I am using. When I am changing the data for Division the state of district list is changed but it is not shown in the form. I need to click on the form and the changed state data is reflected. I want it to get reflected once the state is changed.
function Input(props) {
const { label, name, ...rest } = props
return (
<div>
{/* <label htmlFor={name}>{label}</label> */}
<Field id={name} name={name} {...rest} />
<ErrorMessage name={name} component={TextError}/>
</div>
)
}
function Select(props) {
const { label, name, options, ...rest } = props
return (
<div className="form-group">
{/* <label htmlFor={name}>{label}</label> */}
<Field as='select' id={name} name={name} {...rest} >
{
options.map(option => {
return(
<option key={option.value} value={option.value}>
{option.key}
</option>
)
})
}
</Field>
<ErrorMessage name={name} component={TextError}/>
</div>
)
}
function Test() {
const [divisions, setDivisions] = useState([])
const [districts, setDistricts] =useState([])
const [blocks, setBlocks] = useState([])
const [initialValues,setInitialValues] = useState({
divisionCode:"",
districtCode:"",
blockCode : "",
name : "",
})
const validationSchema = Yup.object({
divisionCode :Yup.string().required('Required!!'),
districtCode :Yup.string().required('Required!!'),
blockCode : Yup.string().required('Required!!'),
name : Yup.string().required('Required!!'),
})
async function getDivisionList() {
try {
const res = await axios.get("APIURL")
return (res.data.response)
} catch (error) {
console.error(error);
}
}
function getDistrictList(divisionCode) {
axios.get("APIURL")
.then(res=>{
setDistricts(res.data.response)
return res.data.response[0].value
})
}
function getBlockList(districtCode) {
axios.get("APIURL")
.then(res => {
setBlocks(res.data.response)
})
}
useEffect(() => {
getDivisionList().then(res => {
setDivisions(res)
});
}, [])
const onSubmit = values => {
console.log('Form data ',values)
console.log('Saved data ',JSON.parse(JSON.stringify(values)))
}
return (
<Formik
initialValues = {initialValues}
validationSchema = {validationSchema}
onSubmit = {onSubmit}
>
{
formik => <Form>
<Select control='select' label='Division' onBlur={e => { var data = getDistrictList(e.currentTarget.value)}} name='divisionCode' options={divisions} />
<Select control='select' label='District' onBlur={e => { getBlockList(e.currentTarget.value)}} name='districtCode' options={districts} />
<Select control='select' label='Block' name='blockCode' options={blocks} />
<Input control='input' type='text' label='Name' name='name' />
<button type="submit" disabled={!formik.isValid}>Submit</button>
</Form>
}
</Formik>
)
}
export default Test
Thanks in Advance. It will be very helpful.

Unable to render redux form select input choices dynamically

I am trying to populate a select menu in redux forms dynamically.
I've been using the debugging tools in chrome and can see that the 'departments' variable sees the array list
({departments.map(department => <option key={department} value={department}>{department}</option>)}
but the final choice list isn't populating. I'm guessing it has something to do with the renderSelectField function, but I'm not sure what I am overlooking?
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import isValidEmail from 'sane-email-validation';
class SimpleReduxForm extends Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
this.renderSelectField = this.renderSelectField.bind(this);
}
onSubmit = async (data) => {
try {
let response = await fetch('/api/getRecords', {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-type': 'application/json'
}
});
let responseJson = await response.json();
//display success message to user
alert('Form successfully submitted')
return responseJson;
//reset form
} catch (error) {
alert(error);
}
}
renderInputField(field) {
return (
<div className="form-group">
<label htmlFor={field.input.name}>{field.label}</label>
<div className="field">
<input placeholder={field.label} {...field.input} className="form-control" type={field.input.type} />
</div>
</div>
)
}
renderSelectField(field) {
return (
<div className="form-group">
<label htmlFor={field.input.name}>{field.label}</label>
<div className="field">
<select {...field.input}
className="form-control"
defaultselection={field.defaultSelection}
><option>{field.defaultselection}</option></select>
</div>
</div>
)
}
render() {
const { handleSubmit, pristine, reset, submitting, invalid } = this.props;
//Options for select - this should be an AJAX call to a table to get options list
const departments = ["Dept 1", "Dept 2", "Dept 3"]
return (
<form onSubmit={handleSubmit(this.onSubmit)}>
<Field
label="Username"
name="username"
component={this.renderInputField}
type="text"
/>
<Field
label="Email"
name="email"
component={this.renderInputField}
type="email"
/>
<Field
label="Age"
name="num_field"
component={this.renderInputField}
type="text"
/>
<Field
label="Department"
name="department"
defaultselection="Select..."
component={this.renderSelectField}>
{departments.map(department => <option key={department} value={department}>{department}</option>)}
</Field>
<div>
<button type="submit" className="btn btn-primary" disabled={pristine || submitting}>Submit</button>
<button type="button" className="btn btn-warning" disabled={pristine || submitting} onClick={reset}> Clear Values </button>
</div>
</form >
)
}
}
//Validate Errors Before Submission
const validate = (values) => {
//create errors object
const errors = {}
/*Example showing to check is a field contains data
* if no, submission == invalid*/
if (!values.username) {
errors.username = 'Required!'
}
/*check to see if email is provided and that submission is an actual email address*/
if (!values.email) {
errors.email = 'Required!'
} else if (!isValidEmail(values.email)) {
errors.email = 'Invalid Email!'
}
/* Example to show that the length of a field
* can be checked as part of validation */
if (values.num_field < 2) {
errors.num_field = "Must be at least 10 years old"
}
return errors
}
const mapStateToProps = state => ({
SimpleReduxForm: state.form.SimpleReduxForm
});
export default reduxForm({
validate,
form: 'SimpleReduxForm',
enableReinitialize: true,
keepDirtyOnReinitialize: true,
})(connect(mapStateToProps)(SimpleReduxForm));
I figured it out. Just in case anyone else runs into this issue. I needed to add {field.children} into the renderSelectField function. So the final function looks like:
renderSelectField(field) {
return (
<div className="form-group">
<label htmlFor={field.input.name}>{field.label}</label>
<select {...field.input}
className="form-control"
defaultselection={field.defaultSelection}
><option>{field.defaultselection}</option>{field.children}</select>
</div>
)
}

How to work with axios.put and react?

I am trying create a simple crud system with react redux and form-redux.
The code below but does not work and gives error.
First I created an action for update and then created a reducer for that.
And then created component to use the action.
Let me know how to get this to work.
//-------------action--------------
export const EDIT_POST = 'EDIT_POST';
export const editPost = (id) => {
const request = axios.put(`${BOOK_URL}/books/${id}`);
return {
type: EDIT_POST,
payload: id,
}
};
//---------------- reducer-----------------
case EDIT_POST: {
return {...state, post: action.payload.data}
}
//----------------route--------------
<Route path='/posts/edit/:id' component={PostEdit}/>
//-------------------PostEdit---------------
class PostEdit extends Component {
componentDidMount = () => {
this.props.editPost(this.props.match.params.id);
console.log(this.props.editPost(this.props.match.params.id));
};
renderField = field => {
const {meta: {touched, error}} = field;
const className = `form-group ${touched && error ? 'has-danger' : ''}`;
return (
<div className='has-danger'>
<label>{field.label}</label>
<input type="text" {...field.input} className="form-control"/>
<div className="text-help">
{touched ? error : ''}
</div>
</div>
);
};
render() {
const {handleSubmit} = this.props;
return (
<form onSubmit={handleSubmit(this.onSubmitForm)}>
<Field name="title" label="Title" component={this.renderField}/>
<Field name="author" label="Author" component={this.renderField}/>
<Field name="description" label="Description" component={this.renderField}/>
<Field name="publicationDate" label="PublicationDate" component={this.renderField}/>
<button type='submit' className="btn btn-primary">Submit</button>
<Link to='/' className='btn btn-danger'>Cancel</Link>
</form>
);
}
}
export default reduxForm({
form :'updateForm'
})(connect(null, {editPost})(PostEdit));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
Could you tell what error you are getting?
You have to change your action like
export const fetchPosts = () => {
return (dispatch) => {
axios.get(`${BOOK_URL}/books`).then((response) => {
return dispatch({
type: FETCH_POSTS,
payload: // your response here
});
})
}
}

Resources