How to avoid checking if a function exists before calling it? - javascript

I need access to the formik props outside of the form.
While using refs, how can I avoid checking if a function exists before calling it?
function BasicInfo({ event, initialValues, onSubmit }: Props) {
const { coords } = useLocation();
const { data: places, forward, getStaticMapUrl } = useMapbox();
const { data: games, search, leanGame } = useGames();
const throttle = useThrottle();
const formRef = useRef<FormikProps<BasicInfoValues>>(null);
const initValues: BasicInfoValues = initialValues || {
title: '',
game: { name: '' },
address: '',
starts_at: '',
ends_at: '',
coords: '',
};
const { setFieldValue, values, handleChange } = formRef.current || {};
const handleEditLocationClick = function () {
if (!setFieldValue) return;
setFieldValue('address', '');
setFieldValue('coords', '');
};
const renderLocation = function () {
if (!values) return null;
const { coords: coordinates, address } = values;
if (coordinates && address) {
const staticMapUrl = getStaticMapUrl({
coords: JSON.parse(coordinates),
width: 600,
height: 165,
});
const { street, city } = parseAddress(address);
return (
<div className="basic-info__location">
<div className="basic-info__location__image-container">
<Image
src={staticMapUrl}
alt="static map"
layout="fill"
objectFit="fill"
/>
</div>
<div className="basic-info__location__address-container">
<div className="basic-info__location__address-container__address">
<span className="basic-info__location__street">{street}</span>
<span className="basic-info__location__city">{city}</span>
</div>
<Button
text="Edit location"
color="secondary"
size="small"
onClick={handleEditLocationClick}
/>
</div>
</div>
);
} else {
return (
<AutoComplete<Feature>
name="address"
value={values.address}
onChange={(e) => {
if (!handleChange) return;
handleChange(e);
throttle.wait(() => {
if (!e.target.value) return;
if (!coords) return;
forward({
coords,
q: e.target.value,
});
}, 500);
}}
label="Venue location"
placeholder="Search for a venue or address"
items={places}
Input={InputGroup}
itemRenderer={(item) => <AddressItem placeName={item.place_name} />}
onItemClick={(item) => {
if (!setFieldValue) return;
setFieldValue('address', item.place_name);
setFieldValue('coords', JSON.stringify(item.center));
}}
/>
);
}
};
return (
<Formik initialValues={initValues} onSubmit={onSubmit} innerRef={formRef}>
{({ values, handleChange, setFieldValue }) => (
<Form id="basic-info" className="basic-info">
<FormSection
title="Basic Info"
description="Name your event and tell gamers what game will be played. Add
details that highlight what makes it unique."
icon="segment"
>
<InputGroup
name="title"
value={values.title}
onChange={handleChange}
label="Event title"
placeholder="Be clear and descriptive"
/>
<AutoComplete<Game>
name="game.name"
value={values.game.name}
onChange={(e) => {
handleChange(e);
throttle.wait(() => {
if (!e.target.value) return;
search({ name: e.target.value, limit: 5 });
}, 500);
}}
label="Featured game"
placeholder="Search games"
Input={InputGroup}
items={games}
itemRenderer={(item) => <GameItem game={item} />}
onItemClick={(item) => {
const game = leanGame(item);
setFieldValue('game', game);
}}
/>
<span>
Need game ideas?{' '}
<Link href="/" passHref>
<a className="link">Browse games by category</a>
</Link>
</span>
</FormSection>
<FormSection
title="Location"
description="Help gamers in the area discover your event and let attendees know where to show up."
icon="map"
>
{renderLocation()}
</FormSection>
<FormSection
title="Date and time"
description="Tell gamers when your event starts and ends so they can make plans to attend."
icon="date_range"
>
<InputGroup
name="starts_at"
value={values.starts_at}
onChange={handleChange}
label="Event starts"
placeholder="Search for a venue or address"
type="datetime-local"
// icon="calendar_today"
/>
<InputGroup
name="ends_at"
value={values.ends_at}
onChange={handleChange}
label="Event ends"
placeholder="Search for a venue or address"
type="datetime-local"
// icon="calendar_today"
/>
</FormSection>
</Form>
)}
</Formik>
);
}
Specifically this function where I check if setFieldValue exists:
const handleEditLocationClick = function () {
if (!setFieldValue) return;
setFieldValue('address', '');
setFieldValue('coords', '');
};
There are other functions that also need formik props so I will have to do these checks. I guess I could pass in setFieldValue function as an argument to the handleEditLocationClick function, but that doesn't seem like a good practice.

You should not be using ref at all here. Just pass the render prop parameter down to your helper functions:
function BasicInfo({ event, initialValues, onSubmit }: Props) {
const { coords } = useLocation();
const { data: places, forward, getStaticMapUrl } = useMapbox();
const { data: games, search, leanGame } = useGames();
const throttle = useThrottle();
const initValues: BasicInfoValues = initialValues || {
title: '',
game: { name: '' },
address: '',
starts_at: '',
ends_at: '',
coords: '',
};
function renderLocation({ values, handleChange, setFieldValue }) {
const handleEditLocationClick = function () {
setFieldValue('address', '');
setFieldValue('coords', '');
};
const { coords: coordinates, address } = values;
if (coordinates && address) {
const staticMapUrl = getStaticMapUrl({
coords: JSON.parse(coordinates),
width: 600,
height: 165,
});
const { street, city } = parseAddress(address);
return (
<div className="basic-info__location">
<div className="basic-info__location__image-container">
<Image
src={staticMapUrl}
alt="static map"
layout="fill"
objectFit="fill"
/>
</div>
<div className="basic-info__location__address-container">
<div className="basic-info__location__address-container__address">
<span className="basic-info__location__street">{street}</span>
<span className="basic-info__location__city">{city}</span>
</div>
<Button
text="Edit location"
color="secondary"
size="small"
onClick={handleEditLocationClick}
/>
</div>
</div>
);
} else {
return (
<AutoComplete<Feature>
name="address"
value={values.address}
onChange={(e) => {
handleChange(e);
throttle.wait(() => {
if (!e.target.value) return;
if (!coords) return;
forward({
coords,
q: e.target.value,
});
}, 500);
}}
label="Venue location"
placeholder="Search for a venue or address"
items={places}
Input={InputGroup}
itemRenderer={(item) => <AddressItem placeName={item.place_name} />}
onItemClick={(item) => {
setFieldValue('address', item.place_name);
setFieldValue('coords', JSON.stringify(item.center));
}}
/>
);
}
}
return (
<Formik initialValues={initValues} onSubmit={onSubmit} innerRef={formRef}>
{({ values, handleChange, setFieldValue }) => (
<Form id="basic-info" className="basic-info">
<FormSection
title="Basic Info"
description="Name your event and tell gamers what game will be played. Add
details that highlight what makes it unique."
icon="segment"
>
<InputGroup
name="title"
value={values.title}
onChange={handleChange}
label="Event title"
placeholder="Be clear and descriptive"
/>
<AutoComplete<Game>
name="game.name"
value={values.game.name}
onChange={(e) => {
handleChange(e);
throttle.wait(() => {
if (!e.target.value) return;
search({ name: e.target.value, limit: 5 });
}, 500);
}}
label="Featured game"
placeholder="Search games"
Input={InputGroup}
items={games}
itemRenderer={(item) => <GameItem game={item} />}
onItemClick={(item) => {
const game = leanGame(item);
setFieldValue('game', game);
}}
/>
<span>
Need game ideas?{' '}
<Link href="/" passHref>
<a className="link">Browse games by category</a>
</Link>
</span>
</FormSection>
<FormSection
title="Location"
description="Help gamers in the area discover your event and let attendees know where to show up."
icon="map"
>
{renderLocation({ values, handleChange, setFieldValue })}
</FormSection>
<FormSection
title="Date and time"
description="Tell gamers when your event starts and ends so they can make plans to attend."
icon="date_range"
>
<InputGroup
name="starts_at"
value={values.starts_at}
onChange={handleChange}
label="Event starts"
placeholder="Search for a venue or address"
type="datetime-local"
// icon="calendar_today"
/>
<InputGroup
name="ends_at"
value={values.ends_at}
onChange={handleChange}
label="Event ends"
placeholder="Search for a venue or address"
type="datetime-local"
// icon="calendar_today"
/>
</FormSection>
</Form>
)}
</Formik>
);
}

Related

How to get selected values from multiple dropdowns on button click using ReactJs

I want to console.log() multiple values from five different dropdowns on a button click. I have done this for one dropdown, but I don't know how to do it for more. I'm still a beginner.
Here's my code:
export default function Suma() {
const typedemande = [
{ value: "first", label: "first" },
{ value: "second", label: "second" },
];
const [message, setMessage] = useState('');
const handleChange = event => {
setMessage(event);
};
const handleClick = event => {
event.preventDefault();
console.log(message);
};
return (
<div>
<div className="col-lg">
<Select placeholder="choose" id="message" className="react-dropdown " name="message" onChange={handleChange}
value={message}
isClearable
isSearchable={false}
classNamePrefix="dropdown"
options={typedemande}
/>
</div>
<div className="text-center">
<button className="mr-2 btn btn-primary" onClick={handleClick}>Click me</button>
</div>
</div>
);
};
I hope you are looking for this one:
export default function App() {
const typedemande = [
{ value: "first", label: "first" },
{ value: "second", label: "second" },
{ value: "third", label: "third" },
{ value: "fourth", label: "fourth" },
{ value: "five", label: "five" },
];
const [showAll, setShowAll ] = useState([]);
const [dropdowns,setDrodowns] = useState({
'message1': '',
'message2': '',
'message3': '',
'message4': '',
'message5': '',
});
const handleChange = (event) => {
setDrodowns({...dropdowns,[event.target.name]:event.target.value});
}
const handleClick = (event) => {
event.preventDefault(); // if you use the element inside `form` then it would prevent to submit
console.log(dropdowns);//to log the values in console
setShowAll(Object.values(dropdowns));// to show the changes in UI
}
return (
<div>
<div className="col-lg">
<Select
name="message1"
onChange={handleChange}
value={"second"}
options={typedemande}
/>
<Select
name="message2"
onChange={handleChange}
value={"second"}
options={typedemande}
/>
<Select
name="message3"
onChange={handleChange}
value={"second"}
options={typedemande}
/>
<Select
name="message4"
onChange={handleChange}
value={"second"}
options={typedemande}
/>
<Select
name="message5"
onChange={handleChange}
value={"second"}
options={typedemande}
/>
</div>
<hr/>
<ul>
{ showAll.map((val,i)=><li key={i}>{i+1} --- {val}</li>) }
</ul>
<hr/>
<div className="text-center">
<button className="mr-2 btn btn-primary" onClick={handleClick}>Click me</button>
</div>
</div>
);
}
For details check the code sandbox link
Out put
Edit: Based on user comments I edited the answer
You could pass a parameter to your handleChange.
const handleChange = (event, position) => {
console.log(position);
};
<Select onChange={(e) => handleChange(e, 1)} />
<Select onChange={(e) => handleChange(e, 2)} />
<Select onChange={(e) => handleChange(e, 3)} />
Improving axtck's answer, you can get each select value like below
import React, {useState} from 'react';
import Select from 'react-select';
export function App(props) {
const typedemande = [
{ value: "first", label: "first" },
{ value: "second", label: "second" },
];
const [messages, setMessages] = useState([]);
const handleChange = (event, pos) => {
console.log(pos)
console.log(event.value)
let mz = [...messages];
if (mz.length > 0 && mz.findIndex(msg => msg.index == pos) > -1) {
mz[mz.findIndex(msg => msg.index == pos)] = event.value;
setMessages(mz);
}
else {
mz.push({
index: pos,
value: event.value
});
setMessages(mz);
}
};
const handleClick = event => {
event.preventDefault();
for (let i = 0; i < messages.length; i++)
console.log(messages[i].value)
};
return (
<div>
<div className="col-lg">
<Select placeholder="choose" id="message" className="react-dropdown " name="message" onChange={(e) => handleChange(e, 1)}
value={messages[0] ? messages[0].label : ''}
isClearable
isSearchable={false}
classNamePrefix="dropdown"
options={typedemande}
/>
<Select placeholder="choose" id="message" className="react-dropdown " name="message" onChange={(e) => handleChange(e, 2)}
value={messages[1] ? messages[1].label : ''}
isClearable
isSearchable={false}
classNamePrefix="dropdown"
options={typedemande}
/>
</div>
<div className="text-center">
<button className="mr-2 btn btn-primary" onClick={handleClick}>Click me</button>
</div>
</div>
);
}

After Delete Mutation not Refetching Query (Apollo Graphql & Nextjs)

I recently switched my reactjs code to nextjs code, and I've observed that while I'm in reactjs code, When I delete data or perform a delete action, it appears like the queries are re-fetched and I am given the most recent or updated data in the datatable, but when I attempt it on Nextjs, it does not work. Is there a way to fix this?
Keep note I am using client side for this action.
Code
Form.js
export default function MainCategoryForm() {
const [name, setName] = useState("");
const [file, setFile] = useState();
const [status, setStatus] = useState("");
const [createMainCategory, { loading }] = useMutation(
CREATE_MAINCATEGORY_MUTATION
);
async function onSubmit() {
const res = await createMainCategory({
variables: {
name,
slug: name.toLowerCase(),
file,
status,
},
update: (cache, { data: { createMainCategory } }) => {
const { mainCategories } = cache.readQuery({
query: FETCH_MAINCATEGORIES_QUERY,
});
cache.writeQuery({
query: FETCH_MAINCATEGORIES_QUERY,
data: { mainCategories: mainCategories.concat([createMainCategory]) },
});
},
refetchQueries: [{ query: FETCH_MAINCATEGORIES_QUERY }],
});
if (res) {
toast.success(`Main Category Created`, { autoClose: 2000 });
setName("");
setStatus("");
setFile("");
}
}
console.log(file);
return (
<>
<Form onSubmit={onSubmit} className={loading ? "loading" : ""}>
<h2>Create a Main Category:</h2>
<Form.Field>
<input
name="file"
type="file"
onChange={(event) => {
setFile(event.target.files[0]);
}}
/>
<Form.Input
placeholder="Please Enter Name"
name="name"
label="Name: "
onChange={(event) => {
setName(event.target.value);
}}
value={name}
/>
<label>Status: </label>
<select
name="category"
className="form-control"
onChange={(event) => {
setStatus(event.target.value);
}}
value={status}
>
<option active="true" hidden>
Please Enter Status
</option>
<option value="Activated">Activated</option>
</select>
<br />
<Button type="submit" color="teal">
Submit
</Button>
</Form.Field>
</Form>
</>
);
}
As shown above this code I have this refetchQueries: [{ query: FETCH_MAINCATEGORIES_QUERY }], in which after the add mutation or mutation it will refetch the query needed for the recent data to show in my datatable, I tried also putting that in the DeleteButton Component but it doesn't work.
Table
export default function MainCategoryTable({
mainCategory: { id, name, slug, status, url, createdAt },
}) {
return (
<>
<tr>
<td>{id}</td>
<td>
<img src={url} width={300} />
</td>
<td>{name}</td>
<td>{slug}</td>
<td>{status}</td>
<td>{dayjs(createdAt).format("h:mm:ss a")}</td>
<td>
<DeleteButton name={name} mainCategoryId={id} />
<Button>
<Link href={`/mainCategories/${id}`}>
<Icon name="edit" style={{ margin: 0 }} />
</Link>
</Button>
</td>
</tr>
</>
);
}
DeleteButton Component
export default function DeleteButton({ mainCategoryId, callback }) {
const [confirmOpen, setConfirmOpen] = useState(false);
const mutation = DELETE_MAINCATEGORY_MUTATION;
const [deleteMainCategoryOrMutation] = useMutation(mutation, {
update(proxy) {
setConfirmOpen(false);
if (mainCategoryId) {
const data = proxy.readQuery({
query: FETCH_MAINCATEGORIES_QUERY,
});
data.getMainCategories = data.getMainCategories.filter(
(ap) => ap.id !== mainCategoryId
);
toast.error(`Main Category Deleted`, { autoClose: 2000 });
proxy.writeQuery({ query: FETCH_MAINCATEGORIES_QUERY, data });
}
if (callback) callback();
},
variables: {
mainCategoryId,
},
});
return (
<>
<MyPopup content={"Delete Main Category"}>
<Button
as="div"
color="red"
floated="right"
onClick={() => setConfirmOpen(true)}
>
<Icon name="trash" style={{ margin: 0 }} />
</Button>
</MyPopup>
<Confirm
open={confirmOpen}
onCancel={() => setConfirmOpen(false)}
onConfirm={deleteMainCategoryOrMutation}
/>
</>
);
}
If you need any more code, such as my backend or any files to figure out what's wrong, I'll always amend my article. If you need any clarification or don't understand what I mean, please leave a comment down below.
You didn't set the refetchQueries in the DELETE_MAINCATEGORY_MUTATION mutation, instead you used the update option and read the query from the cache but you mutated the data, which is not the right way to do it, instead you should return a new array as follows:
const [deleteMainCategoryOrMutation] = useMutation(mutation, {
update(proxy) {
setConfirmOpen(false);
if (mainCategoryId) {
const previousData = proxy.readQuery({ query: FETCH_MAINCATEGORIES_QUERY });
const getMainCategories = previousData.getMainCategories.filter(
(ap) => ap.id !== mainCategoryId
);
const data = {
getMainCategories,
};
toast.error(`Main Category Deleted`, { autoClose: 2000 });
proxy.writeQuery({ query: FETCH_MAINCATEGORIES_QUERY, data });
}
if (callback) callback();
},
variables: {
mainCategoryId,
},
});

REACT: What I should write in propTypes when in form I use input type="file'?

I make form using Formik in my app. When I send form to my local server I create image with title. Attach images I should using input type="file".But I have very little experience using the formik.
What I should write in propTypes when in form I use input type="file' in file InputImage.js?
And How to add input type="file" in file AddImage.js in mark place?
Now I want to create input which attach image component InputImage.js similar to InputTitle.js.
I comment line where I dont know what I should write.
AddImage.js:
const AddImage = (props) => {
const {handleSubmit, values, handleChange} = useFormik({
initialValues: {
title: '',
image: '' // Did I write correctly here?
},
validateOnchange: false,
onSubmit: async (formValues) => {
const response = await api(`${imageRoutePath}`, {
method:'POST',
body: JSON.stringify(formValues),
});},
});
return (
<div>
<form onSubmit={handleSubmit}>
<InputTitle
label="title"
id="title"
inputProps={{
name:'title',
value: values.title,
onChange: handleChange,
}}
/>
<InputImage
label="image"
id="image"
inputProps={{
name:'image',
// WHAT I SHOULD WRITE THERE?
onChange: handleChange,
}}
/>
<button type="submit" disabled={isSubmitting}>Add</button>
</form>
</div>
);
};
export default AddImage;
InputImage.js:
const InputImage = ({
label, inputProps, error, id,
}) => (
<div className="formInputCategory">
<label htmlFor={id} className="formInputLabelCategory">
{label}
</label>
<input {...inputProps} id={id} />
{error && <span className="formInputErrorCategory">{error}</span>}
</div>
);
InputImage.propTypes = {
label: PropTypes.string.isRequired,
// WHAT I SHOULD WRITE THERE?
error: PropTypes.string,
id: PropTypes.string.isRequired,
};
InputImage.defaultProps = {
error: '',
}
---------------------------------------------------------------------------------------
example how I write InputTitle.js:
const InputTitle = ({
label, inputProps, error, id,
}) => (
<div className="formInputCategory">
<label htmlFor={id} className="formInputLabelCategory">
{label}
</label>
<input {...inputProps} id={id} />
{error && <span className="formInputErrorCategory">{error}</span>}
</div>
);
InputTitle.propTypes = {
label: PropTypes.string.isRequired,
inputProps: PropTypes.instanceOf(Object).isRequired,
error: PropTypes.string,
id: PropTypes.string.isRequired,
};
InputTitle.defaultProps = {
error: '',
}
Formik doesnot support fileupload by default, But you can try the following
<input id="file" name="file" type="file" onChange={(event) => {
setFieldValue("file", event.currentTarget.files[0]);
}} />
Here "file" represents the key that you are using for holding the file
setFieldValue is obtained from <Formik />
reference : formik setFieldValue prop
your code will look like :
const AddImage = (props) => {
const {handleSubmit, values, handleChange, setFieldValue } = useFormik({
initialValues: {
title: '',
image: '' // Did I write correctly here?
},
validateOnchange: false,
onSubmit: async (formValues) => {
const response = await api(`${imageRoutePath}`, {
method:'POST',
body: JSON.stringify(formValues),
});},
});
return (
<div>
<form onSubmit={handleSubmit}>
<InputTitle
label="title"
id="title"
inputProps={{
name:'title',
value: values.title,
onChange: handleChange,
}}
/>
<InputImage
label="image"
id="image"
inputProps={{
name:'file',
id="file",
// WHAT I SHOULD WRITE THERE?
type="file",
onChange={(event) => {
setFieldValue("file", event.currentTarget.files[0]);
}},
}}
/>
<button type="submit" disabled={isSubmitting}>Add</button>
</form>
</div>
);
};
export default AddImage;

How to Validate Multiple Phone Numbers with Formik Yup

I am building a form a where a user can store multiple phone numbers and set the privacy of that number (whether he wants the number to be publicly displayed or not).
I have succesfully created the form but stuck in formik-yup validation and getting the values (phone number with privacy value) to further send it to backend.
import React, { Component } from "react";
import { Formik, Form, Field, ErrorMessage, FieldArray } from "formik";
import * as Yup from "yup";
import DropDown from "./DropDown";
const phoneNumberPrivacyDropdown = [
{ value: "private", option: "Private" },
{ value: "public", option: "Public" }
];
const phoneRegExp = /^((\\+[1-9]{1,4}[ \\-]*)|(\\([0-9]{2,3}\\)[ \\-]*)|([0-9]{2,4})[ \\-]*)*?[0-9]{3,4}?[ \\-]*[0-9]{3,4}?$/; // for Mobile Numbers
export default class PhoneNumbers extends Component {
constructor(props) {
super(props);
this.state = {
phoneNumbers: [{ privacy: "", number: "" }]
};
this.addNewPhoneNumber = this.addNewPhoneNumber.bind(this);
this.removePhoneNumber = this.removePhoneNumber.bind(this);
}
addNewPhoneNumber() {
let newPhoneNumber = { privacy: "", number: "" };
this.setState({
phoneNumbers: [...this.state.phoneNumbers, newPhoneNumber]
});
}
removePhoneNumber = key => {
let phoneNumbers = this.state.phoneNumbers;
phoneNumbers.splice(key, 1);
this.setState({
phoneNumbers: phoneNumbers
});
};
render() {
return (
<div>
<h1 className="form-subHead">Multiple Phone Numbers.</h1>
<Formik
initialValues={{
phoneNumbers: this.state.phoneNumbers
}}
validationSchema={Yup.object().shape({
phoneNumbers: Yup.array().of(
Yup.object().shape({
mobile: Yup.string()
.required("Please tell us your mobile number.")
.length(10, "Please enter a valid mobile number.")
.matches(phoneRegExp, "Please enter a valid mobile number.")
})
)
})}
onSubmit={(values, { resetForm, setErrors, setSubmitting }) => {
setTimeout(() => {
console.log("Getting form values - ", values);
setSubmitting(false);
}, 500);
}}
enableReinitialize={true}
>
{props => {
const {
values,
touched,
dirty,
errors,
isSubmitting,
handleChange,
handleBlur,
setFieldValue,
setFieldTouched
} = props;
return (
<Form id="signUpForm" className="signinupForm mt-4" noValidate>
<div className="formSection">
<div className="row form-row">
<div className="col-sm-6">
<div className="form-group phoneNumberGroup">
<label htmlFor="phoneNumbers" className="form-label">
Phone Number
</label>
{this.state.phoneNumbers.map((phoneNumber, i) => (
<div className="phoneNumber" key={i}>
<Field
type="number"
name={`phone${i + 1}`}
placeholder="Contact Number"
className="form-control mobileNumber"
/>
<div className="selectPhonePrivacy">
<DropDown
items={phoneNumberPrivacyDropdown}
inputname="phonePrivacy"
showIconOnly={false}
/>
</div>
{i != 0 && (
<span
className="modifyInput removeElement"
onClick={() => this.removePhoneNumber({ i })}
>
-
</span>
)}
<ErrorMessage
name="mobile"
component="span"
className="invalid-input"
/>
</div>
))}
{this.state.phoneNumbers.length <= 2 && ( // only upto 3 phone numbers allowed!
<a
className="modifyInput addElement"
onClick={this.addNewPhoneNumber}
>
Add Another Phone
</a>
)}
</div>
{/* Phone Numbers Group */}
</div>
</div>
</div>
<div className="text-center">
<button
type="submit"
className="btn btn-filled"
disabled={!dirty || isSubmitting}
>
Finish
</button>
{/*Submit */}
</div>
</Form>
);
}}
</Formik>
</div>
);
}
}
Here is a codesandbox demo for the actual codes -
https://codesandbox.io/s/dynamic-field-validation-7t1ww

not able to display material-ui dropdown with props values

I am trying to incorporate material-ui DropDown element into my page but I am getting map errors
My page code
import React, {Component} from 'react';
import {Button, Dropdown, Form} from 'semantic-ui-react'
import SimpleReactValidator from 'simple-react-validator';
import cookie from 'react-cookies'
import {Redirect} from 'react-router-dom'
const jsonData = require('../../../../../jsonData/data.json');
const dummyData = require('../../../../../jsonData/dummy.json');
import auth, {
axiosInst as axios,
removeElement,
addElement,
beforeSubmiteShaker,
afterSubmiteShaker
} from "../../../../helper";
import QuestionsFilters from '../../../../components/QuestionsFilters'
import DropDown from '../../../../components/ui/Select/Select'
const items = [
{value: 1, primaryText: 'Dhaval'},
{value: 2, primaryText: 'Dhavalu'},
{value: 3, primaryText: 'Dhavalaa'},
]
class RegisterCandidate extends Component {
constructor(props) {
super(props);
this.state = {
firstname: "",
lastname: "",
mobile_no: "",
email: "",
password: "",
city_id: "",
preference_list: [],
medium: "", //how did u find us
is_looking_for: "",
affiliate: "",
revert: "",
emailExist: "",
mobileExist: "",
isLogin: false,
historyRoute: "",
};
this.validator = new SimpleReactValidator();
}
componentDidMount() {
this.props.fetchCities()
this.props.fetchPreferences()
}
change(e) {
this.setState({
[e.target.name]: e.target.value
})
};
dropDownChange(e, {value, name}) {
this.setState({
[name]: value
})
}
emailcheck(e) {
if (this.validator.fieldValid('email')) {
let uri = '/email/exists';
axios.get(uri, {
params: {email: this.state.email}
})
.then((response) => {
this.setState({emailExist: response.data});
if (response.data.status != 'Failed') {
removeElement("p[id='serv-error email-exist']")
return axios.get('/droppedregistrations/add', {
params: {email: this.state.email}
}).then((response) => {
console.log("Email Dropped " + response.data)
}).catch((error) => {
console.log('failed')
})
} else {
removeElement("p[id='serv-error email-exist']")
addElement("#email", "serv-error email-exist", "This email already exists.")
}
})
.catch(error => {
console.log('error Email')
})
}
}
mobilecheck(e) {
if (this.state.mobile_no != isNaN && this.state.mobile_no.length == 10) {
let uri = '/number/exists';
axios.get(uri, {
params: {mobile_no: this.state.mobile_no}
}).then((response) => {
this.setState({mobile_noExist: response.data});
if (response.data.status != 'Failed') {
removeElement("p[id='serv-error mobile-exist']")
return axios.get('/droppedregistrations/add', {
params: {mobile_no: this.state.mobile_no}
}).then((response) => {
console.log("mobile dropped " + response.data)
}).catch((error) => {
console.log('failed')
})
} else {
removeElement("p[id='serv-error mobile-exist']")
addElement("#mobile_no", "serv-error mobile-exist", "This mobile already exists.")
}
})
.catch(error => {
console.log('You are experiencing slow internet please be patient and try again later')
})
}
}
addUserSubmit(e) {
e.preventDefault();
beforeSubmiteShaker("s_reg_submit", "shaker")
if (this.validator.allValid()) {
const userDetails = {//add data to a donstant to pot
firstname: this.state.firstname,
lastname: this.state.lastname,
email: this.state.email,
password: this.state.password,
mobile_no: this.state.mobile_no,
city_id: this.state.city_id,
medium: this.state.medium,
is_looking_for: this.state.is_looking_for,
preference_list: this.state.preference_list,
affiliate: this.state.affiliate
}
let uri = '/register/candidate';
axios.post(uri, userDetails)
.then((response) => {
this.setState({revert: response.data});
const gotBack = this.state.revert
if (gotBack.status === 'Success') {
cookie.remove('token')
cookie.save('token', gotBack.token, {path: '/'})
if (cookie.load('token')) {
this.setState({isLogin: true})
}
else {
console.log('Something went wrong while redirect.')
}
}
})
.catch(error => {
if (error.response.status === 422) {
afterSubmiteShaker("s_reg_submit", "shaker")
$.each(error.response.data.errors, function (index, value) {
var errorDiv = '#' + index;
$(errorDiv).after("<p id='serv-error' class='validation-message'>" + value + "</p>");
});
}
})
} else {
this.validator.showMessages();
this.forceUpdate();
afterSubmiteShaker("s_reg_submit", "shaker")
}
}
render() {
const {
firstname,
lastname,
mobile_no,
email,
password,
city_id,
preference_list,
medium,
is_looking_for,
affiliate,
isLogin
} = this.state;
//to get the items in level of education
let looking_for = jsonData.looking_for;
var looking_fors = []
for (let i = 0; i < looking_for.length; i++) {
looking_fors.push(looking_for[i]['value']);
}
if (isLogin) {
return <Redirect to='/register/candidate/step2'/>;
} else {
return (
<section className="container mt-3">
{/*one click register section*/}
<div className="row p-3">
<div className="col-md-6">
<div className="card text-center">
<div className="card-body">
<h3>Register Using</h3>
<br/>
<a href="/" className={"btn btn-block btn-lg btn-info"}> Register with Facebook</a>
<a href="/google/redirect" className={"btn btn-block btn-lg btn-danger"}> Register
with Google</a>
<br/>
</div>
</div>
</div>
{/*end of one click register section*/}
{/*manuel register section*/}
<div className="col-md-6">
<div id="shaker" className="card">
<div className="card-body">
<h5 className="card-title">Register Candidate</h5>
<Form onSubmit={this.addUserSubmit.bind(this)}>
<Form.Field>
<label>First Name*</label>
<input name="firstname" id="firstname" value={firstname}
onChange={e => this.change(e)} type="text"
placeholder='Enter First Name*'/>
{this.validator.message('first name', firstname, 'required|alpha')}
</Form.Field>
<Form.Field>
<label>Last Name*</label>
<input name="lastname" id="lastname" value={lastname}
onChange={e => this.change(e)} type="text"
placeholder='Enter Last Name'/>
{this.validator.message('last name', lastname, 'required|alpha')}
</Form.Field>
<Form.Field>
<label>Mobile No*</label>
<input name="mobile_no" id="mobile_no" value={mobile_no}
onBlur={this.mobilecheck.bind(this)} onChange={e => this.change(e)}
type="text" placeholder='Enter mobile_no'/>
{this.validator.message('mobile no.', mobile_no, 'required|phone|min:10|max:10')}
</Form.Field>
<Form.Field>
<label>Email*</label>
<input name="email" id="email" value={email}
onBlur={this.emailcheck.bind(this)} onChange={e => this.change(e)}
type="text" placeholder='Enter Email'/>
{this.validator.message('email', email, 'required|email')}
</Form.Field>
<Form.Field>
<label>Password*</label>
<input name="password" id="password" value={password}
onChange={e => this.change(e)} type="password"
placeholder='Enter Password'/>
{this.validator.message('password', password, 'required|min:6')}
</Form.Field>
<Form.Field>
<label>City*</label>
<Dropdown placeholder='Select City' id="city_id" name="city_id" search
selection options={this.props.city_opt} value={city_id}
onChange={this.dropDownChange.bind(this)}/>
{this.validator.message('city', city_id, 'required|gt:0')}
</Form.Field>
<Form.Field>
<label>Preference*</label>
<Dropdown placeholder='Select Preference' id="preference_list"
name="preference_list" search multiple selection
options={this.props.preference_list_opt} value={preference_list}
onChange={this.dropDownChange.bind(this)}/>
{this.validator.message('preference', preference_list, 'required|max:3', false, {max: 'Maximum 3 preferences allowed'})}
</Form.Field>
<Form.Field>
<label>How did you find us?*</label>
<Dropdown placeholder='Please Select' id="medium" name="medium" search
selection options={jsonData.medium} value={medium}
onChange={this.dropDownChange.bind(this)}/>
{this.validator.message('medium', medium, 'required')}
</Form.Field>
<Form.Field>
<label>What are you looking for?*</label>
<Dropdown placeholder='Please Select' id="is_looking_for"
name="is_looking_for" search selection
options={jsonData.looking_for} value={is_looking_for}
onChange={this.dropDownChange.bind(this)}/>
{this.validator.message('', is_looking_for, 'required|in:' + looking_fors)}
</Form.Field>
<Form.Field>
<label>affiliate Code</label>
<input name="affiliate" value={affiliate} onChange={e => this.change(e)}
type="text" placeholder='Enter preference_list'/>
</Form.Field>
<Button id="s_reg_submit" className={"btn btn-block btn-lg"}
type='submit'>Submit</Button>
</Form>
<QuestionsFilters/>
</div>
</div>
</div>
{/*end of manuel register section*/}
<DropDown items={items}/>
</div>
</section>
)
}
}
}
export default RegisterCandidate;
my DropDown component code
import React, {Component} from 'react';
import DropDownMenu from 'material-ui/DropDownMenu';
import MenuItem from 'material-ui/MenuItem';
const styles = {
customWidth: {
width: 200,
},
};
class DropDownMenuSimpleExample extends Component {
constructor(props) {
super(props);
this.state = {value: 1, items: ['abcd', 'efgh']};
}
handleChange(event, index, value) {
return this.setState({value})
}
render() {
let propItems = this.props.items
return (
<DropDownMenu
value={this.state.value}
onChange={this.handleChange.bind(this)}
style={styles.customWidth}
autoWidth={false}
>
{(propItems !== null || propItems != 'undefined' ) ? (propItems).map((item, index) => <MenuItem key={index} value={1}
primaryText="Custom height"/>) : (this.state.items).map((item, index) =>
<MenuItem key={index} value={1} primaryText="Custom width"/>)}
</DropDownMenu>
);
}
}
export default DropDownMenuSimpleExample
it is telling me that It cannot call map on null and in the first render the value of props shows undefined but then it is defined so not able to get to render the DropDown element properly..
any help will be highly appreciated
Please Note :- with just the state I get the proper dropdown but the issue happens only when I am passing props as data for the dropdown

Categories