I have a React.js App where I search pets as below
When the page first loads, I display some text and image. Then user makes a search and gets the results according to the search. But if there is no matching results, I want to display a message as "No Items were found"
How can I do this ?
My if condition is not working in the submit function.
Below is my code
Search.js
import React, { useState } from "react";
import SearchResults from "./SearchResults";
import { Container, Row, Col } from "react-bootstrap";
import { FaSearch } from "react-icons/fa";
import "./Search.css";
import { useFormik } from 'formik';
const Search = ({ searchGuests }) => {
const [searchResults, setSearchresults] = React.useState([])
const formik = useFormik({
initialValues: {
location: "",
age: "",
breed: "",
gender: "",
},
onSubmit: values => {
const searchItem = searchGuests.guestsResults.filter(item => {
return (
item.location
.toLowerCase()
.includes(
values.location.toLowerCase()
) &&
item.age
.toLowerCase()
.includes(
values.age.toLowerCase()
) &&
item.breed
.toLowerCase()
.includes(
values.breed.toLowerCase()
) &&
item.gender
.toLowerCase()
.includes(
values.gender.toLowerCase()
)
)
})
if(searchItem) {
return setSearchresults(searchItem)
} else {
return searchResults("No Items Were Found")
}
// return JSON.stringify(values, null, 2);
},
});
const LoadingPage = () => {
return (
<Col sm={12} className="loadPage">
<h4>Start Searching Now and Discover New Friends</h4>
<h6>They are waiting for their new home</h6>
<img src="https://timesofindia.indiatimes.com/photo/67586673.cms" className="w-100 searchCatLogo" />
</Col>
);
};
return (
<Container>
<Row className="searchContainer py-5">
<h1>{searchGuests.searchGuestsTitle.title}</h1>
<Col sm={12}>
<form onSubmit={formik.handleSubmit} className="searchForm">
<label htmlFor="location"></label>
<select id="location" name="location" value={formik.values.location} {...formik.getFieldProps('location')}>
<option value="Select Location">Select Location</option>
{searchGuests.locationCities.map(city => <option>{city}</option>)}
</select>
<label htmlFor="age"></label>
<input
id="age"
type="text"
placeholder="age"
{...formik.getFieldProps('age')}
/>
<label htmlFor="breed"></label>
<select id="breed" name="breed" value={formik.values.breed} {...formik.getFieldProps('breed')}>
<option value="Select Breed">Select Breed</option>
{searchGuests.searchBreed.map(breed => <option>{breed}</option>)}
</select>
<label htmlFor="gender"></label>
<select name="gender" value={formik.values.gender} {...formik.getFieldProps('gender')}>
<option value="Select Gender">Select Gender</option>
<option value="Female">Female</option>
<option value="Male">Male</option>
</select>
<button type="submit">
<FaSearch size={27} className="searchIcon" />
</button>
</form>
</Col>
<Col sm={12}>
<Row className="mt-5 items">
{searchResults.length > 0 ? searchResults.map(result => {
return (
<SearchResults
key={result.id}
img={result.img}
location={result.location}
age={result.age}
breed={result.breed}
gender={result.gender}
name={result.name}
/>
)
}) : <LoadingPage />}
</Row>
</Col>
</Row>
</Container>
);
};
export default Search;
SearchResults.js
import React, { useState } from "react";
import { Col, Button, Card } from "react-bootstrap";
import "./Search.css";
import { BsGeoAlt } from "react-icons/bs";
import { FaPaw } from "react-icons/fa";
import AdoptionFormModal from "./AdoptionFormModal"
const SearchResults = ({ img, id, location, age, breed, gender, name }) => {
const [modalShow, setModalShow] = useState(false);
return (
<>
<Col lg={4} className="mb-5">
<Card style={{ width: '18rem' }}>
<Card.Img variant="top" src={img} />
<Card.Body id="modalBody">
<Card.Text id="cardText">
<div className="firstDiv">
<p>{breed}</p>
<p>{name}</p>
<Button variant="primary" className="adoptButton shadow-none" onClick={() => setModalShow(true)}>
<FaPaw className="pawIcon"/>
Adopt
</Button>
</div>
<div className="secondDiv">
<p>{gender}, {age}</p>
<p className="mt-4"><BsGeoAlt size={25}/> {location}</p>
</div>
</Card.Text>
</Card.Body>
</Card>
</Col>
<AdoptionFormModal
show={modalShow}
onHide={() => setModalShow(false)}
/>
</>
);
};
export default SearchResults;
data.js
export let searchGuests = {
searchGuestsTitle: {
title: "Adopt Your New Pet",
},
guestsResults: [
{
id: 1,
img:
"https://imagesvc.meredithcorp.io/v3/mm/image?url=https%3A%2F%2Fstatic.onecms.io%2Fwp-content%2Fuploads%2Fsites%2F37%2F2020%2F09%2F22%2F50-cute-dog-names.jpg",
location: "Istanbul",
age: "2",
breed: "Poodle",
gender: "Female",
name: "Sushi",
},
{
id: 2,
img:
"https://imagesvc.meredithcorp.io/v3/mm/image?url=https%3A%2F%2Fstatic.onecms.io%2Fwp-content%2Fuploads%2Fsites%2F37%2F2020%2F09%2F22%2F50-cute-dog-names.jpg",
location: "Istanbul",
age: "1",
breed: "Terrier",
gender: "Male",
name: "Sushi",
},
{
id: 3,
img:
"https://imagesvc.meredithcorp.io/v3/mm/image?url=https%3A%2F%2Fstatic.onecms.io%2Fwp-content%2Fuploads%2Fsites%2F37%2F2020%2F09%2F22%2F50-cute-dog-names.jpg",
location: "Istanbul",
age: "2",
breed: "Poodle",
gender: "Female",
name: "Sushi",
},
],
locationCities : [
"Istanbul",
"Ankara",
"Izmir"
],
searchBreed : [
"Poodle",
"Terrier",
"Rottweiler",
"Golden Retriever",
"Cat",
"Tobby cat",
"Scottish"
],
searchAge : [
"<3",
">3",
"All"
]
};
You can render it based on the type of searchResults is a string or array
{/* Show result */}
{searchResults.length > 0 &&
Array.isArray(searchResults) &&
searchResults.map((result) => {
return (
<SearchResults
key={result.id}
img={result.img}
location={result.location}
age={result.age}
breed={result.breed}
gender={result.gender}
name={result.name}
/>
);
})}
{/* Show notfound */}
{searchResults.length > 0 && typeof searchResults === "string" && (
<div>{searchResults}</div>
)}
{/* Show loading */}
{searchResults.length === 0 && <LoadingPage />}
if(searchItem.length > 0) {
return setSearchresults(searchItem)
} else {
return searchResults("No Items Were Found")
}
First: You are directly mutated the state, so your component won't re-render:
return searchResults("No Items Were Found")
And also it's inefficient to save error state the same state you store your response, so create another state for that:
const [error, setError] = React.useState('')
And then set your error like this:
setError("No Items Were Found")
return;
Lastly, check for the existent of error and render it:
{error && <p>{error}</p>
Related
I'm kind of new with java and ReactJS and I have a big issue where when I tried to post my data, it posted a lot of unwanted data like rendering the whole table.
My console.log() printed this :
location_dest_id: 2
location_id: 1
origin: "test3"
picking_type_id: 1
stock_move_ids: Array(1)
0:
altKey: false
bubbles: true
button: 0
buttons: 0
cancelable: true
clientX: 317
clientY: 652
ctrlKey: false
currentTarget: null
defaultPrevented: false
demand: "12"
detail: 1
done: "0"
eventPhase: 3
getModifierState: ƒ modifierStateGetter(keyArg)
isDefaultPrevented: ƒ functionThatReturnsFalse()
isPropagationStopped: ƒ functionThatReturnsFalse()
isTrusted: true
metaKey: false
movementX: 0
movementY: 0
nativeEvent: PointerEvent {isTrusted: true, pointerId: 1, width: 1,
height: 1, pressure: 0, …}
pageX: 317
pageY: 754
product_tmpl_id: 9
product_uom: "1"
relatedTarget: null
screenX: 317
screenY: 723
shiftKey: false
target: span
timeStamp: 203547.59999990463
type: "click"
view: Window {window: Window, self: Window, document: document, name: '', location: Location, …}
_reactName: "onClick"
_targetInst: null
From what I understand, the error is when I tried to pass "stock_move_ids" nested data, it POST so much data as like the console.log() above. What it should be passed is like this :
[
{
"date":"02-09-2022",
"origin":"test2",
"picking_type_id":2,
"location_id":1,
"location_dest_id":2,
"stock_move_ids":
[
{
"demand":12,
"done":0,
"product_uom":1,
"product_tmpl_id":18
}
]
}
]
Is there any way to solve my problem? My code is based on this template : https://codesandbox.io/s/j0opp
Here's my code looks like :
Parent
import React, { useEffect, useContext } from "react";
import { Button, Form, Input, DatePicker, Select } from 'antd';
import { Stockmovetable } from "./Stockmovetable";
import { AppContext } from '../../../context/Appcontext'
const Stockpickingnew = ({ title }) => {
const { Function, State } = useContext(AppContext)
const { fetchDataPickingType, fetchDataLocation, fetchDataPupuk, option, stock_move_ids, StockPick, StockPickFailed } = Function
const { dateFormat, dataPupuk, dataStockLocation, dataStockPickingType } = State
useEffect(() => {
fetchDataPickingType()
fetchDataLocation()
fetchDataPupuk()
}, [])
return (
<>
<div className='new'>
<div className="top">
<h1>{title}</h1>
</div>
<div className="bottom">
<div className="stockPicking">
<Form
name="stockPickings"
layout="vertical"
onFinish={StockPick}
onFinishFailed={StockPickFailed}
autoComplete="off"
>
<div className="left">
<Form.Item
label="Origin :"
name='origin'
>
<Input placeholder="Origin" />
</Form.Item>
<Form.Item
label="Picking Type :"
name='picking_type_id'
>
<Select
placeholder="Picking Type"
options={dataStockPickingType.map(e => ({label: e.name, value: e.id}))}
/>
</Form.Item>
<Form.Item
label="Date :"
name='date'
>
<DatePicker
format={dateFormat}
/>
</Form.Item>
</div>
<div className="right">
<Form.Item
label="Location :"
name='location_id'
>
<Select
placeholder="Tujuan Awal"
options={dataStockLocation.map(e => ({label: e.name, value: e.id}))}
/>
</Form.Item>
<Form.Item
label="Destination :"
name='location_dest_id'
>
<Select
placeholder="Tujuan Akhir"
options={dataStockLocation.map(e => ({label: e.name, value: e.id}))}
/>
</Form.Item>
</div>
<div className="stockMove">
<Form.List name="stock_move_ids">
{(stock_move_ids, { add, remove }) => {
return <Stockmovetable stock_move_ids={stock_move_ids} option={option} add={add} remove={remove} dataPupuk={dataPupuk} />;
}}
</Form.List>
</div>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
</div>
</div>
</div>
</>
)
}
export default Stockpickingnew
Child
import React from "react";
import { Form, Input, Button, Table, Select } from "antd";
import { PlusOutlined, MinusOutlined } from "#ant-design/icons";
const { Column } = Table;
export const Stockmovetable = props => {
const { stock_move_ids, add, remove, dataPupuk, option } = props;
return (
<Table
dataSource={stock_move_ids}
pagination={false}
footer={() => {
return (
<Form.Item>
<Button onClick={add}>
<PlusOutlined /> Add field
</Button>
</Form.Item>
);
}}
>
<Column
dataIndex={"product_tmpl_id"}
title={"Nama Produk"}
render={(value, row, index) => {
return (
<Form.Item name={[index, "product_tmpl_id"]}>
<Select
placeholder="Produk"
options={dataPupuk.map(e => ({ label: e.name, value: e.id }))}
/>
</Form.Item>
);
}}
/>
<Column
dataIndex={"demand"}
title={"Demand"}
render={(value, row, index) => {
// console.log(row);
return (
<Form.Item name={[index, "demand"]}>
<Input
placeholder="Demand"
/>
</Form.Item>
);
}}
/>
<Column
dataIndex={"done"}
title={"Done"}
render={(value, row, index) => {
return (
<Form.Item name={[index, "done"]}>
<Select
placeholder="Tujuan Akhir"
options={option}
/>
</Form.Item>
);
}}
/>
<Column
dataIndex={"product_uom"}
title={"product_uom"}
render={(value, row, index) => {
return (
<Form.Item name={[index, "product_uom"]}>
<Input
placeholder="product_uom"
/>
</Form.Item>
);
}}
/>
<Column
title={"Action"}
render={(value, row, index) => {
return (
<React.Fragment>
<Button
icon={<MinusOutlined />}
shape={"circle"}
onClick={() => remove(row.name)}
/>
</React.Fragment>
);
}}
/>
</Table>
);
};
Context
export const AppContext = createContext()
export const AppProvider = props => {
const Navigate = useNavigate()
const dateFormat = ['DD-MM-YYYY'];
const StockPick = (values) => {
console.log('Success:', values);
let stockpick = [{
date: moment(values.date).format("DD-MM-YYYY"),
origin: values.origin,
picking_type_id: values.picking_type_id,
location_id: parseInt(values.location_id),
location_dest_id: parseInt(values.location_dest_id),
stock_move_ids: [
{
demand: parseInt(values?.stock_move_ids?.[0]?.demand),
done: parseInt(values?.stock_move_ids?.[0]?.done),
product_uom: parseInt(values?.stock_move_ids?.[0]?.product_uom),
product_tmpl_id: values?.stock_move_ids?.[0]?.product_tmpl_id,
},
],
}];
let params = JSON.stringify(stockpick)
console.log(params)
axios.post('http://127.0.0.1:5000/api/stockpickings', params, { headers })
.then(() => {
Navigate('/')
})
.catch(error => {
if (error.response) {
console.log(error.response);
}
});
};
}
I think this is all I can provide / needed to fix my codings, if there's anything I need to add and or fix, please tell me. Thank you.
Okay after some hours tries
as the comment above, i think all i need to do is just not making the body as dataindex.
from :
<Form.List name="stock_move_ids">
{(stock_move_ids, { add, remove }) => {
return <Stockmovetable stock_move_ids={stock_move_ids} option={option} add={add} remove={remove} dataPupuk={dataPupuk} />;
}}
</Form.List>
to
<Form.List name="stock_move_ids">
{(stock_move, { add, remove }) => {
return <Stockmovetable stock_move_ids={stock_move_ids} option={option} add={add} remove={remove} dataPupuk={dataPupuk} />;
}}
</Form.List>
Thank you very much for the helps!
I'm using Formik and windmill for the multiselect form, see picture below. The problem is that I implemented a search functionality to look into the array of options and now it appears that even though I'm selecting an option it is not passing the value to Formik and is not submitting the form. Any help would be greatly appreciated. Thanks!
form
import { React, useState } from 'react';
import { Formik, Form, Field, ErrorMessage } from 'formik';
import * as Yup from 'yup';
import { partService } from '../../services';
import { SearchIcon, AddIcon } from '../../icons';
import { Input, Label, Select, HelperText } from '#windmill/react-ui';
function CreateElementForm({ formRef, callback, partId }) {
const [searchTerm, setSearchTerm] = useState('');
const [iconSelection, setIconSelection] = useState(true);
const tempArray = [
{ name: 'data1', value: 'value1' },
{ name: 'data2', value: 'value2' },
{ name: 'data3', value: 'value3' },
{ name: 'data4', value: 'value4' },
];
return (
<Formik
innerRef={formRef}
initialValues={{
description: '',
percentage: '',
material: '',
}}
validationSchema={Yup.object().shape({
description: Yup.string().required('Element name is required'),
percentage: Yup.number().required('Percentage is required'),
material: Yup.string().required('Material is required'),
})}
onSubmit={(
{ description, percentage, material },
{ setStatus, setSubmitting }
) => {
setStatus();
setTimeout(async () => {
await partService
.createElement(partId, description, percentage, material)
.then(
(response) => {
callback(true);
},
(error) => {
if (error.response) {
setStatus(error.response.data.message);
} else {
setStatus('Some error occured.');
}
callback(false);
}
);
}, 400);
}}
>
{({ errors, status, touched, isSubmitting, setFieldValue, values }) => (
<Form>
<Label>
<span>Description</span>
<Field
className='mt-1'
as={Input}
name='description'
type='text'
placeholder='Part Element'
/>
<ErrorMessage name='name'>
{(msg) => <HelperText valid={false}>{msg}</HelperText>}
</ErrorMessage>
</Label>
<Label>
<span>Percentage</span>
<Field
className='mt-1'
as={Input}
name='percentage'
type='number'
placeholder='%'
/>
<ErrorMessage name='percentage'>
{(msg) => <HelperText valid={false}>{msg}</HelperText>}
</ErrorMessage>
</Label>
<Label>
<span>Search Field</span>
<div className='relative text-gray-500 focus-within:text-purple-600 dark:focus-within:text-purple-400'>
<Input
className='mt-1 pl-10 text-black dark:text-gray-300'
placeholder='Jane Doe'
onChange={(event) => {
setSearchTerm(event.target.value);
}}
/>
<div className='absolute inset-y-0 flex items-center ml-3 pointer-events-none'>
{iconSelection ? (
<SearchIcon className='w-5 h-5' aria-hidden='true' />
) : (
<AddIcon className='w-5 h-5' aria-hidden='true' />
)}
</div>
</div>
</Label>
<Label className='mt-4'>
<span>Multiselect</span>
<Field
className='mt-1'
as={Select}
name='material'
multiple={true}
type='text'
onChange={(evt) =>
setFieldValue('material', evt.target.selectedOptions.val)
}
>
{tempArray
.filter((value) => {
if (searchTerm === '') {
return value;
} else if (
value.name.toLowerCase().includes(searchTerm.toLowerCase())
) {
return value;
}
})
.map((i) => (
<option key={i.name} val={i.value}>
{i.name}
</option>
))}
</Field>
</Label>
{status && <HelperText valid={false}>{status}</HelperText>}
</Form>
)}
</Formik>
);
}
export default CreateElementForm;
I have a React.js app. I have a search page component where i search pets by location, age, breed and gender. My search inputs are all select box ( i used formik for form handling ). I have a problem with age input. I want to make this as a select box where i can choose some age ranges ( < 3, > 3, All ). But I dont know how to make this as a data and in the filter part. Other inputs are all working but age does not work since it is a string as i assume. And I am not very good at coverting strings to numbers.
Here is my code below
Data.js
export let searchGuests = {
searchGuestsTitle: {
title: "Adopt Your New Pet",
},
guestsResults: [
{
id: 1,
img:
"https://imagesvc.meredithcorp.io/v3/mm/image?url=https%3A%2F%2Fstatic.onecms.io%2Fwp-content%2Fuploads%2Fsites%2F37%2F2020%2F09%2F22%2F50-cute-dog-names.jpg",
location: "Istanbul",
age: "2",
breed: "Poodle",
gender: "Female",
name: "Sushi",
},
{
id: 2,
img:
"https://imagesvc.meredithcorp.io/v3/mm/image?url=https%3A%2F%2Fstatic.onecms.io%2Fwp-content%2Fuploads%2Fsites%2F37%2F2020%2F09%2F22%2F50-cute-dog-names.jpg",
location: "Istanbul",
age: "1",
breed: "Terrier",
gender: "Male",
name: "Sushi",
},
{
id: 3,
img:
"https://imagesvc.meredithcorp.io/v3/mm/image?url=https%3A%2F%2Fstatic.onecms.io%2Fwp-content%2Fuploads%2Fsites%2F37%2F2020%2F09%2F22%2F50-cute-dog-names.jpg",
location: "Istanbul",
age: "2",
breed: "Poodle",
gender: "Female",
name: "Sushi",
},
],
locationCities : [
"Istanbul",
"Ankara",
"Izmir"
],
searchBreed : [
"Poodle",
"Terrier",
"Rottweiler",
"Golden Retriever",
"Cat",
"Tobby cat",
"Scottish"
],
searchAge : [
"<3",
">3",
"All"
]
};
Search.js
import React, { useState } from "react";
import SearchResults from "./SearchResults";
import { Container, Row, Col } from "react-bootstrap";
import { FaSearch } from "react-icons/fa";
import "./Search.css";
import { useFormik } from 'formik';
const Search = ({ searchGuests }) => {
const [searchResults, setSearchresults] = React.useState([])
const formik = useFormik({
initialValues: {
location: "",
age: "",
breed: "",
gender: "",
},
onSubmit: values => {
const searchItem = searchGuests.guestsResults.filter(item => {
return (
item.location
.toLowerCase()
.includes(
values.location.toLowerCase()
) &&
item.age
.toLowerCase()
.includes(
values.age.toLowerCase()
) &&
item.breed
.toLowerCase()
.includes(
values.breed.toLowerCase()
) &&
item.gender
.toLowerCase()
.includes(
values.gender.toLowerCase()
)
)
})
return setSearchresults(searchItem)
// return JSON.stringify(values, null, 2);
},
});
const LoadingPage = () => {
return (
<Col sm={12} className="loadPage">
<h4>Start Searching Now and Discover New Friends</h4>
<h6>They are waiting for their new home</h6>
<img src="https://timesofindia.indiatimes.com/photo/67586673.cms" className="w-100 searchCatLogo" />
</Col>
);
};
return (
<Container>
<Row className="searchContainer py-5">
<h1>{searchGuests.searchGuestsTitle.title}</h1>
<Col sm={12}>
<form onSubmit={formik.handleSubmit} className="searchForm">
<label htmlFor="location"></label>
<select id="location" name="location" value={formik.values.location} {...formik.getFieldProps('location')}>
<option value="Select Location">Select Location</option>
{searchGuests.locationCities.map(city => <option>{city}</option>)}
</select>
<label htmlFor="age"></label>
<select id="age" name="age" value={formik.values.age} {...formik.getFieldProps('age')}>
<option value="Select Age">Select Age</option>
{searchGuests.SearchAge.map(age => <option>{age}</option>)}
</select>
<label htmlFor="breed"></label>
<select id="breed" name="breed" value={formik.values.breed} {...formik.getFieldProps('breed')}>
<option value="Select Breed">Select Breed</option>
{searchGuests.searchBreed.map(breed => <option>{breed}</option>)}
</select>
<label htmlFor="gender"></label>
<select name="gender" value={formik.values.gender} {...formik.getFieldProps('gender')}>
<option value="Select Gender">Select Gender</option>
<option value="Female">Female</option>
<option value="Male">Male</option>
</select>
<button type="submit">
<FaSearch size={27} className="searchIcon" />
</button>
</form>
</Col>
<Col sm={12}>
<Row className="mt-5 items">
{searchResults.length > 0 ? searchResults.map(result => {
return (
<SearchResults
key={result.id}
img={result.img}
location={result.location}
age={result.age}
breed={result.breed}
gender={result.gender}
name={result.name}
/>
)
}) : <LoadingPage />}
</Row>
</Col>
</Row>
</Container>
);
};
export default Search;
SearchResults.js
import React, { useState } from "react";
import { Col, Button, Card } from "react-bootstrap";
import "./Search.css";
import { BsGeoAlt } from "react-icons/bs";
import { FaPaw } from "react-icons/fa";
import AdoptionFormModal from "./AdoptionFormModal"
const SearchResults = ({ img, id, location, age, breed, gender, name }) => {
const [modalShow, setModalShow] = useState(false);
return (
<>
<Col lg={4} className="mb-5">
<Card style={{ width: '18rem' }}>
<Card.Img variant="top" src={img} />
<Card.Body id="modalBody">
<Card.Text id="cardText">
<div className="firstDiv">
<p>{breed}</p>
<p>{name}</p>
<Button variant="primary" className="adoptButton shadow-none" onClick={() => setModalShow(true)}>
<FaPaw className="pawIcon"/>
Adopt
</Button>
</div>
<div className="secondDiv">
<p>{gender}, {age}</p>
<p className="mt-4"><BsGeoAlt size={25}/> {location}</p>
</div>
</Card.Text>
</Card.Body>
</Card>
</Col>
<AdoptionFormModal
show={modalShow}
onHide={() => setModalShow(false)}
/>
</>
);
};
export default SearchResults;
Seems you need a sort of "switch" statement for the 3 age categories.
const checkAge = age => {
switch(values.age.toLowerCase()) {
case "<3":
return Number(age) < 3;
case ">3"
return Number(age) >= 3;
case "all":
default:
return true;
}
};
...
return (
item.location
.toLowerCase()
.includes(
values.location.toLowerCase()
) &&
checkAge(item.age) &&
item.breed
.toLowerCase()
.includes(
values.breed.toLowerCase()
) &&
item.gender
.toLowerCase()
.includes(
values.gender.toLowerCase()
)
)
I have a child component called ProductCard that takes props from the parent called CreateNewSales. The onChange function takes two argument and is defined in the parent. If I call that function in the ProductCard, I keep getting this error for the typeahead though every other input except the quantity input seems to be working just fine. Here are the code:
This is the ProductCard:
import React, { useState, Fragment } from 'react';
import { FormGroup, Label, Input, Col} from 'reactstrap';
import { Typeahead } from 'react-bootstrap-typeahead';
import PropTypes from 'prop-types';
import {PlusSquare, MinusSquare} from 'react-feather'
const plusSquare = <PlusSquare/>
const minusSquare = <MinusSquare/>
const ProductCard = (props) => {
console.log(props)
const ProductOptions = [{id:1,name: "Clothes"}, {id:2,name:"Services"}, {id:3,name:"Shows"}, {id:4,name: "Peace"}] //dummy date //res_data.payload.data.product
// const [singleSelections, setSingleSelections] = useState([]);
// from quantityIncrement.js
const [count, setCount] = useState(1)
const decrement = () => {
if(count === 1){
setCount(count)
} else{
setCount(count-1)
}
};
const increment = () => {
setCount(count+1)
};
return(
<Fragment>
<FormGroup>
<Label>Product Name</Label>
<Typeahead
id="basic-typeahead"
labelKey="name"
onChange={(e) => props.handleChange(e, props.index)}
// onInputChange={props.setProductName}
options={ProductOptions}
name={ProductOptions}
selected={props.value.productName}
style={{backgroundColor:"#d5deee"}}
value={props.value.productName}
/>
</FormGroup>
<FormGroup>
<div className="form-row">
<Col>
<Label for="quantity">Quantity</Label>
<div style={{display: "flex"}}>
<Input value={count} id="quantity" name="quantity" onChange={e=> setCount(e.target.value)} style={{backgroundColor: "#d5deee"}} />
<div style={{display:"flex", marginTop:"5px"}}><span><i style={{ width: 15, fontSize: 10, padding: 11, color: '#848b97' }} onClick={increment}>{plusSquare}</i></span><span><i style={{ width: 15, fontSize: 12, color: '#848b97' }} onClick={decrement}>{minusSquare}</i></span></div>
</div>
</Col>
<Col>
<Label for="discount">Discount</Label>
<Input type="text" id="discount" onChange = {(e) => props.handleChange(e, props.index)} value={props.value.discount} name="discount" style={{backgroundColor:"#d5deee"}} />
</Col>
</div>
</FormGroup>
<FormGroup>
<div className="form-row">
<Col>
<Label for="price">Price</Label>
<Input type="text" id="price" onChange={(e) => props.handleChange(e, props.index)} value={props.value.price} name="price" style={{backgroundColor:"#d5deee"}} />
</Col>
<Col>
<Label for="amountPaid">Amount Paid</Label>
<Input type="text" id="amountPaid" onChange={(e) => props.handleChange(e, props.index)} value={props.value.amountPaid} name="amountPaid" style={{backgroundColor:"#d5deee"}} />
</Col>
</div>
</FormGroup>
</Fragment>
)
}
ProductCard.propTypes = {
handleChange: PropTypes.func.isRequired,
value: PropTypes.object.isRequired,
// onClickQuantity: PropTypes.func.isRequired
}
export default ProductCard
This is the CreateNewSale:
import React, { Fragment, useState } from 'react';
import {Form, FormGroup, Label, Input, Card, Col, Row, CardBody, Button, ButtonGroup, Table} from 'reactstrap';
import {toast} from 'react-toastify';
import { Typeahead } from 'react-bootstrap-typeahead';
import { withRouter} from 'react-router';
// import {useHistory} from 'react-router-dom';
import {connect} from 'react-redux';
import {validateCreateNewSaleForm, responseErrorParser} from "../../components/authentication/validator"
// productCard.js
import ProductCard from '../../components/sales/ProductCard'
import {Trash2} from 'react-feather'
const trash2 = <Trash2/>
const CreateNewSale = (props) => {
const [, setIsCreatingNewSale] = useState(false)
// const [singleSelections, setSingleSelections] = useState([]);
// from the productCard.js
const [newProductValues, setNewProductValues] = useState([{
productName: [],
discount: "",
price: "",
amountPaid: "",
quantity: "1",
}]);
// const [newProductName, setNewProductName] = useState([{
// newProductNames: [],
// newCustomerName: []
// }])
// const handleInputChange = (event) => {
// setNewProductName({
// ...newProductName,
// [event.target.name]: event.target.value
// });
// }
const [customerName, setCustomerName] = useState([])
const [date, setDate] = useState('')
const [dueDate, setDueDate] = useState('')
const [vat, setVat] = useState('')
// const [visible, setVisible] = useState(false)
const handleChange = (event, index) => {
console.log( event )
const values = [...newProductValues];
values[index][event.target.name] = event.target.value
console.log('=======================>', values)
setNewProductValues(values);
// setNewProductValues({
// ...newProductValues,
// [event.target.name]: event.target.value
// });
}
const handleAddFields = (e) => {
setNewProductValues([...newProductValues, {discount:"", price: "",
amountPaid: "",
quantity: "1",
productName:[]
}])
}
const handleRemoveFields = (index) => {
const values = [...newProductValues];
values.splice(index, 1);
setNewProductValues(values);
}
const customerOptions = [{id: 1, name: "Olalekan"}, {id: 2, name:"Michael"}, {id: 3,name:"Emeka"}, {id:4,name: "Glory"}] //dummy data //res_data.payload.data.customer
const fields = {
customer_name: { default: '', message: 'Please enter an already created customer name' },
product_name: { default: '', message: 'Please enter an already created customer name' },
quantity: { default: '', message: 'Please select a quantity' },
discount: { default: '', message: 'Please enter the discount given' },
price: { default: '', message: 'Please select the price given' },
amount_paid: { default: '', message: 'Please enter the amount paid by the customer' },
date: { default: '', message: 'Please enter date' },
due_date: { default: '', message: 'Please enter due date given' },
vat: { default: '', message: 'Please enter the vat' },
}
const handleCreateNewSale = async (e) => {
e.preventDefault()
setIsCreatingNewSale(true);
const responsePayload = {
customer_name: newProductValues.newCustomerName, //customerName
product_name: newProductValues.newproductNames,
quantity: newProductValues.quantity,
discount: newProductValues.discount,
price: newProductValues.price,
amount_paid: newProductValues.amountPaid,
date: date,
due_date: dueDate,
vat: vat
}
const errors = validateCreateNewSaleForm(responsePayload, fields)
if (errors.isErrors) {
setIsCreatingNewSale(false)
setTimeout(() => {
errors.errors.forEach(e => toast.error(e.message))
}, 400);
} else {
const response = await props.CreateNewSale(responsePayload)
if (response.status) {
const newSale = response.payload.data.id
localStorage.setItem('__grm__act__biz__', newSale.toString())
// props.history.push(`/business/${newSale}.toString/sales/invoice`, {previousLocation: props.location.pathname})
} else {
setIsCreatingNewSale(false)
const payload = response.payload
const errs = responseErrorParser(payload.data)
setTimeout(() => {
errs.forEach(e => toast.error(e.message))
}, 400);
}
}
}
return(
<Fragment>
<div style={{display:"flex", fontFamily:"'Poppins', sans-serif"}}>
<div className="col-lg-10" style={{margin: "0 auto", maxWidth:"500px", width:"100%"}}>
<Form>
<Card>
<CardBody>
<FormGroup>
<Label>Customer Name</Label>
<Typeahead
id="basic-typeahead"
labelKey="name"
onChange={setCustomerName}
options={customerOptions}
selected={customerName}
value={customerName}
name="customerName"
style={{backgroundColor:"#d5deee"}}
/>
</FormGroup>
</CardBody>
</Card>
{ newProductValues.map((newProductValue, index) => (
<div key={index}>
<Card >
<CardBody>
<Col style={{textAlign: "right"}}>
<i onClick={() => handleRemoveFields()} >{trash2}</i>
</Col>
<ProductCard index={index} handleChange={handleChange} value={newProductValue} />
</CardBody>
</Card>
</div>
))}
<Row>
<Col>
<p onClick={() => handleAddFields()} style={{marginLeft:"20px"}}> <span style={{fontSize:"18px"}}>+</span> Add another product</p>
</Col>
</Row>
<Row>
<Col>
<p onClick={() => handleAddFields()} style={{marginLeft:"20px"}}> <span style={{fontSize:"18px"}}>+</span> Add another product</p>
</Col>
</Row>
<Card>
<CardBody>
<FormGroup>
<div className="form-row">
<Col>
<Label for="date">Date</Label>
<Input className="form-control digits" type="date" defaultValue="2018-01-01" value={date} onChange={e => setDate(e.target.value)} id="date" name="date" style={{backgroundColor:"#d5deee"}} />
</Col>
<Col>
<Label for="dueDate">Due Date</Label>
<Input className="form-control digits" type="date" defaultValue="2018-01-01" value={dueDate} onChange={e => setDueDate(e.target.value)} id="dueDate" name="dueDate" style={{backgroundColor:"#d5deee"}} />
</Col>
</div>
</FormGroup>
<FormGroup>
<div className="form-row">
<Col>
<Label for="vat">VAT %</Label>
<Input type="text" id="vat" value={vat} onChange={e => setVat(e.target.value)} style={{backgroundColor:"#d5deee"}} />
</Col>
</div>
</FormGroup>
<div style={{margin:"0 auto", textAlign:"center"}}>
<p style={{fontSize:"12px"}}>Only click cleared if this sales have been paid in full</p>
<Row>
<Col>
<ButtonGroup>
<Button outline color="primary" type="button">Cleared</Button>
<Button outline color="primary" type="button">Not Cleared</Button>
</ButtonGroup>
</Col>
</Row>
</div>
<Row className="m-t-50">
<Col lg={`6`}>
<Button outline color="primary" size="lg" style={{maxWidth:"200px", width:"100%"}}>SAVE</Button>
</Col>
<Col lg={`6`}>
<Button color="primary" size="lg" onClick={e => handleCreateNewSale(e)} style={{maxWidth:"200px", width:"100%"}}>CREATE</Button>
</Col>
</Row>
</CardBody>
</Card>
</Form>
</div>
<div className="col-lg-2" style={{backgroundColor:"#eaf6fd", position:"fixed", right:0, height:"100%",}}>
<Card className="m-t-50">
<CardBody>
<div>You have added <span>0</span> products</div>
</CardBody>
</Card>
<div className="table-responsive">
<Table borderless>
<tbody>
<tr>
<td className="bd-t-none">Sub Total</td>
<td>000 000 000</td>
</tr>
<tr style={{fontWeight:"bold"}}>
<td className="bd-t-none">Total</td>
<td>000 000 000</td>
</tr>
</tbody>
</Table>
</div>
</div>
</div>
</Fragment>
)
}
const mapStateToProps = (state) => ({
requestingCreateNewSale: state.isRequestingCreateNewSale,
});
const actions = {
CreateNewSale: CreateNewSale
};
export default connect(mapStateToProps, actions)(withRouter(CreateNewSale))
And this is the error I keep getting everytime I want to change the value for the productName typeahead:
TypeError: Cannot read property 'name' of undefined
handleChange
src/pages/sales/CreateNewSales.js:54
51 | const handleChange = (event, index) => {
52 | console.log( event )
53 | const values = [...newProductValues];
> 54 | values[index][event.target.name] = event.target.value
| ^ 55 | console.log('=======================>', values)
56 | setNewProductValues(values);
57 | // setNewProductValues({
View compiled
onChange
src/components/sales/ProductCard.jsx:40
37 | <Typeahead
38 | id="basic-typeahead"
39 | labelKey="name"
> 40 | onChange={(e) => props.handleChange(e, props.index)}
| ^ 41 | // onInputChange={props.setProductName}
42 | options={ProductOptions}
43 | name={ProductOptions}
When I sort through an array of objects and then output them to the options select, I get errors:
Encountered two children with the same key, Objects are not valid as a React child.
Highlighted ---- important fragments below.
import React, { useState } from "react";
import { DatePicker } from "antd";
import BarChartsMonth from "./../../../components/BarCharts/Barchartmonth";
import BarChartsWeek from "./../../../components/BarCharts/Barchartweek";
import "antd/dist/antd.css";
import { Select } from "antd";
const TeamActivity = () => {
const { MonthPicker, WeekPicker } = DatePicker;
const handleChangeView = val => {
setView(val);
};
+++++++++++++++++++++++++++++++++++++++++++++++++++++
const users = [
{ name: "Peter", surname: "Parker" },
{ name: "Edvard", surname: "Rykinozhnici" },
{ name: "Top", surname: "Ragnakek" },
{ name: "Pavel", surname: "Doorov" }
];
++++++++++++++++++++++++++++++++++++++++++++++++++++++
const UsCollection = users.map((user,i)=> {
return (
<Option key={i} value > {user}</Option>
)
})
++++++++++++++++++++++++++++++++++++++++++++++++++++++
const [view, setView] = useState("Month");
return (
<div className="activity-page">
{" "}
<div className="main-title">Activity of the team</div>
<div className="wrapper-wrapper">
<div className="sort-by">Sort by:</div>
<div className="datepickers-wrapper">
<MonthPicker onChange={(date, type) => handleChange(date, "month")} />
<WeekPicker
placeholder="Select week"
onChange={(date, type) => handleChange(date, "week")}
/>
</div>
<div>
<Select
showSearch
style={{ width: 200 }}
placeholder="Select a person"
optionFilterProp="children"
filterOption={(input, option) =>
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
>
+++++++++++++++++++++++++++++++++++++++++
{UsCollection}
+++++++++++++++++++++++++++++++
</Select>
</div>
</div>
<div className="container ">
<h1 className="week-activity"> {view} activity</h1>
<div className="changes-week-month">
<form>
<input
type="radio"
name="state"
id="week-id"
onClick={() => {
handleChangeView("Week");
}}
/>
<label htmlFor="week-id"> Week </label>
<input
type="radio"
name="state"
id="month-id"
defaultChecked={true}
onClick={() => {
handleChangeView("Month");
}}
/>
<label htmlFor="month-id"> Month </label>
</form>
</div>
{view === "Month" ? <BarChartsMonth /> : <BarChartsWeek />}
</div>
</div>
);
};
export default TeamActivity;
Выполнить кодВернуться к сообщению