nested Delete Function Causes undefined Error - javascript

i've 2 components 1/ parent ----> which pass a delete function to child Component but it causes undefined
TypeError: Cannot read property 'onHandleDelete' of undefined
i tried every thing that react docs provides and there is nothing works for me
the parent Compnent
import React from 'react';
import PropTypes from 'prop-types';
import get from 'lodash/get';
// import isEmpty from 'lodash/isEmpty';
import { connect } from 'react-redux';
import isObject from 'lodash/isObject';
import classnames from 'classnames';
import moment from 'moment-hijri';
import uuid from 'uuid';
// Material-UI
import { withStyles } from '#material-ui/core/styles';
import Grid from '#material-ui/core/Grid';
import Paper from '#material-ui/core/Paper';
import Typography from '#material-ui/core/Typography';
// components
import Actions from './Actions';
import Supervisor from './Supervisor';
// Forms
import Entity from '~/Components/Entity';
import { BaseFormComponent, Form, FormBreak, FormDivider, FormContainer, FormItem } from '~/Components/FormComponent';
import { LabelAndValue } from '~/Components/FormComponent/Controls';
import {
isRequired,
noEnglish,
isObjectRequired,
} from '~/Services/Validators';
// Redux
import OfficesActions from '~/Redux/Actions/Offices';
import { VisitStatus } from '~/Services/StaticLookups';
import Strings from '~/Services/Strings';
import Notifications from '~/Services/Notifications';
import Message from '~/Components/Message';
import Navigate from '~/Services/Navigate';
import styles from './styles';
#withStyles(styles)
class AssignmentsEditorScreen extends BaseFormComponent {
constructor(props) {
super(props);
this.initState({
supervisors: [],
insititution: [],
inistitutionsLookup: [],
selectedSupervisors: [],
previousSupervisorsFromId: [],
plannedVisitInfoFromId: {},
institutionDetails: {},
account: {},
error: false,
form: {
id: {
type: this.types.string,
value: 0,
},
institutionId: {
type: this.types.number,
validators: [ isRequired() ],
},
visitTypeId: {
type: this.types.number,
validators: [ isRequired() ],
},
startDate: {
type: this.types.object,
validators: [ isObjectRequired() ]
},
endDate: {
type: this.types.object,
validators: [ isObjectRequired() ]
},
office: {
type: this.types.string,
UIOnly: true
},
plannedVisitSupervisors: {
type: this.types.string,
UIOnly: true
},
visitStatus: {
type: this.types.string,
UIOnly: true
},
reason: {
type: this.types.string,
validators: [ noEnglish() ],
},
},
});
}
componentDidMount() {
super.componentDidMount();
const id = get(this, 'props.match.params.id', null);
const { offices, account } = this.props;
this.searchableEntity.get();
if (id) {
this.addFormFields({
referenceNumber: {
type: this.types.string,
UIOnly: true
},
});
this.entity.get({ id });
} else {
this.removeFormFields([ 'referenceNumber' ]);
}
if (!offices.isSet) {
this.props.getOffices();
}
this.setState({ account });
}
componentDidUpdate(prevProps) {
if(this.state.account !== this.props.account) {
const employeeOfficeId = this.props.account.myRoles.find(item => item.roleName === 'PTSupervisionEmployees').officeId;
// // To Switch to employee Office Once he Opens The Editor
this.handleOfficesChange(employeeOfficeId);
this.setFieldValue('office', employeeOfficeId);
this.setState({ account: this.props.account });
}
}
onSubmit() {
const id = get(this, 'props.match.params.id', null);
const { selectedSupervisors, previousSupervisorsFromId } = this.state;
let plannedVisitors = [];
if(selectedSupervisors.length > 0) {
selectedSupervisors.map(item => {
plannedVisitors.push({ supervisorName: item.name });
});
} else {
plannedVisitors = [];
}
// To Append The Previous Supervisors with the newest supervisors
if(previousSupervisorsFromId.length > 0) {
previousSupervisorsFromId.map(item => {
plannedVisitors.push({ supervisorName: item.supervisorName });
});
}
if (this.isFormValid) {
this.entity.post({
...this.formValues,
plannedVisitSupervisors: [ ...plannedVisitors ],
// to set id = 0 --> in create Mode || id Value in update Mode
id: id ? id : 0
});
} else {
console.log('this', this.formValues,);
this.showFormErrors();
}
}
onEntityPosted() {
const id = get(this, 'props.match.params.id', null);
if(id) {
Notifications.notify('success', Strings.assignmentUpdatedSuccessfuly);
} else {
Notifications.notify('success', Strings.assignmentSavedSuccessfully);
}
Navigate.go('/plannedvisits');
}
onEntityPostedError(data) {
if(data === 'Failed to create visit!, another visit is on for the institution!') {
Notifications.notify('error', Strings.duplicatPlannedVisits);
this.setState({ error: true });
}
}
onEntityReceived(data) {
const previousSupervisorsFromId = [ ...data.plannedVisitSupervisors ];
const plannedVisitInfoFromId = { ...data };
if(isObject(data)) {
this.updateFormValues(data, () => {
if(data.hasOwnProperty('visitReasonId')) {
this.addFormFields({
visitReasonId: {
type: this.types.number,
validators: [ isRequired() ],
value: data.visitReasonId,
}
});
}
});
}
this.setState({
previousSupervisorsFromId,
plannedVisitInfoFromId
});
}
onSearchableEntityReceived(data) {
if(data && data.licensingInstitutionsModel) {
const insititution = [ ...data.licensingInstitutionsModel ];
if (data.licensingInstitutionsModel && data.licensingInstitutionsModel.length > 0) {
{
data.licensingInstitutionsModel.map(item => {
this.setState(prevState => ({
inistitutionsLookup: prevState.inistitutionsLookup.concat({
id: item.institutionId,
nameAr: item.fullName
}),
insititution
}));
});
}
}
}
}
onSupervisorsByOfficeEntityReceived(data) {
this.setState({ supervisors: data.result });
}
handleOfficesChange(officeId) {
this.supervisorsByOfficeEntity.get({ officeId });
}
onHandleDelete(member) {
console.log('member', member);
}
x(m) {
console.log('member');
}
handleReasonField(id) {
if(id === 1) {
this.addFormFields({
visitReasonId: {
type: this.types.string,
validators: [ isRequired() ]
},
});
} else {
this.removeFormFields([ 'visitReasonId' ]);
}
}
getInstitutionValues(institutionId) {
const { insititution } = this.state;
if(insititution && insititution.length > 0) {
const institutionDetails = insititution.filter(item => item.institutionId === institutionId);
this.setState({ institutionDetails });
}
}
handleAddMember(value) {
const member = this.state.supervisors.find(item => item.name === value);
if(member) {
// in order not to allow add multiple supervisors withe the same name
const isInPreviousSupervisors = this.state.previousSupervisorsFromId.find(item => item.supervisorName === member.name);
if(!isInPreviousSupervisors) {
const selectedSupervisors = [ ...this.state.selectedSupervisors ];
selectedSupervisors.push(member);
this.setState({ selectedSupervisors });
} else {
Notifications.notify('error', `${member.displayName} ${Strings.userAlreadyInTheList} ${member.name}`);
}
}
}
get offices() {
const { offices } = this.props;
if(offices.list.length >= 1) {
const lookup = {};
offices.list.forEach(item => {
lookup[item.id] = item.name;
});
return lookup;
}
return {};
}
get plannedVisitsSupervisors() {
const lookup = {};
this.state.supervisors.forEach(item => {
const isSelectedBefore = this.state.selectedSupervisors.find(sItem => sItem.name === item.name);
if(!isSelectedBefore) {
lookup[item.name] = item.displayName;
}
});
return lookup;
}
get getSupervisors() {
const { selectedSupervisors, previousSupervisorsFromId } = this.state;
return selectedSupervisors.concat(previousSupervisorsFromId);
}
render() {
// const { form, inistitutions, supervisorsByOffice, previousSupervisorsFromId } = this.state;
const { form, error, inistitutionsLookup, plannedVisitInfoFromId } = this.state;
const { classes } = this.props;
const id = get(this, 'props.match.params.id', null);
const inistitutionsCheck = inistitutionsLookup && inistitutionsLookup.length === 0;
// const supervisorsCheck = supervisorsByOffice && supervisorsByOffice.length === 0;
const disabled = plannedVisitInfoFromId && plannedVisitInfoFromId.plannedVisitActions && !plannedVisitInfoFromId.plannedVisitActions.allowEdit;
const {
TextField,
LookupSelectField,
StaticLookupSelectField,
SelectAutocompleteField,
Date2Field,
} = this;
return (
<React.Fragment>
<Entity
storeId={'Supervision-PlannedVisit-Editor'}
entityRef={ref => { this.entity = ref; }}
onEntityReceived={data => this.onEntityReceived(data)}
onEntityPosted={data => this.onEntityPosted(data)}
onEntityPostedError={data => this.onEntityPostedError(data)}
render={store => (
<Form loading={store.loading}>
<Grid container spacing={24}>
<If condition={error}>
<Grid item xs={12}>
<Message variant={'error'} text={Strings.duplicatPlannedVisits} />
</Grid>
</If>
<Grid item xs={9}>
<Paper elevation={1} className={classes.box1}>
<fieldset className={classes.fieldSet}>
<legend>{Strings.assignmentDetails}</legend>
<FormContainer>
<FormItem lg={6}>
<div className={classnames(classes.placeholder, id || inistitutionsCheck ? classes.disabledField : {})}>
<SelectAutocompleteField
name={'institutionId'}
label={Strings.assignmentInstitutionName}
emptyString={Strings.searchByNameAndLicense}
disabled={id || inistitutionsCheck}
data={inistitutionsLookup}
onChange={field => this.getInstitutionValues(field.value)}
// onSearch={e => console.log('e', e)}
/>
</div>
</FormItem>
<FormItem lg={form.visitTypeId.value === 1 ? 3 : 6}>
<LookupSelectField
name={'visitTypeId'}
label={Strings.assignmentvisitType}
lookup={'VisitType'}
onChange={field => this.handleReasonField(field.value)}
disabled={disabled}
/>
</FormItem>
<If condition={form.visitTypeId.value === 1 && form.visitReasonId}>
<FormItem lg={3}>
<LookupSelectField
name={'visitReasonId'}
label={Strings.assignmentvisitReason}
lookup={'VisitReason'}
disabled={disabled}
/>
</FormItem>
</If>
<FormItem lg={6}>
<Choose>
<When condition={disabled}>
<LabelAndValue
label={Strings.assignmentvisitStartDate}
value={plannedVisitInfoFromId.startDate.hijriDate}
/>
</When>
<Otherwise>
<Date2Field
name={'startDate'}
label={Strings.assignmentvisitStartDate}
minDate={[
moment().iYear(),
moment().iMonth() + 1,
moment().iDate()
]}
/>
</Otherwise>
</Choose>
</FormItem>
<FormItem lg={6}>
<Choose>
<When condition={disabled}>
<LabelAndValue
label={Strings.assignmentvisitEndDate}
value={plannedVisitInfoFromId.endDate.hijriDate}
/>
</When>
<Otherwise>
<Date2Field
name={'endDate'}
label={Strings.assignmentvisitEndDate}
minDate={[
moment().iYear(),
moment().iMonth() + 1,
moment().iDate()
]}
/>
</Otherwise>
</Choose>
</FormItem>
</FormContainer>
</fieldset>
</Paper>
<Paper elevation={1} className={classes.box}>
<fieldset className={classes.fieldSet}>
<legend>
<Choose>
<When condition={disabled}>
{Strings.assignedSupervisors}
</When>
<Otherwise>
{Strings.addSupervisors}
</Otherwise>
</Choose>
</legend>
<FormContainer className={classes.supervisorsContainer}>
<If condition={!disabled}>
<FormItem>
<div className={ classes.lookup }>
<StaticLookupSelectField
name= {'office'}
label={Strings.office}
lookup= {this.offices}
onChange={field => this.handleOfficesChange(field.value)}
/>
</div>
</FormItem>
<FormItem>
<StaticLookupSelectField
name={'plannedVisitSupervisors'}
label={Strings.supervisorListName}
lookup={this.plannedVisitsSupervisors}
onChange={field => this.handleAddMember(field.value)}
/>
</FormItem>
</If>
<If condition={this.getSupervisors.length > 0}>
<If condition={!disabled}>
<FormBreak />
<FormDivider />
<Typography variant='subtitle1' className={classes.supervisorsHeader}>
{Strings.assignedSupervisors}
</Typography>
</If>
<FormContainer className={classes.supervisorsContainer}>
<For each='member' of={this.getSupervisors} index='i'>
<FormItem lg={4} key={`${i}_${uuid()}`}>
<Supervisor
data={member}
onHandleDelete={data => this.onHandleDelete(data)}
disabled={disabled}
/>
</FormItem>
</For>
</FormContainer>
</If>
</FormContainer>
</fieldset>
</Paper>
<Paper elevation={1} className={classes.box}>
<FormItem fullWidth>
<TextField
multiline
name={'reason'}
label={Strings.assignmentvisitreason}
disabled={disabled}
/>
</FormItem>
</Paper>
</Grid>
<Grid item xs={3}>
{/* =========== Sidebar ============= */}
<If condition={form && form.visitStatus}>
<Paper elevation={1} className={classes.box}>
{/* lookup is Needed */}
<FormItem fullWidth>
<StaticLookupSelectField
name={'visitStatus'}
label={Strings.assignmentvisitStatus}
lookup={VisitStatus}
disabled
/>
</FormItem>
</Paper>
</If>
<If condition={form && form.referenceNumber}>
<Paper elevation={1} className={classes.box}>
<FormItem fullWidth>
<TextField
name={'referenceNumber'}
label={Strings.violationReferenceNumber}
disabled
/>
</FormItem>
</Paper>
</If>
<Paper elevation={1} className={classes.box}>
<FormItem fullWidth style={{ marginBottom: 10 }}>
<Actions disabled={disabled} onSubmit={() => this.onSubmit()} />
</FormItem>
</Paper>
</Grid>
</Grid>
</Form>
)}
/>
{/* To get all institutions */}
<Entity
storeId={'Supervision-Assignment-Searchable-Institution'}
entityRef={ref => { this.searchableEntity = ref;}}
onEntityReceived={data => this.onSearchableEntityReceived(data)}
/>
{/* To get supervisors by office */}
<Entity
storeId={'Supervision-Assignment-Supervisors-By-Office'}
entityRef={ref => { this.supervisorsByOfficeEntity = ref;}}
onEntityReceived={data => this.onSupervisorsByOfficeEntityReceived(data)}
/>
</React.Fragment>
);
}
}
AssignmentsEditorScreen.propTypes = {
classes: PropTypes.object,
offices: PropTypes.object,
account: PropTypes.object,
getOffices: PropTypes.func
};
const mapStateToProps = store => ({
offices: store.offices,
account: store.account.data || {}
});
const mapDispatchToProps = dispatch => ({
getOffices: () => dispatch(OfficesActions.get()),
});
AssignmentsEditorScreen.propTypes = {
classes: PropTypes.object,
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(AssignmentsEditorScreen);
the child Component
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import Typography from '#material-ui/core/Typography';
import Paper from '#material-ui/core/Paper';
import Icon from '#material-ui/core/Icon';
import IconButton from '#material-ui/core/IconButton';
import DeleteIcon from '#material-ui/icons/Delete';
import Tooltip from '#material-ui/core/Tooltip';
import strings from '~/Services/Strings';
import { supervisor } from './styles';
#withStyles(supervisor)
class Supervisor extends Component {
render() {
const { classes, data, disabled } = this.props;
console.log('da', data);
return (
<Paper className={classes.memberItem}>
<If condition={!disabled}>
<Tooltip title={strings.delete}>
<IconButton className={classes.delete} onClick={() => this.props.onHandleDelete(data)}>
<DeleteIcon />
</IconButton>
</Tooltip>
</If>
<div className={classes.memberIconWrapper}>
<Icon className={classes.memberIcon}>person</Icon>
</div>
<div className={classes.memberDetails}>
<div className={classes.subInfo}>
<Typography gutterBottom className={classes.memberJobTitle} variant='subtitle1'>
{strings.supervisorNameSub}
</Typography>
<Typography className={classes.memberName} variant='subtitle2'>
<Choose>
<When condition={data.displayName}>
{data.displayName}
</When>
<Otherwise>
<Choose>
<When condition={data.name}>
{data.name}
</When>
<Otherwise>
<Choose>
<When condition={data.supervisorName}>
{data.supervisorName}
</When>
<Otherwise>
{strings.emptyString}
</Otherwise>
</Choose>
</Otherwise>
</Choose>
</Otherwise>
</Choose>
</Typography>
</div>
<div className={classes.subInfo}>
<Typography gutterBottom className={classes.memberJobTitle} variant='subtitle1'>
{strings.assignedVisitCountSub}
</Typography>
<Typography className={classes.memberName} variant='subtitle2'>
<Choose>
<When condition={typeof(data.assignedVisitCount) === 'number'}>
{data.assignedVisitCount}
</When>
<Otherwise>
{strings.emptyString}
</Otherwise>
</Choose>
</Typography>
</div>
<div className={classes.subInfo}>
<Typography gutterBottom className={classes.memberJobTitle} variant='subtitle1'>
{strings.requiredVisitsCountPerMonthSub}
</Typography>
<Typography className={classes.memberName} variant='subtitle2'>
<Choose>
<When condition={typeof(data.requiredVisitsCountPerMonth) === 'number'}>
{data.requiredVisitsCountPerMonth}
</When>
<Otherwise>
{strings.emptyString}
</Otherwise>
</Choose>
</Typography>
</div>
</div>
</Paper>
);
}
}
Supervisor.propTypes = {
classes: PropTypes.object,
data: PropTypes.object,
disabled: PropTypes.bool,
key: PropTypes.string,
onHandleDelete: PropTypes.func,
};
export default Supervisor;
here is my parent Component
<For each='member' of={this.getSupervisors} index='i'>
<FormItem lg={4} key={`${i}_${uuid()}`}>
<Supervisor
data={member}
onHandleDelete={data => this.onHandleDelete(data)}
disabled={disabled}
/>
</FormItem>
</For>
here is onHandleDelete Function
onHandleDelete(data) {
console.log('member');
}
here is my Child Component(Supervisor)
<If condition={!disabled}>
<Tooltip title={strings.delete}>
<IconButton className={classes.delete} onClick={() => this.props.onHandleDelete(data)}>
<DeleteIcon />
</IconButton>
</Tooltip>
</If>
this is the error from console
Uncaught TypeError: Cannot read property 'onHandleDeletion' of undefined
at Object.onHandleDeletion (index.js:497)
at onClick (Supervisor.js:24)
at HTMLUnknownElement.callCallback (react-dom.development.js:100)
at Object.invokeGuardedCallbackDev (react-dom.development.js:138)
at Object.invokeGuardedCallback (react-dom.development.js:187)
at Object.invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:201)
at executeDispatch (react-dom.development.js:461)
at executeDispatchesInOrder (react-dom.development.js:483)
at executeDispatchesAndRelease (react-dom.development.js:581)
at executeDispatchesAndReleaseTopLevel (react-dom.development.js:592)
at forEachAccumulated (react-dom.development.js:562)
at runEventsInBatch (react-dom.development.js:723)
at runExtractedEventsInBatch (react-dom.development.js:732)
at handleTopLevel (react-dom.development.js:4476)
at batchedUpdates$1 (react-dom.development.js:16659)
at batchedUpdates (react-dom.development.js:2131)
at dispatchEvent (react-dom.development.js:4555)
at interactiveUpdates$1 (react-dom.development.js:16714)
at interactiveUpdates (react-dom.development.js:2150)
at dispatchInteractiveEvent (react-dom.development.js:4532)
where data is an object that for sure it carries data
i expect logging data in the console

Related

React Material-ui Dialog "open" is not working

I want to make function that show Dialog when I click fab.
but it doesn't work...
I think "handleDialogToggle()" or State is problem.
import React from "react";
import Card from "#material-ui/core/Card";
import CardContent from "#material-ui/core/CardContent";
import Typography from "#material-ui/core/Typography";
import { withStyles } from '#material-ui/core/styles';
import Grid from "#material-ui/core/Grid";
import Button from "#material-ui/core/Button";
import Fab from "#material-ui/core/Fab";
import AddIcon from '#material-ui/icons/Add';
import Dialog from "#material-ui/core/Dialog";
import DialogActions from "#material-ui/core/DialogActions";
import DialogContent from "#material-ui/core/DialogContent";
import DialogTitle from "#material-ui/core/DialogTitle";
import TextField from "#material-ui/core/TextField";
const styles = theme => ({
fab: {
position: 'fixed',
bottom: '20px',
right: '20px'
}
});
const databaseURL = "https://my-app-10a33-default-rtdb.firebaseio.com";
class Word extends React.Component {
constructor(props) {
super(props);
this.state = {
words: {},
dialog: false,
word: '',
weight: ''
};
this.handleDialogToggle = this.handleDialogToggle.bind(this);
}
_get() {
fetch(`${databaseURL}/words.json`).then(res => {
if (res.status !== 200) {
throw new Error(res.statusText);
}
return res.json();
}).then(words => this.setState({ words: words }));
}
_post(word) {
return fetch(`${databaseURL}/words.json`, {
method: 'POST',
body: JSON.stringify(word)
}).then(res => {
if (res.status !== 200) {
throw new Error(res.statusText);
}
return res.json();
}).then(data => {
let nextState = this.state.words;
nextState[data.name] = word;
this.setState({ words: nextState });
});
}
_delete(id) {
return fetch(`${databaseURL}/words/${id}.json`, {
method: 'DELETE'
}).then(res => {
if (res.status !== 200) {
throw new Error(res.statusText);
}
return res.json();
}).then(() => {
let nextState = this.state.words;
delete nextState[id];
this.setState({ words: nextState });
})
}
shouldComponentUpdate(nextProps, nextState) {
return nextState.words !== this.state.words;
}
componentDidMount() {
this._get();
}
handleDialogToggle = () => {
this.setState({
dialog: !this.state.dialog
});
console.log(this.state)
}
handleValueChange = (e) => {
let nextState = {};
nextState[e.target.name] = e.target.value;
this.setState(nextState);
}
handleSubmit = () => {
const word = {
word: this.state.word,
weight: this.state.weight
}
this.handleDialogToggle();
if (!word.word && !word.weight) {
return;
}
this._post(word);
}
handleDelete = (id) => {
this._delete(id)
}
render() {
const { classes } = this.props;
return (
<div className="App">
{Object.keys(this.state.words).map(id => {
const word = this.state.words[id];
return (
<div key={id}>
<Card>
<CardContent>
<Typography color="textSecondary" gutterBottom>
가중치: {word.weight}
</Typography>
<Grid container>
<Grid item xs={6}>
<Typography variant="h5" component="h2">
{word.word}
</Typography>
</Grid>
<Grid item xs={6}>
<Button variant="contained" color="primary" onClick={() => this.handleDelete(id)}>삭제</Button>
</Grid>
</Grid>
</CardContent>
</Card>
</div>
);
})}
<Fab color="primary" className={classes.fab} onClick={this.handleDialogToggle.bind(this)}>
<AddIcon />
</Fab>
<Dialog open={this.state.dialog} onClose={this.handleDialogToggle} >
<DialogTitle>
단어 추가
</DialogTitle>
<DialogContent>
<TextField label="단어" type="text" name="word" value={this.state.word} onChange={this.handleValueChange} /><br />
<TextField label="가중치" type="text" name="weight" value={this.state.weight} onChange={this.handleValueChange} /><br />
</DialogContent>
<DialogActions>
<Button variant="contained" color="primary" onClick={this.handleSubmit}>추가</Button>
<Button variant="outlined" color="primary" onClick={this.handleDialogToggle}>닫기</Button>
</DialogActions>
</Dialog>
</div>
);
}
}
export default withStyles(styles)(Word);
enter image description here
when I click Fab, state.dialog is changed.
but Dialog doesn't work.
if I write 'dialog: true' in constructor, Dialog is showed.
enter image description here
How can I show the Dialog when I click Fab?
Thank you for answer!

Invalid value for createPaymentMethod: card should be an object or element

I'm trying to create a payment method using nextJs library from stripe, like this:
import React, { FunctionComponent } from 'react';
import type { DisplaySettingsProps } from '#company/frontoffice/types';
import { Row, Col, Input } from '#company/ui';
import { useIntl } from '#company/frontoffice/providers';
import messages from './payment.lang';
import { loadStripe } from '#stripe/stripe-js';
import {
Elements,
useStripe,
useElements,
CardElement,
} from '#stripe/react-stripe-js';
/* Make sure to call `loadStripe` outside of a component’s render to avoid. recreating the `Stripe` object on every render. */
const stripePromise = loadStripe(
'pk_test_51JzJxAGAsaImrqUbu1pKDHiCSaq9A0sZTgpV6Li3sd8yYrCCVvPhCPfZ3i9JJ8ySgY5Uhceu6iGxlgylx2vuXBj100SpHqClA8'
);
const PaymentForm: FunctionComponent = () => {
const stripe = useStripe();
const elements = useElements();
const f = useIntl();
const handleSubmit = async (e) => {
e.preventDefault();
console.log('inside handleSubmit');
if (!stripe || !elements) {
console.log('!stripe || !elements');
return;
}
const cardElement = elements.getElement(CardElement);
/*
Returns:
result.paymentMethod: a PaymentMethod was created successfully.
result.error: there was an error.
*/
const { paymentMethod, error: backendError } = await stripe.createPaymentMethod({
type: 'card',
card: cardElement,
billing_details: {
name: 'Jenny Rosen',
},
});
if (backendError) {
console.error(backendError.message);
return;
}
};
return (
<Elements stripe={stripePromise}>
<form
onSubmit={(e) => {
console.log('handleSubmit');
handleSubmit(e);
}}
>
<Row mb={4}>
<Col col={12}>
<Input label={f(messages.name)} placeholder={f(messages.name)} required={true} />
</Col>
</Row>
<Row>
<Col>
<CardElement />
</Col>
</Row>
<Row>
<button>[submit /]</button>
</Row>
</form>
</Elements>
);
};
export type StripePaymentFormProps = DisplaySettingsProps & {
displaySettings: any /* TODO */;
};
const StripePaymentForm: FunctionComponent<StripePaymentFormProps> = () => {
return (
<Elements stripe={stripePromise}>
<PaymentForm />
</Elements>
);
};
export default StripePaymentForm;
Which prints out this form:
But when I hit the submit button I see this error in the developer's console:
v3:1 Uncaught (in promise) IntegrationError: Invalid value for createPaymentMethod: card should be an object or element. You specified: null.
And If I console.log(cardElement) prints out null
Any idea why?
-EDIT-
I'm experiencing the same if I split the CardElement into CardNumber, CardExpiry and CardCVC (Which is actually the way I need it)
/* global fetch */
import React, { FunctionComponent } from 'react';
import type { DisplaySettingsProps } from '#company/frontoffice/types';
import { Row, Col, Text, Image, Input } from '#company/ui';
import { StyledInput } from './styledPayment';
import StripeInput from './Stripeinput';
import messages from './payment.lang';
import { useIntl } from '#company/frontoffice/providers';
import { loadStripe } from '#stripe/stripe-js';
import {
Elements,
useStripe,
useElements,
CardNumberElement,
CardCvcElement,
CardExpiryElement,
} from '#stripe/react-stripe-js';
/* Make sure to call `loadStripe` outside of a component’s render to avoid. recreating the `Stripe` object on every render. */
const stripePromise = loadStripe(
'pk_test_51JzJxAGAsaImrqUbu1pKDHiCSaq9A0sZTgpV6Li3sd8yYrCCVvPhCPfZ3i9JJ8ySgY5Uhceu6iGxlgylx2vuXBj100SpHqClA8'
);
const PaymentForm: FunctionComponent = () => {
const stripe = useStripe();
const elements = useElements();
const f = useIntl();
const handleSubmit = async (e) => {
e.preventDefault();
console.log('inside handleSubmit');
if (!stripe || !elements) {
console.log('!stripe || !elements');
return;
}
// what we had
const cardNumber = elements.getElement(CardNumberElement);
const cardCVC = elements.getElement(CardCvcElement);
const cardExpiry = elements.getElement(CardExpiryElement);
console.log(cardNumber, cardCVC, cardExpiry);
const { paymentMethod, error: backendError } = await stripe.createPaymentMethod({
type: 'card',
card: cardNumber,
billing_details: {
name: 'Jenny Rosen',
},
});
/* Create payment intent on server */
const { error: backendError_, clientSecret } = await fetch('/create-payment-intent', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
paymentMethodType: 'card',
currency: 'eur',
}),
}).then((r) => r.json());
if (backendError) {
console.error(backendError.message);
return;
}
if (backendError_) {
console.error(backendError_.message);
return;
}
console.log(`clientSecret generated: ${clientSecret}`);
// Confirm de payment on the client
const { error: stripeError, paymentIntent, error } = await stripe.confirmCardPayment(
clientSecret,
{
payment_method: {
card: cardNumber,
},
}
);
if (stripeError) {
console.error(stripeError.message);
return;
}
console.log(`PaymentIntent ${paymentIntent.id} is ${paymentIntent.status}`);
// what we had
console.log(cardExpiry);
};
return (
<Elements stripe={stripePromise}>
<form
onSubmit={(e) => {
handleSubmit(e);
}}
>
<Row mb={4}>
<Col col={12}>
<Input label={f(messages.name)} placeholder={f(messages.name)} required={true} />
</Col>
</Row>
<Row mb={4}>
<Col col={12}>
<StyledInput
label={f(messages.number)}
placeholder={f(messages.number)}
required={true}
InputProps={{
inputComponent: StripeInput,
inputProps: {
component: CardNumberElement,
},
}}
/>
</Col>
</Row>
<Row colGap={5} mb={4}>
<Col col={[12, 6]}>
<StyledInput
label={f(messages.expiry)}
placeholder={f(messages.expiry)}
required={true}
InputProps={{
inputComponent: StripeInput,
inputProps: {
component: CardExpiryElement,
},
}}
/>
</Col>
<Col col={[10, 5]}>
<StyledInput
label={f(messages.security)}
placeholder="CVC"
required={true}
InputProps={{
inputComponent: StripeInput,
inputProps: {
component: CardCvcElement,
},
}}
/>
<Text as="p" fontSize="xs" color="text2" mt={2}>
{f(messages.securitySub)}
</Text>
</Col>
<Col col={[2, 1]}>
{/* Todo - replace the scr when we receive from design (already requested) */}
<Image
mt={[6, 6]}
display="block"
mx="auto"
width="size10"
maxWidth="sizeFull"
src="card-cvc.png"
alt="Credit card cvc icon"
/>
</Col>
</Row>
<Row>
<button>[submit /]</button>
</Row>
</form>
</Elements>
);
};
export type StripePaymentFormProps = DisplaySettingsProps & {
displaySettings: any /* TODO */;
};
const StripePaymentForm: FunctionComponent<StripePaymentFormProps> = () => {
return (
<Elements stripe={stripePromise}>
<PaymentForm />
</Elements>
);
};
export default StripePaymentForm;
Providing this form:
Beeing all 3 console.log's null
And the stripeinput: (just to style them)
import React, { FunctionComponent, useRef, useImperativeHandle } from 'react';
type StripeInputPrpps = {
component: any; // todo type this
inputRef: any; // todo type this
props: any; // todo type this
};
const StripeInput: FunctionComponent<StripeInputPrpps> = ({
component: Component,
inputRef,
...props
}) => {
const elementRef = useRef();
/* Todo - obtain this values from theme.props */
const color = 'rgb(19, 40, 72)';
const placeholderColor = 'rgb(137, 147, 164)';
const inputStyle = {
color: color,
'::placeholder': {
color: placeholderColor,
},
};
useImperativeHandle(inputRef, () => ({
focus: () => {
if (elementRef && elementRef.current) {
elementRef.current.focus;
}
},
}));
return (
<Component
onReady={(element) => (elementRef.current = element)}
options={{
style: {
base: inputStyle,
},
}}
{...props}
/>
);
};
export default StripeInput;
Per #Jonatan-steele suggestion I deleted the duplicated Elements provider (the one in PaymentForm component) and it works just fine, the createPaymentMethod returns the paymentMethod as described in the docs.
return (
<form
onSubmit={(e) => {
handleSubmit(e);
}}
>
<Row mb={4}>
<Col col={12}>
<Input label={f(messages.name)} placeholder={f(messages.name)} required={true} />
</Col>
</Row>
<Row mb={4}>
<Col col={12}>
<StyledInput
label={f(messages.number)}
placeholder={f(messages.number)}
required={true}
InputProps={{
inputComponent: StripeInput,
inputProps: {
component: CardNumberElement,
},
}}
/>
</Col>
</Row>
<Row colGap={[0, 5]} mb={4}>
<Col col={[12, 6]}>
<StyledInput
label={f(messages.expiry)}
placeholder={f(messages.expiry)}
required={true}
InputProps={{
inputComponent: StripeInput,
inputProps: {
component: CardExpiryElement,
},
}}
/>
</Col>
<Col col={[10, 5]}>
<StyledInput
label={f(messages.security)}
placeholder="CVC"
required={true}
InputProps={{
inputComponent: StripeInput,
inputProps: {
component: CardCvcElement,
},
}}
/>
<Text as="p" fontSize="xs" color="text2" mt={2}>
{f(messages.securitySub)}
</Text>
</Col>
<Col col={[2, 1]}></Col>
</Row>
<Row>
<button>[submit /]</button>
</Row>
</form>
);
In my case, i had a loader replacing the payment component once payment button was pressed which disrupted the stripe flow! i removed the loader , then it worked fine.
2hrs wasted successfully
You should use parameter following below:
{
type: 'card'
card: elements.getElement(CardElement),
}

React error Objects are not valid as a React child (found: object with keys {$$typeof, type, compare)

I got a legacy project of Meteor and React and I try to make it work. But I got an error at one point. Can anyone help me to resolve following error?
Uncaught Error: Objects are not valid as a React child (found: object with keys {$$typeof, type, compare}). If you meant to render a collection of children, use an array instead.
in Unknown
in main (created by Basic)
in Basic (created by Context.Consumer)
in Content (created by PageLayout)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Layout (created by PageLayout)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Layout (created by PageLayout)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Layout (created by PageLayout)
in LocaleProvider (created by Context.Consumer)
in LocaleReceiver (created by ConfigProvider)
in ConfigProvider (created by PageLayout)
in PageLayout (created by ForwardRef)
in ForwardRef
in ForwardRef
at throwOnInvalidObjectType (modules.js:64084)
at reconcileChildFibers (modules.js:64984)
at reconcileChildren (modules.js:67433)
at mountIndeterminateComponent (modules.js:68213)
at beginWork (modules.js:69267)
at HTMLUnknownElement.callCallback (modules.js:50859)
at Object.invokeGuardedCallbackDev (modules.js:50908)
at invokeGuardedCallback (modules.js:50963)
at beginWork$1 (modules.js:73874)
at performUnitOfWork (modules.js:72828)
Within the secure.js the PostCategories are mounted like this
ContentRouter.route('/content-creator', {
name: 'Content Creator',
icon: 'solution',
permission: 'main-navigation.view.module.posts',
className: 'posts',
visible: true,
action: () => {
mount(PageLayout, { content: <PostCategories /> });
},
});
The PageLayout looks like this :
import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
import React, { Component } from 'react';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import Konami from 'react-konami-code';
import deDE from 'antd/lib/locale-provider/de_DE';
import moment from 'moment';
import 'moment/locale/de';
import {
ConfigProvider, Layout, Menu, Icon, Steps, Badge, Button, message,
} from 'antd';
import { Workspaces } from '../../../api/workspaces/collection';
import DoneStepper from './components/doneStepper';
import { DASHBOARD_VERSION } from '../../../helpers/Constants';
const {
Header, Content, Sider, Footer,
} = Layout;
moment.locale('de');
class PageLayout extends Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
logout = () => {
Meteor.logout(() => {
FlowRouter.go('/anmelden');
});
};
renderMenuItems = () => {
const groupName = FlowRouter.current().route.group.name;
const items = FlowRouter._routes.map((route) => {
const isSecure = route && route.group && route.group.parent && route.group.parent && route.group.parent.name === 'secure';
const isVisible = route && route.options && route.options.visible && route.options.visible === true;
const isActive = groupName === route.group.name;
let active = null;
if (isActive) {
active = 'ant-menu-item-selected';
}
return (
isVisible
&& isSecure && (
<Menu.Item key={route.path} style={route.options.style} className={[route.options.className, active]}>
<a href={route.path}>
<Icon type={route.options.icon} />
<span className="nav-text">{route.name}</span>
</a>
</Menu.Item>
)
);
});
return items;
};
easterEgg = () => {
message.success('Development Mode enabled');
};
handleResetWorkspace = () => {
const { user } = this.props;
Meteor.call('workspace.reset', user.profile.workspace, (error) => {
if (error) {
console.log(error);
message.error('error');
} else {
message.success('success');
}
});
};
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true, error: error };
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
console.log(error, errorInfo);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
console.log('ERROR IS:');
console.log(this.state.error);
return <h1>Something went wrong.</h1>;
}
const { ready, user, workspace, content } = this.props;
if (ready) {
if (workspace && workspace.appName && workspace.appName.name) {
document.title = `App - ${workspace.appName.name}`;
} else {
document.title = 'App';
}
}
return (
<ConfigProvider locale={deDE}>
<Layout className="manager-dashboard">
<Header className="header">
<div className="logo__area">
<a href="/">
<img className="logo" src="/logo.svg" alt="cms" />
</a>
</div>
<DoneStepper />
<Konami action={this.easterEgg}>
<Button type="primary" onClick={() => this.handleResetWorkspace()}>
<Icon type="delete" size="small" />
{'reset workspace'}
</Button>
</Konami>
<div className="member">
<a onClick={this.logout}>
{'Abmelden '}
<Icon type="logout" />
</a>
</div>
</Header>
<Layout>
<Sider width={200} style={{ background: '#fff' }}>
<div className="fixed-sidebar">
<Menu
defaultSelectedKeys={[FlowRouter.current().route.path]}
selectedKeys={[FlowRouter.current().route.path]}
>
{this.renderMenuItems()}
</Menu>
</div>
</Sider>
<Layout className="dashboard__content">
<Content
style={{
background: '#fff',
padding: 20,
minHeight: '100vh',
}}
>
{content}
</Content>
<Footer>{`App - Version ${DASHBOARD_VERSION}`}</Footer>
</Layout>
</Layout>
</Layout>
</ConfigProvider>
);
}
}
export default withTracker(() => {
const user = Meteor.user();
const handleWorkspace = Meteor.subscribe('workspaces.one', {
query: { _id: user.profile.workspace },
fields: { appName: 1 },
});
const ready = handleWorkspace.ready();
let workspace = null;
if (ready) {
workspace = Workspaces.findOne({ _id: user.profile.workspace }, { fields: { appName: 1 } });
}
return {
ready,
user,
workspace,
};
})(PageLayout);
And the PostCategories look like this:
/* eslint-disable jsx-a11y/click-events-have-key-events */
import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import { PostCategories } from '/imports/api/posts/collection';
import React, { Component } from 'react';
import Role from '/imports/helpers/Role';
import CreatePostCategory from '/imports/ui/secure/partials/forms/createPostCategory.js';
import {
Card, Button, message, Row, Col, Icon, Switch, Popover,
} from 'antd';
import Text from '../../components/Text.js';
import Preview from '../../components/preview/index.js';
import CustomSpinner from '../../components/custom-spinner';
const DragIndicator = () => (
<svg viewBox="0 0 14 5" height="15" width="15" xmlns="http://www.w3.org/2000/svg">
<path d="m0 0h14v.83823529h-14z" />
<path d="m0 2h14v.83823529h-14z" />
<path d="m0 4h14v.83823529h-14z" />
</svg>
);
const getItemStyle = (isDragging, draggableStyle) => ({
userSelect: 'none',
...draggableStyle,
});
const reorder = (list, startIndex, endIndex) => {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
class Categories extends Component {
constructor(props) {
super(props);
this.state = {
categories: props.categories,
createCategory: false,
justReorder: false,
deletePopoverVisible: {},
};
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.ready && !prevState.justReorder) {
prevState.categories = nextProps.categories;
prevState.justReorder = false;
} else {
prevState.justReorder = false;
}
return prevState;
}
handleCategoryState = (id, checked) => {
Meteor.call('post.category.state', id, checked, (error) => {
if (error) {
console.log(error);
message.error('error');
} else {
message.success('success');
}
});
};
handleDeletePopoverVisibleChange = (change, id) => {
this.state.deletePopoverVisible[id] = change;
this.setState(this.state);
};
handleDelete = (e, id) => {
e.preventDefault();
Meteor.call('post.category.delete', id, (error) => {
if (error) {
console.log(error);
message.error('error');
} else {
message.success('success');
}
});
};
handleCreateCategory = (e) => {
e.preventDefault();
const form = this.createCategoryForm;
form.validateFieldsAndScroll((err, values) => {
if (err) {
return;
}
values.workspace = this.props.user.profile.workspace;
values.index = this.props.categories.length + 1;
Meteor.call('post.category.insert', values, (error) => {
if (error) {
console.log(error);
message.error('error');
} else {
message.success('success');
form.resetFields();
this.setState({ createCategory: false, categoryId: null });
}
});
});
};
onDragEnd = (data) => {
console.log(data);
const { source, destination, draggableId } = data;
if (!destination) {
return;
}
if (source.droppableId === destination.droppableId) {
this.state.categories = reorder(this.state.categories, data.source.index, data.destination.index);
this.state.categories.forEach((category, idx) => (category.index = idx));
this.state.justReorder = true;
this.setState(this.state, () => {
Meteor.call('post.category.reorder', { categories: this.state.categories }, (error, result) => {
if (error) {
console.log(error);
message.error('error.');
}
});
});
}
};
render() {
const { ready, categories } = this.props;
const { categories: stateCategories, deletePopoverVisible, createCategory } = this.state;
let content = <CustomSpinner />;
if (ready) {
content = (
<Col>
<Row gutter={40}>
<Col xs={24} lg={16} xxl={18}>
{!Array.isArray(categories) ? (
<Row gutter={40}>
<Col
xs={{ span: 24 }}
lg={{ span: 18, offset: 3 }}
xl={{ span: 12, offset: 6 }}
style={{ textAlign: 'center' }}
>
<img src="/emptystates/contentcreator.svg" alt="empty dashboard" />
</Col>
<Col
xs={{ span: 24 }}
lg={{ span: 18, offset: 3 }}
xl={{ span: 16, offset: 4 }}
style={{ textAlign: 'center', marginTop: '40px' }}
>
<h2>Head</h2>
<p>
Text
</p>
<Button
className="button--center"
size="large"
type="primary"
onClick={() => this.setState({ createCategory: true })}
>
Los geht's
<Icon type="right" />
</Button>
</Col>
</Row>
) : (
<div>
<div className="page-title">
<h1>Content Creator</h1>
<p>
Text
</p>
</div>
<Row gutter={40}>
<Col xs={24}>
<div className="shadow-wrapper">
<DragDropContext
onDragEnd={this.onDragEnd}
>
<Droppable droppableId="droppable">
{(provided, snapshot) => (
<div {...provided.droppableProps} ref={provided.innerRef}>
{stateCategories.map((category, index) => (
<Draggable key={category._id} draggableId={category._id} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={getItemStyle(snapshot.isDragging, provided.draggableProps.style)}
>
<Card className="card--list-view">
<div>
<div className="card__title">
<span {...provided.dragHandleProps}>
<Icon
component={DragIndicator}
style={{
paddingRight: '20px',
paddingTop: '6px',
}}
/>
</span>
<a href={`/content-creator/kategorie/${category._id}`}>
{category.title}
</a>
</div>
<div className="card__edit">
<span>
<a
href={`/content-creator/neuen-artikel-anlegen?id=${category._id}`}
onClick={() => FlowRouter.go(
'/content-creator/neuen-artikel-anlegen',
{},
{ id: category._id },
)
}
>
<Icon type="folder-add" />
{' Artikel hinzufügen'}
</a>
</span>
<Text
label="Umbenennen der Kategorie"
required
message="Bitte tragen Sie einen Kategorietitel ein."
initialValue={category.title}
render={(
<span>
<Icon
style={{
marginRight: 5,
}}
type="edit"
/>
Umbenennen
</span>
)}
onValid={(result) => {
Meteor.call(
'post.category.edit',
category._id,
{ title: result },
(e) => {
if (e) {
console.log(e);
message.error('Bei dieser Aktion ist ein Fehler aufgetreten');
} else {
message.success('Kategorie Titel erfolgreich erstellt');
}
},
);
}}
/>
<Popover
content={(
<div className="manager-dashboard">
<div
className="page__actions"
style={{ marginBottom: 0, marginTop: 0 }}
>
<Button
type="danger"
onClick={() => this.handleDeletePopoverVisibleChange(false, category._id)
}
>
<Icon type="close" />
{' Abbrechen'}
</Button>
<Button
type="primary"
style={{ marginLeft: '10px' }}
onClick={event => this.handleDelete(event, category._id)}
>
{'Löschen '}
<Icon type="delete" />
</Button>
</div>
</div>
)}
title="Diese Kategorie wirklich löschen?"
trigger="click"
visible={deletePopoverVisible[category._id]}
onVisibleChange={change => this.handleDeletePopoverVisibleChange(change, category._id)
}
>
<Icon type="delete" />
{' Löschen'}
</Popover>
<Switch
style={{ float: 'right' }}
defaultChecked={category.state}
onChange={checked => this.handleCategoryState(category._id, checked)}
checkedChildren="Öffentlich"
unCheckedChildren="Entwurf"
/>
</div>
</div>
</Card>
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
{Role.hasPermission('product.category.create') && (
<Card
className="card--list-view add-new-card"
onClick={() => this.setState({ createCategory: true })}
>
<div className="card__title">
<a>+ Neue Kategorie hinzufügen</a>
</div>
</Card>
)}
</div>
</Col>
</Row>
</div>
)}
{createCategory && (
<CreatePostCategory
ref={(form) => {
this.createCategoryForm = form;
}}
visible={createCategory}
onCancel={() => this.setState({ createCategory: false })}
onCreate={this.handleCreateCategory}
/>
)}
</Col>
<Col xs={24} sm={12} lg={8} xxl={6}>
<Preview type="categories" />
</Col>
</Row>
</Col>
);
}
return content;
}
}
export default withTracker(() => {
const user = Meteor.user();
const handleCategories = Meteor.subscribe('posts.categories.all', {
query: { workspace: user.profile.workspace },
sort: { index: 1, title: 1 },
fields: {
title: 1, state: 1, index: 1, workspace: 1,
},
});
const ready = handleCategories.ready();
let categories = null;
if (ready) {
categories = PostCategories.find(
{ workspace: user.profile.workspace },
{
sort: { index: 1, title: 1 },
fields: {
title: 1, state: 1, index: 1, workspace: 1,
},
},
).fetch();
}
return {
user,
ready,
categories,
};
}, Categories);
I am completly clueless what is going wrong, maybe somebody could help me out
React components are functions, and this error message comes about because you are passing in an object rather than a function.
That may be enough for you to find the problem now. It is helpful for us if you can do some narrowing down of the problem, rather than dumping a large amount of code - it becomes too expensive (time consuming) for us to help you if we have to pore through hundreds of lines of code.

Using axios to make use of a service not working

I'm trying to use S3 service to upload an image and it's telling me that certain variables aren't defined when I have defined them. I have imported axios and all the other stuff that are required
import React, { useState } from "react";
import ReactDOM from "react-dom";
import axios from "axios";
import Grid from "#material-ui/core/Grid";
import Button from "#material-ui/core/Button";
import Card from "#material-ui/core/Card";
import TextField from "#material-ui/core/TextField";
import CreateIcon from "#material-ui/icons/Create";
import Box from "#material-ui/core/Box";
import CardMedia from "#material-ui/core/CardMedia";
import MuiAlert from "#material-ui/lab/Alert";
import Snackbar from "#material-ui/core/Snackbar";
import { withStyles } from "#material-ui/core/styles";
import { makeStyles } from "#material-ui/core/styles";
import Chip from "#material-ui/core/Chip";
import Avatar from "#material-ui/core/Avatar";
import Slider from "#material-ui/core/Slider";
import Typography from "#material-ui/core/Typography";
import InputAdornment from "#material-ui/core/InputAdornment";
import { connect } from "react-redux";
function mapStateToProps(state) {
return {};
}
const useStyles = makeStyles((theme) => ({
root: {
"& > *": {
margin: theme.spacing(1),
marginLeft: theme.spacing(15),
},
},
input: {
display: "none",
},
}));
const useSliderStyles = makeStyles({
root: {
width: 250,
},
input: {
width: 100,
},
});
const UploadButton = () => {
const classes = useStyles();
return (
<div className={classes.root}>
<input
accept='image/*'
className={classes.input}
id='contained-button-file'
multiple
type='file'
/>
<label htmlFor='contained-button-file'>
<Button variant='contained' color='primary' component='span'>
Upload
</Button>
</label>
</div>
);
};
const StyledCard = withStyles({
root: { height: 600, width: 350 },
})(Card);
const PetitionForm = () => {
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [open, setOpen] = useState(false);
const [petition, validPetition] = useState(false);
const [noTitle, titleError] = useState(false);
const [noDescription, descriptionError] = useState(false);
const [hashtag, setHashtag] = useState("");
const [arrayOfHashtags, addHashtag] = useState([]);
const [money, setMoney] = React.useState(500);
const slider = useSliderStyles();
const handleTitleChange = (event) => setTitle(event.target.value);
const handleDescriptionChange = (event) => setDescription(event.target.value);
const handleClose = (event, reason) => {
if (reason === "clickaway") {
return;
}
};
const Alert = (props) => (
<MuiAlert elevation={6} variant='filled' {...props} />
);
const clearField = (event) => {
setOpen(true);
if (title.length > 0 && description.length > 0) {
validPetition(true);
setTitle("");
setDescription("");
addHashtag([]);
setHashtag("");
axios({
url: `call/s3/backend`,
method: "post",
data: {
images: imageArray.toByteArray(),
},
})
.then((res) => {
imageUrlArr = res.data;
axios({
url: `api/petition_posts`,
method: "post",
data: {
petition_post: {
title: title,
description: description,
hashtags: arrayOfHashtags.join(" "),
amount_donated: 0,
media: imageUrlArr,
goal: money,
card_type: "petition",
org_profile_id: 1,
},
},
})
.then((res) => {
console.log(res.data);
})
.catch((error) => console.log(error));
})
.catch((error) => console.log(error));
}
titleError(true ? title.length === 0 : false);
descriptionError(true ? description.length === 0 : false);
};
const handleDelete = (h) => () => {
addHashtag((arrayOfHashtags) =>
arrayOfHashtags.filter((hashtag) => hashtag !== h)
);
};
const handleHashtagChange = (event) => setHashtag(event.target.value);
const handleSliderChange = (event, newValue) => {
setMoney(newValue);
};
const handleInputChange = (event) => {
setMoney(event.target.value === "" ? "" : Number(event.target.value));
};
const newHashtag = () => {
if (arrayOfHashtags.length < 3) {
addHashtag((arrayOfHashtags) => arrayOfHashtags.concat(hashtag));
} else {
console.log("Too many hashtags");
}
};
const Hashtags = arrayOfHashtags.map((h) => (
<Chip
key={h.length}
size='small'
avatar={<Avatar>#</Avatar>}
label={h}
onDelete={handleDelete(h)}
/>
));
return (
<StyledCard>
<Box mt={1}>
<Grid container justify='center'>
<TextField
id='outlined-multiline-static'
multiline
rows={1}
variant='outlined'
placeholder='Title'
value={title}
onChange={handleTitleChange}
helperText={
open // only displays helper text if button has been clicked and fields haven't been filled
? !noTitle || petition
? ""
: "Can't be an empty field"
: ""
}
/>
</Grid>
</Box>
<Box mt={1}>
<Grid container justify='center'>
<CardMedia title='Petition'>
<UploadButton />
</CardMedia>
</Grid>
</Box>
<div className={slider.root}>
<Typography>Amount to raise</Typography>
<Box>
<Grid container justify='center'>
<Slider
min={500}
max={10000}
value={typeof money === "number" ? money : 0}
onChange={handleSliderChange}
aria-labelledby='input-slider'
/>
<TextField
className={slider.input}
value={money}
onChange={handleInputChange}
InputProps={{
startAdornment: (
<InputAdornment position='start'>$</InputAdornment>
),
}}
helperText={
money < 500 || money > 10000
? "Please enter a value between 500 and 10000"
: ""
}
/>
</Grid>
</Box>
</div>
<Box mt={1} mb={1}>
<Grid container justify='center'>
<TextField
size='small'
inputProps={{
style: { fontSize: 15 },
}}
id='outlined-multiline-static'
multiline
rows={1}
placeholder='Hashtags'
variant='outlined'
value={hashtag}
onChange={handleHashtagChange}
helperText={
arrayOfHashtags.length === 3
? "You reached the maximum amount of hashtags"
: ""
}
/>
<Button color='primary' onClick={newHashtag}>
Create!
</Button>
{arrayOfHashtags.length > 0 ? Hashtags : ""}
</Grid>
</Box>
<Box mt={1} justify='center'>
<Grid container justify='center'>
<TextField
size='small'
inputProps={{
style: { fontSize: 15 },
}}
id='outlined-multiline-static'
multiline
rows={5}
placeholder='Description'
variant='outlined'
value={description}
onChange={handleDescriptionChange}
helperText={
// only displays helper text if button has been clicked and fields haven't been filled
open
? !noDescription || petition
? ""
: "Can't be an empty field"
: ""
}
/>
</Grid>
</Box>
<Box mt={1}>
<Grid container justify='center'>
<Button onClick={clearField}>
<CreateIcon />
Create Petition!
</Button>
{open && petition && (
<Snackbar open={open} autoHideDuration={2000} onClose={handleClose}>
<Alert severity='success'>
You have successfully create a petition!
</Alert>
</Snackbar>
)}
{open && !petition && (
<Snackbar open={open} autoHideDuration={2000} onClose={handleClose}>
<Alert severity='error'>You're missing one or more fields</Alert>
</Snackbar>
)}
</Grid>
</Box>
</StyledCard>
);
};
export default connect(mapStateToProps)(PetitionForm);
This is the error I'm getting, someone mentioned something about the scope and I think that shouldn't matter when I'm trying to assign a value to a variable as far I as know
Line 109:19: 'imageArray' is not defined no-undef
Line 113:11: 'imageUrlArr' is not defined no-undef
Line 123:24: 'imageUrlArr' is not defined no-undef
Search for the keywords to learn more about each error.

useState in react hooks converting to functional component

I am trying to convert the setState into useState.
so I followed the below article and converted partially
https://medium.com/javascript-in-plain-english/state-management-with-react-hooks-no-redux-or-context-api-8b3035ceecf8- but not sure how to convert the props
but its not rendering the values.
so I debugged and put console inside useEffect.
I am getting undefind at this line, am I using useState wrongly
useEffect(() => {
console.log("useEffect setspHeight--->", setspHeight);
can you let me know how to fix it and do I need to make any other changes for react hooks
class component code
import React, { Fragment, useState, Component } from 'react';
import styles from './styles';
import { withStyles } from '#material-ui/core/styles';
import classnames from 'classnames';
import ExpandMoreIcon from '#material-ui/icons/ExpandMore';
import IconButton from '#material-ui/core/IconButton';
import Button from '#material-ui/core/Button';
import ExpansionPanel from '#material-ui/core/ExpansionPanel';
import ExpansionPanelSummary from '#material-ui/core/ExpansionPanelSummary';
import ExpansionPanelDetails from '#material-ui/core/ExpansionPanelDetails';
import Typography from '#material-ui/core/Typography';
import Drawer from '#material-ui/core/Drawer';
import AppBar from '#material-ui/core/AppBar';
import Tabs from '#material-ui/core/Tabs';
import Tab from '#material-ui/core/Tab';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import Radio from '#material-ui/core/Radio';
import RadioGroup from '#material-ui/core/RadioGroup';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import FormControl from '#material-ui/core/FormControl';
import FormLabel from '#material-ui/core/FormLabel';
import Checkbox from '#material-ui/core/Checkbox';
import { MuiThemeProvider, createMuiTheme } from '#material-ui/core/styles';
import dataStyles from '../../../../styles.css';
function TabContainer(props) {
return (
<Typography component="div" style={{ padding: 8 * 3 }}>
{props.children}
</Typography>
);
}
const radioValues = [
{
label: 'Select All Providers for This Category',
value: 'PRO',
},
];
class SportsExpansion extends Component {
state = {
value: 0,
spHeight: [],
spLength: '',
};
componentDidMount() {
this.setState({ spHeight: this.props.spHeight });
if (
this.props.spHeight.filter(
check => check.spWeight === 'One'
).length > 0
) {
this.setState({ value: 0 });
} else if (
this.props.spHeight.filter(
check => check.spWeight === 'Two'
).length > 0
) {
this.setState({ value: 1 });
}
}
handleChange = (event, value) => {
console.log('handleChange -value', value);
this.setState({ value });
};
handleSportsRadioValueChange = (category, value) => {
console.log('handleSportsRadioValueChange -category', category);
console.log('handleSportsRadioValueChange -value', value);
this.setState({ spLength: value });
};
render() {
const { classes, data } = this.props;
let udVal = '';
let starVal = '';
udVal = data.udVals ? data.udVals[0].number : '';
starVal = data.starVals ? data.starVals[0].number : '';
const { canEdit, value } = this.state;
const { spHeight } = this.state;
return (
<div>
<AppBar
className={classes.benchmarkTabHeader}
position="static"
color="default"
>
<Tabs
onChange={this.handleChange}
variant="scrollable"
scrollButtons="on"
indicatorColor="primary"
textColor="primary"
style={{ display: 'block' }}
classes={{
indicator: classes.tabsIndicator,
scrollButtons: classes.MuiPrivateTabScrollButton,
}}
>
<Tab
style={{
display:
this.state.spHeight.filter(
check =>
check.spWeight === 'One'
).length === 0
? 'none'
: '',
color: value == 0 ? '#1976D2' : '#66696C',
}}
label={`Groups (${
this.state.spHeight.filter(
check =>
check.spWeight === 'One'
).length
})`}
icon={
<FontAwesomeIcon style={{ display: 'block' }} />
}
classes={{
root: classes.tabRoot,
selected: classes.tabSelected,
wrapper: classes.alignment,
}}
/>
<Tab
style={{
display:
this.state.spHeight.filter(
check =>
check.spWeight ===
'Two'
).length === 0
? 'none'
: '',
color: value == 1 ? '#1976D2' : '#66696C',
}}
label={`Two (${
this.state.spHeight.filter(
check =>
check.spWeight ===
'Two'
).length
})`}
icon={
<FontAwesomeIcon style={{ display: 'block' }} />
}
classes={{
root: classes.tabRoot,
selected: classes.tabSelected,
wrapper: classes.alignment,
}}
/>
</Tabs>
</AppBar>
{value === 0 && (
<TabContainer>
<div>
{' '}
<FormControl
component="fieldset"
className={classes.formControl}
>
<FormLabel component="legend" />
<RadioGroup
aria-label="Gender"
name="gender1"
className={classes.one}
value={this.state.spLength}
onChange={e => {
this.setState({
spLength: e.target.value,
});
}}
>
{this.state.spHeight
.filter(
check =>
check.spWeight ===
'One'
)
.map((radio, radioIndex) => {
return (
<FormControlLabel
key={radioIndex}
value={radio.value}
control={<Radio />}
label={radio.label}
classes={{
label:
classes.checkboxLabel,
}}
/>
);
})}
)}
</RadioGroup>
</FormControl>
<div className="tiger-button-container">
<Button
variant="outlined"
color="primary"
size="small"
className="tiger-button-upload"
>
ghsd jkjkjk
</Button>
</div>
<Drawer
style={{ width: 500 }}
anchor="right"
open={this.state.right}
>
<div tabIndex={0} role="button"></div>
</Drawer>
</div>
</TabContainer>
)}
{value === 1 && (
<TabContainer>
<div>
<div>
<FormControl
component="fieldset"
className={classes.formControl}
>
<RadioGroup
aria-label="Gender"
name="gender1"
className={classes.one}
value={this.state.spLength}
onChange={e => {
this.setState({
spLength:
e.target.value,
});
}}
>
{this.state.spHeight
.filter(
check =>
check.spWeight ===
'Two'
)
.map((radio, radioIndex) => {
return (
<FormControlLabel
key={radioIndex}
value={radio.label}
control={<Radio />}
label={radio.label}
classes={{
label:
classes.checkboxLabel,
}}
/>
);
})}
</RadioGroup>
</FormControl>
<div className="tiger-button-container">
<Button
variant="outlined"
color="primary"
size="small"
className="tiger-button-upload"
>
ghsd jkjkjk
</Button>
</div>
</div>
</div>
</TabContainer>
)}
</div>
);
}
}
export default withStyles(styles)(SportsExpansion);
////////////////////////////////////////////////
functional component code
import React, { Fragment, useState, useEffect, Component } from 'react';
import styles from './styles';
import { withStyles } from '#material-ui/core/styles';
import classnames from 'classnames';
import ExpandMoreIcon from '#material-ui/icons/ExpandMore';
import IconButton from '#material-ui/core/IconButton';
import Button from '#material-ui/core/Button';
import ExpansionPanel from '#material-ui/core/ExpansionPanel';
import ExpansionPanelSummary from '#material-ui/core/ExpansionPanelSummary';
import ExpansionPanelDetails from '#material-ui/core/ExpansionPanelDetails';
import Typography from '#material-ui/core/Typography';
import Drawer from '#material-ui/core/Drawer';
import AppBar from '#material-ui/core/AppBar';
import Tabs from '#material-ui/core/Tabs';
import Tab from '#material-ui/core/Tab';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import Radio from '#material-ui/core/Radio';
import RadioGroup from '#material-ui/core/RadioGroup';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import FormControl from '#material-ui/core/FormControl';
import FormLabel from '#material-ui/core/FormLabel';
import Checkbox from '#material-ui/core/Checkbox';
import { MuiThemeProvider, createMuiTheme } from '#material-ui/core/styles';
import dataStyles from '../../../../styles.css';
function TabContainer(props) {
return (
<Typography component="div" style={{ padding: 8 * 3 }}>
{props.children}
</Typography>
);
}
const radioValues = [
{
label: 'Select All Providers for This Category',
value: 'PRO',
},
];
//class SportsExpansion extends Component {
const SportsExpansion = (props) => {
const [value, setValue] = useState(0);
const [spHeight, setspHeight] = useState([]);
const [spLength, setspLength] = useState('');
// const { classes, data } = this.props;
const { classes, data } = props;
let udVal = '';
let starVal = '';
udVal = data.udVals ? data.udVals[0].number : '';
starVal = data.starVals ? data.starVals[0].number : '';
// const { canEdit, value } = this.state;
// const { spHeight } = this.state;
useEffect(() => {
// code to run on component mount
console.log("useEffect setspHeight--->", setspHeight);
//this.setState({ spHeight: this.props.spHeight });
setspHeight(spHeight);
if (
spHeight.filter(
check => check.spWeight === 'One'
).length > 0
) {
useState({ value: 0 });
} else if (
spHeight.filter(
check => check.spWeight === 'Two'
).length > 0
) {
useState({ value: 1 });
}
}, [])
//handleChange = (event, value) => {
const handleChange = (event, value) => {
console.log('handleChange -value', value);
useState({ value });
};
// handleSportsRadioValueChange = (category, value) => {
const handleSportsRadioValueChange = (category, value) => {
console.log('handleSportsRadioValueChange -category', category);
console.log('handleSportsRadioValueChange -value', value);
useState({ spLength: value });
};
return (
<div>
<AppBar
className={classes.benchmarkTabHeader}
position="static"
color="default"
>
<Tabs
onChange={this.handleChange}
variant="scrollable"
scrollButtons="on"
indicatorColor="primary"
textColor="primary"
style={{ display: 'block' }}
classes={{
indicator: classes.tabsIndicator,
scrollButtons: classes.MuiPrivateTabScrollButton,
}}
>
<Tab
style={{
display:
//this.state.spHeight.filter(
spHeight.filter(
check =>
check.spWeight === 'One'
).length === 0
? 'none'
: '',
color: value == 0 ? '#1976D2' : '#66696C',
}}
label={`Groups (${
//this.state.spHeight.filter(
spHeight.filter(
check =>
check.spWeight === 'One'
).length
})`}
icon={
<FontAwesomeIcon style={{ display: 'block' }} />
}
classes={{
root: classes.tabRoot,
selected: classes.tabSelected,
wrapper: classes.alignment,
}}
/>
<Tab
style={{
display:
//this.state.spHeight.filter(
spHeight.filter(
check =>
check.spWeight ===
'Two'
).length === 0
? 'none'
: '',
color: value == 1 ? '#1976D2' : '#66696C',
}}
label={`Two (${
//this.state.spHeight.filter(
spHeight.filter(
check =>
check.spWeight ===
'Two'
).length
})`}
icon={
<FontAwesomeIcon style={{ display: 'block' }} />
}
classes={{
root: classes.tabRoot,
selected: classes.tabSelected,
wrapper: classes.alignment,
}}
/>
</Tabs>
</AppBar>
{value === 0 && (
<TabContainer>
<div>
{' '}
<FormControl
component="fieldset"
className={classes.formControl}
>
<FormLabel component="legend" />
<RadioGroup
aria-label="Gender"
name="gender1"
className={classes.one}
value={//this.state.spLength
spLength}
onChange={e => {
useState({
spLength: e.target.value,
});
}}
>
{//this.state.spHeight
spHeight
.filter(
check =>
check.spWeight ===
'One'
)
.map((radio, radioIndex) => {
return (
<FormControlLabel
key={radioIndex}
value={radio.value}
control={<Radio />}
label={radio.label}
classes={{
label:
classes.checkboxLabel,
}}
/>
);
})}
)}
</RadioGroup>
</FormControl>
<div className="tiger-button-container">
<Button
variant="outlined"
color="primary"
size="small"
className="tiger-button-upload"
>
ghsd jkjkjk
</Button>
</div>
{/*<Drawer
style={{ width: 500 }}
anchor="right"
open={
//this.state.right
right}
>
<div tabIndex={0} role="button"></div>
</Drawer>*/}
</div>
</TabContainer>
)}
{value === 1 && (
<TabContainer>
<div>
<div>
<FormControl
component="fieldset"
className={classes.formControl}
>
<RadioGroup
aria-label="Gender"
name="gender1"
className={classes.one}
value={//this.state.spLength
spLength}
onChange={e => {
useState({
spLength:
e.target.value,
});
}}
>
{//this.state.spHeight
spHeight
.filter(
check =>
check.spWeight ===
'Two'
)
.map((radio, radioIndex) => {
return (
<FormControlLabel
key={radioIndex}
value={radio.label}
control={<Radio />}
label={radio.label}
classes={{
label:
classes.checkboxLabel,
}}
/>
);
})}
</RadioGroup>
</FormControl>
<div className="tiger-button-container">
<Button
variant="outlined"
color="primary"
size="small"
className="tiger-button-upload"
>
ghsd jkjkjk
</Button>
</div>
</div>
</div>
</TabContainer>
)}
</div>
);
}
export default withStyles(styles)(SportsExpansion);
You have to replace where you call useState({ value: 1 }); to setValue(value)
You are using useState wrong. useState replaces the state object. The first value in the array is the value you can call in your component. The second value in the array replaces this.setState and sets the value of the state instance. So to use the useState hook if you had the following:
const [value, setValue] = useState(0);
you would use value in your component to reference this state instance. And you would use setValue to set the value of this state instance. So to set value to 2 you would do setValue(2).

Categories