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