wrap a response data with object in javascript - javascript

I have a component which makes a POST network call. The component is:
import { ErrorMessage, Field, Formik, FormikHelpers } from "formik";
import { Button } from "../../../common/ui/Button";
import * as yup from "yup";
// import { RadioSwitch } from "#components/common/RadioSwitch";
import { addPersonalInfo } from "#request/profile";
import { toast } from "react-toastify";
type FormValues = {
division: string;
district: string;
thana: string;
house: string;
// same_address: boolean;
};
type IReqBody = {
division: string;
district: string;
thana: string;
house: string;
// same_address: boolean;
};
const initialValues: FormValues = {
division: "",
district: "",
thana: "",
house: "",
// same_address: false,
};
const AddPersentAddressSchema = yup.object({
division: yup.string().required("Division is required"),
district: yup.string().required("District is required"),
thana: yup.string().required("Thana is required"),
house: yup.string().required("House is required"),
});
export const AddPersentAddressForm = ({
close,
}: {
close: () => void;
}): JSX.Element => {
const handleSubmit = async (
values: FormValues,
actions: FormikHelpers<FormValues>
) => {
actions.setSubmitting(true);
const {
division,
district,
thana,
house,
// same_address,
} = values;
const reqbody: IReqBody = {
division: division,
district: district,
thana: thana,
house: house,
// same_address: same_address,
};
try {
const res = await addPersonalInfo(reqbody);
if (res.status == 201) {
toast.success("Your request is being processed");
actions.setSubmitting(false);
close();
} else {
actions.setSubmitting(false);
}
} catch (err) {
toast.error("Failed to Add Present Address");
actions.setSubmitting(false);
}
};
return (
<Formik
initialValues={initialValues}
onSubmit={handleSubmit}
validationSchema={AddPersentAddressSchema}
>
{(formikBag) => (
<form onSubmit={formikBag.handleSubmit}>
<div className="p-6">
<h3 className="mb-6">Add Present Address</h3>
<div className="mb-4">
<label className="space-y-2">
<p>Division *</p>
<Field
type="text"
className="form-input"
name="division"
placeholder="Dhaka"
/>
<ErrorMessage
className="text-xs text-red-500"
component="p"
name="division"
/>
</label>
</div>
<div className="mb-4">
<label className="space-y-2">
<p>District *</p>
<Field
type="text"
className="form-input"
name="district"
placeholder="Dhaka"
/>
<ErrorMessage
className="text-xs text-red-500"
component="p"
name="district"
/>
</label>
</div>
<div className="mb-4">
<label className="space-y-2">
<p>Thana *</p>
<Field
type="text"
className="form-input"
name="thana"
placeholder="Dhanmondi"
/>
<ErrorMessage
className="text-xs text-red-500"
component="p"
name="thana"
/>
</label>
</div>
<div className="mb-4">
<div>
<label className="space-y-2">
<p>House/Road/Village *</p>
<Field
name="house"
as="textarea"
className="form-textarea"
placeholder="Aa"
/>
</label>
</div>
</div>
{/* <div className="mb-4">
<div className="flex justify-between">
<label className="space-y-2">
<p>Same as Permanent Address</p>
</label>
<RadioSwitch
isChecked={false}
// eslint-disable-next-line #typescript-eslint/no-empty-function
onChange={() => {}}
/>
</div>
</div> */}
<div className="flex justify-end">
<Button
type="button"
onClick={() => close()}
size="sm"
variant="text"
style={{ marginRight: 12 }}
>
Cancel
</Button>
<Button
type="submit"
size="sm"
variant="primary"
isLoading={formikBag.isSubmitting}
disabled={formikBag.isSubmitting}
style={{ marginRight: 12 }}
>
Save
</Button>
</div>
</div>
</form>
)}
</Formik>
);
};
If I make this network call the request payload is:
{
"division": "Dhaka",
"district": "DHAKA-NORTH",
"thana": "Dhanmondi",
"house": "Manama Tower"
}
Here is the network call function:
export async function addPersonalInfo(reqbody: any) {
try {
const apiUrl = `${process.env.EJOB}/api/v1.0.0/applicants/applicant/profile/`;
const res = await axios.post(apiUrl, reqbody, {
headers: {
Authorization: "Bearer " + getTokenFromCookies(), //the token is a variable which holds the token
},
});
return Promise.resolve(res);
} catch (error) {
return Promise.reject(error);
}
}
The API expects a valid response like this format:
{
"permanent_address": {
"house": "Manama Tower",
"division": "dhaka",
"district": "dhaka",
"thana": "dhanmondi"
}
}
I tried to wrap up the final response body with {permanent_address:{req}} but nothing seems working. What should I change so that I make a valid request with this code?

Because you are passing the type of IReq to this line, which does not have the structure you want:
const res = await addPersonalInfo(reqbody);
you can change this line to add a new variable and then try:
const temp = {permanent_address:reqbody};
const res = await addPersonalInfo(temp);
const reqbody = { "house": "Manama Tower", "division": "dhaka", "district": "dhaka", "thana": "dhanmondi" };
const temp = {permanent_address:reqbody};
console.log(temp)
then it should be working.
you can also check the jsfiddle

Related

Firebase stops saving when i use refresh

I want to refresh my page after save of newRider to Firebase. But when I use window.location.reload(); or with (false) it does not save. Without it it works.
And is it ok to have code that long in one file?
import React from "react";
import { RidersDB } from "../../Backend/DataBase/RidersDB";
const ridersDB = new RidersDB();
export default function CrewMemberSetCreate() {
const [newIsShown, setNewIsShown] = React.useState(false);
const [newRider, setNewRider] = React.useState({
firstName: "",
lastName: "",
age: 0,
favTrick: "",
dreamTrick: "",
youtube: "",
instagram: "",
isShown: newIsShown,
img: "",
});
function handleChange(event) {
setNewRider((prevNewRider) => {
return {
...prevNewRider,
[event.target.name]: event.target.value,
};
});
}
const createRider = (event) => {
event.preventDefault();
ridersDB.createRider({
firstName: newRider.firstName,
lastName: newRider.lastName,
age: Number(newRider.age),
favTrick: newRider.favTrick,
dreamTrick: newRider.dreamTrick,
youtube: newRider.youtube,
instagram: newRider.instagram,
isShown: newIsShown,
img: "",
});
//here i want reload and tried to usewindow.location.reload(false);
};
return (
<div className="setCreate">
<h2>Add Rider</h2>
<div className="centerForm">
<form>
<input
type="text"
placeholder="First Name"
onChange={handleChange}
value={newRider.firstName}
name="firstName"
/>
<input
type="text"
placeholder="Last Name"
onChange={handleChange}
value={newRider.lastName}
name="lastName"
/>
<input
type="number"
placeholder="Age (number)"
min="1"
onChange={handleChange}
value={newRider.age}
name="age"
/>
<input
type="text"
placeholder="Favourite Trick"
onChange={handleChange}
value={newRider.favTrick}
name="favTrick"
/>
<input
type="text"
placeholder="Dream Trick"
onChange={handleChange}
value={newRider.dreamTrick}
name="dreamTrick"
/>
<input
type="text"
placeholder="Youtube Link"
onChange={handleChange}
value={newRider.youtube}
name="youtube"
/>
<input
type="text"
placeholder="Instagram Link"
onChange={handleChange}
value={newRider.instagram}
name="instagram"
/>
<div className="checkboxDiv">
<label>Show on home page?</label>
<input
type="checkbox"
onClick={() => {
setNewIsShown((prevState) => !prevState);
}}
/>
</div>
<button onClick={createRider}>Add Rider</button>
</form>
</div>
</div>
);
}
RidersDB.js
import { db } from "./firebase-config";
import { addDoc, collection, getDocs } from "firebase/firestore";
export class RidersDB {
constructor() {
this.ridersCollRef = collection(db, "ridersCrew");
this.createRider = async (riderData) => {
await addDoc(this.ridersCollRef, riderData);
};
this.getRiders = async () => {
const data = await getDocs(this.ridersCollRef);
return data.docs.map((doc) => ({ ...doc.data(), id: doc.id }));
};
}
}
The createRider() methods returns a Promise. You should wait for it to resolve and then proceed. Try refactoring the code as shown below:
const createRider = (event) => {
event.preventDefault();
return ridersDB.createRider({
firstName: newRider.firstName,
// ....
}).then(() => {
// resolved, proceed now
window.location.reload(false)
}).catch((e) => console.log(e));
};

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

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

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

Fetch data from API when form's search button clicked and show data on another page in React JS

I am developing a React JS web application where I have a form with four select fields (Make, Model, Min price and Max price) and a Search button. The data for search results will be fetched from API according to the selection of options. I want to show that data on another page in a card (page route path: /search) when user clicked on search button. I am using react router. The API url/end point is https://mysterious-journey-51969.herokuapp.com/api/search-vehicle/?q=mercedes&m=sprinter&pf=0&pt=100000 where "q" field matches Vehicle Make, "m" field matches Model, "pf" field matches Min Price, "pt" field matches Max Price. How I can do that?
Here is my Form component code:
import React, { Component } from 'react';
import { Form, FormGroup, Input } from 'reactstrap';
import { veh_data } from '../shared/vehicle_make_and_models';
const defaultValues = [
{ value: 0, text: 0, key: 1 },
{ value: 500, text: 500, key: 2 },
{ value: 1000, text: 1000, key: 3 },
{ value: 1500, text: 1500, key: 4 },
{ value: 2000, text: 2000, key: 5 },
{ value: 2000, text: 2000, key: 6 }
];
const MIN_TITLE = { selected: true, disabled: true, text: 'Min Price' };
const MAX_TITLE = { selected: true, disabled: true, text: 'Max Price' };
class ImgAndForm extends Component {
constructor(props) {
super(props);
this.handleSearch = this.handleSearch.bind(this);
this.keyToOption = this.keyToOption.bind(this);
this.renderOptions = this.renderOptions.bind(this);
this.handleModelChange = this.handleModelChange.bind(this);
this.state = {
minData: [MIN_TITLE, ...defaultValues],
maxData: [MAX_TITLE, ...defaultValues],
minValue: null,
maxValue: null,
modelSelected: null
};
}
renderOptions(data) {
return data.map(datum => {
// this allows us to indicate whether we are selecting or disabling
const selected = datum.selected || false;
const disabled = datum.disabled || false;
return (
<option key={datum.key} value={datum.value} selected={selected} disabled={disabled}>
{datum.text}
</option>
);
});
}
handleModelChange(event) {
console.log(event.target.value);
this.setState({ modelSelected: event.target.value });
}
handleSearch(event) {
alert("Search button clicked");
}
keyToOption(key) {
return key.split("-")
.map(word => word.slice(0, 1).toUpperCase() + word.slice(1))
.join(" ");
}
handleMinSelect = event => {
const value = event.target.value;
const newMaxValues = [];
defaultValues.forEach(datum => {
if (datum.value >= Number.parseInt(value, 10)) {
newMaxValues.push(datum);
}
});
this.setState({
maxData: [MAX_TITLE, ...newMaxValues],
minValue: value
});
};
handleMaxSelect = event => {
const value = event.target.value;
this.setState({ maxValue: value });
};
render() {
const vehicles = veh_data.reduce((acc, veh, i) => {
let make = Object.keys(veh)[0],
vehModels = veh[make];
return {
makes: [
...acc.makes,
<option key={make + i} value={make}>{this.keyToOption(make)}</option>
],
models: {
...acc.models,
[make]: vehModels.map((model, i) => {
return (
<option key={make + model + i} value={model}>
{this.keyToOption(model)}
</option>
);
})
}
};
}, { makes: [], models: [] });
const selectedModels =
this.state.modelSelected && this.state.modelSelected.length ? (
vehicles.models[this.state.modelSelected]
) : (
<option value="">Model (select make first)</option>
);
return (
<div>
<header className="headerbg d-flex">
<div className="container my-auto">
<div className="row">
<div className="offset-1 col-10 offset-lg-0 col-lg-4">
<div id="search-form-div" className="container">
<div className="row">
<div className="col-12 my-4">
<h3>Search</h3>
<Form onSubmit={this.handleSearch}>
<FormGroup>
<Input
onChange={e => this.handleModelChange(e)}
type="select"
name="q"
id="q"
>
<option value="">Make</option>
{vehicles.makes}
</Input>
</FormGroup>
<FormGroup>
<Input type="select" name="m" id="m">
{selectedModels}
</Input>
</FormGroup>
<FormGroup>
<Input type="select"
name="pf"
id="pf"
value={this.state.minValue}
onChange={this.handleMinSelect}>
{this.renderOptions(this.state.minData)}
</Input>
</FormGroup>
<FormGroup>
<Input
type="select"
name="pt"
id="pt"
value={this.state.maxValue}
onChange={this.handleMaxSelect}>
{this.renderOptions(this.state.maxData)}
</Input>
</FormGroup>
<FormGroup>
<Input type="submit" name="search" id="search" className="btn btn-primary" value="Search" />
</FormGroup>
</Form>
</div>
</div>
</div>
</div>
</div>
</div>
</header>
</div>
);
}
}
export default ImgAndForm;
Here is my Search result component code:
import React, { Component } from 'react';
import Smallheader from './SmallHeader';
import { Card, CardImg, CardTitle, CardSubtitle } from 'reactstrap';
class SearchResult extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<div>
<Smallheader />
<div className="my-5">
<div className="container text-center" id="contactContainer">
<div className="row">
<div className="col-lg-12 mx-auto">
<h2 className="text-center">Search Results</h2>
<hr className="my-4 thick-hr" />
</div>
</div>
<div className="row">
<div className="col-6 col-lg-3 mt-4">
<Card>
<a href="#">
<CardImg src="" className="img-fluid" />
<CardTitle>Title Here</CardTitle>
<CardSubtitle>Price Here</CardSubtitle>
</a>
</Card>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default SearchResult;
Here is a working solution...
https://codesandbox.io/s/lrv2w3qxlq?moduleview=1
I've imported your SearchResults component and put it directly below your ImgAndForm, but you can move it anywhere in that render function.
For this specific situation you would need a way to render this on a new 'page' you would need a way to manage shared application state, like Redux or at least a container component as #MikeZinn mentioned, but to do that properly would require as significant amount of work to implement the routing and re-architect your entire program. (If you want I can show you a small hack to produce the same result without that for now, but I'd advise looking into a more permanent solution.)
Since the SearchResults component can be 'stateless' I removed the constructor function, but I left it as a class for now because this component will likely need state eventually.
I added the axios library to fetch the data from the API, but any other XHR module already used in your program will do.
NOTE: Since the specific API endpoints that your form is currently able to query are unavailable, I've hard coded the 'mercedes' example you provided, but the program will log both 'realQuery' and 'dummyQuery' so you see that it is producing the correct query structure for whenever you fix that.
Form Component
import React, { Component } from "react";
import { Form, FormGroup, Input } from "reactstrap";
// import { veh_data } from '../shared/vehicle_make_and_models';
import SearchResult from "./result";
import axios from "axios";
const veh_data = [
{ "alfa-romeo": ["145", "90", "Alfa 6", "Alfasud"] },
{ "aston-martin": ["15", "2-Litre", "AM Vantage", "Atom", "Cygnet", "DB2"] },
{ audi: ["100", "200", "A1", "A2", "A3", "A4", "A5", "A6", "A7"] }
];
const defaultValues = [
{ value: 0, text: 0, key: 1 },
{ value: 500, text: 500, key: 2 },
{ value: 1000, text: 1000, key: 3 },
{ value: 1500, text: 1500, key: 4 },
{ value: 2000, text: 2000, key: 5 },
{ value: 2000, text: 2000, key: 6 }
];
const MIN_TITLE = { selected: true, disabled: true, text: "Min Price" };
const MAX_TITLE = { selected: true, disabled: true, text: "Max Price" };
class ImgAndForm extends Component {
constructor(props) {
super(props);
this.handleSearch = this.handleSearch.bind(this);
this.keyToOption = this.keyToOption.bind(this);
this.renderOptions = this.renderOptions.bind(this);
this.handleModelChange = this.handleModelChange.bind(this);
this.state = {
minData: [MIN_TITLE, ...defaultValues],
maxData: [MAX_TITLE, ...defaultValues],
minValue: "",
maxValue: "",
modelSelected: "",
makeSelected: "",
searchResults: ""
};
}
renderOptions(data) {
return data.map(datum => {
// this allows us to indicate whether we are selecting or disabling
const selected = datum.selected || false;
const disabled = datum.disabled || false;
return (
<option
key={datum.key}
value={datum.value}
selected={selected}
disabled={disabled}
>
{datum.text}
</option>
);
});
}
handleModelChange(event) {
console.log(event.target.value);
this.setState({ modelSelected: event.target.value });
}
handleMakeChange(event) {
console.log(event.target.value);
this.setState({ makeSelected: event.target.value });
}
async handleSearch(event) {
event.preventDefault();
alert("Search button clicked");
let { makeSelected, modelSelected, minValue, maxValue } = this.state;
let realQuery =
"https://mysterious-journey-51969.herokuapp.com/api/search-vehicle/?" +
`q=${makeSelected.split("-").join("")}` +
`&m=${modelSelected.split("-").join("")}` +
`&pf=${minValue}` +
`&pt=${maxValue}`;
let dummyQuery =
"https://mysterious-journey-51969.herokuapp.com/api/search-vehicle/?q=mercedes&m=sprinter&pf=0&pt=100000";
console.log("realQuery (was not run)", realQuery);
console.log("dummyQuery (was run)", dummyQuery);
let res = await axios.get(dummyQuery).catch(err => console.log(err));
console.log("res", res.data);
if (res && res.data) {
this.setState(prevState => {
return {
...prevState,
searchResults: res.data
};
});
}
}
keyToOption(key) {
return key
.split("-")
.map(word => word.slice(0, 1).toUpperCase() + word.slice(1))
.join(" ");
}
handleMinSelect = event => {
const value = event.target.value;
const newMaxValues = [];
defaultValues.forEach(datum => {
if (datum.value >= Number.parseInt(value, 10)) {
newMaxValues.push(datum);
}
});
this.setState({
maxData: [MAX_TITLE, ...newMaxValues],
minValue: value
});
};
handleMaxSelect = event => {
const value = event.target.value;
this.setState({ maxValue: value });
};
render() {
const vehicles = veh_data.reduce(
(acc, veh, i) => {
let make = Object.keys(veh)[0],
vehModels = veh[make];
return {
makes: [
...acc.makes,
<option key={make + i} value={make}>
{this.keyToOption(make)}
</option>
],
models: {
...acc.models,
[make]: vehModels.map((model, i) => {
return (
<option key={make + model + i} value={model}>
{this.keyToOption(model)}
</option>
);
})
}
};
},
{ makes: [], models: [] }
);
const selectedModels =
this.state.makeSelected && this.state.makeSelected.length ? (
vehicles.models[this.state.makeSelected]
) : (
<option value="">Model (select make first)</option>
);
return (
<div>
<header className="headerbg d-flex">
<div className="container my-auto">
<div className="row">
<div className="offset-1 col-10 offset-lg-0 col-lg-4">
<div id="search-form-div" className="container">
<div className="row">
<div className="col-12 my-4">
<h3>Search</h3>
<Form onSubmit={this.handleSearch}>
<FormGroup key={1}>
<Input
onChange={e => this.handleMakeChange(e)}
type="select"
name="q"
id="q"
>
<option value="">Make</option>
{vehicles.makes}
</Input>
</FormGroup>
<FormGroup key={2}>
<Input
onChange={e => this.handleModelChange(e)}
type="select"
name="m"
id="m"
>
{selectedModels}
</Input>
</FormGroup>
<FormGroup key={3}>
<Input
type="select"
name="pf"
id="pf"
value={this.state.minValue}
onChange={this.handleMinSelect}
>
{this.renderOptions(this.state.minData)}
</Input>
</FormGroup>
<FormGroup key={4}>
<Input
type="select"
name="pt"
id="pt"
value={this.state.maxValue}
onChange={this.handleMaxSelect}
>
{this.renderOptions(this.state.maxData)}
</Input>
</FormGroup>
<FormGroup key={5}>
<Input
type="submit"
name="search"
id="search"
className="btn btn-primary"
value="Search"
/>
</FormGroup>
</Form>
<SearchResult results={this.state.searchResults} />
</div>
</div>
</div>
</div>
</div>
</div>
</header>
</div>
);
}
}
export default ImgAndForm;
Results Component
import React, { Component } from "react";
// import Smallheader from './SmallHeader';
import { Card, CardImg, CardTitle, CardSubtitle } from "reactstrap";
class SearchResult extends Component {
renderResults() {
let { results } = this.props;
console.log("results", results);
if (results && results.length) {
return results.map(({ price, text, title, remote_image }, i) => {
return (
<Card key={"card-" + i}>
<a href="#">
<CardImg src={remote_image} className="img-fluid" />
<CardTitle>{title}</CardTitle>
<CardSubtitle>{price}</CardSubtitle>
</a>
</Card>
);
});
}
}
render() {
return (
<div>
{/* <Smallheader /> */}
<div className="my-5">
<div className="container text-center" id="contactContainer">
<div className="row">
<div className="col-lg-12 mx-auto">
<h2 className="text-center">Search Results</h2>
<hr className="my-4 thick-hr" />
</div>
</div>
<div className="row">
<div className="col-6 col-lg-3 mt-4">{this.renderResults()}</div>
</div>
</div>
</div>
</div>
);
}
}
export default SearchResult;
This is exactly the type of problem Redux Solves without using Redux you will need to store the state on a shared parent component. For example,
class Search extends Component {
state = {
searchResult: null,
};
handleSearch = searchResult => {
this.setState({
searchResult,
});
}
render(){
const { searchResult, } = this.state;
if(searchResult === null){
return (
<ImgAndForm handleSearch={this.handleSearch} />
);
}
return (
<SearchResult searchResult={searchResult} />
);
}
}

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