Problem rendering items in select (React js) - javascript

I am trying to load the options of a select depending on another select. I extract the data from an array (which will later have much more data).
My problem is that the second select loads the correct items BUT in a single options and not separately.
Any idea what I'm failing? I am a newbie in react. I apologize if the code is very horrible! Any help will be welcome.
P.S. I don't know if it makes any difference in this case, but I'm using "Ant Design".
import React, { useState } from 'react';
import { Row, Col, Form, Select, Input } from 'antd';
export default function SelectPetZone(props) {
const { publicationData, setPublicationData } = props;
const { Option } = Select;
const arrayZones = [
{
departmentName: "Montevideo",
neighborhood: ['Centro', 'Cordón', 'Flor de Maroñas', 'Manga']
},
{
departmentName: "Canelones",
neighborhood: ['Las Piedras', 'El Dorado', 'Progreso', 'La Paz']
}
];
const optionsDepartments = [];
const [optionsNeighborhood, setoptionsNeighborhood] = useState([]);
for(const item of arrayZones) {
optionsDepartments.push(<Option key={item.departmentName} value={item.departmentName}> { item.departmentName } </Option>);
}
const chargeNeighborhood = e => {
// Set department
setPublicationData({ ...publicationData, department: e });
let arrayDepartment = arrayZones.filter(function(i) {
return i.departmentName === e;
});
let arrayNeighborhood = arrayDepartment.map(function(i) {
console.log(i.neighborhood); // => Print array with correct values
return <Option key={i.neighborhood} value={i.neighborhood}>{i.neighborhood}</Option>;
})
setoptionsNeighborhood(arrayNeighborhood);
}
return (
<>
<Row>
<Col lg={6}>
<Form.Item>
<Select placeholder='Departamento' onChange={chargeNeighborhood} value={publicationData.department} >
{optionsDepartments}
</Select>
</Form.Item>
</Col>
<Col lg={6} offset={3}>
<Form.Item>
<Select placeholder='Barrio' onChange={e => setPublicationData({ ...publicationData, neighborhood: e })} value={publicationData.neighborhood} >
{optionsNeighborhood}
</Select>
</Form.Item>
</Col>
</Row>
</>
)
}
In chargeNeighbourhood function I also tried to do this but did not get a good result:
const chargeNeighborhood = e => {
// Set department
setPublicationData({ ...publicationData, department: e });
// Load neighborhood
for(const i of arrayZones) {
if(e === i.departmentName) {
for(const j of i.neighborhood) {
// console.log(j);
setoptionsNeighborhood([...optionsNeighborhood, <Option value={j} key={j}>{j}</Option>]);
}
}
}
}

Your problem is that you're mapping arrayDepartment inside changeNeighborhood and, if I understand your problem correctly, after you get the right department, you should be mapping arrayDepartment.neighborhood to map every neighborhood string into an Option.
For clarity, it would be a good idea to change that property name from neighborhood to neighborhoods since it's a list of neighborhoods.
In your comment, it seems like you already concluded that neighborhood is an array when you have this line:
console.log(i.neighborhood); // => Print array with correct values
So when you set your value for the Option component, you're actually passing an entire array:
<Option key={i.neighborhood} value={i.neighborhood}>{i.neighborhood}</Option>
That's why you're seeing all options as an array. Option is just converting that list into a string by concatenating every element of the array.

Related

React how to render only first index in array and then filter to display different item in array

I currently have a view button set up to retrieve all the records for a patient that renders all the records in a card I am sending to a component to display them in cards. It also shows all the records for that patient in a select dropdown. I want it to only render the most recent record when I initially click the view button and then be able to re-render the card when a record is selected in the dropdown.
const onViewClick = (diag) => {
diagnosticServices
.getDiagByHorseId(pageData.pageIndex, pageData.pageSize, diag.id)
.then(getDiagByIdSuccess)
.catch(getDiagByIdError);
setToggleDiagSearch(true);
setRenderDiag(!renderDiag);
};
const getDiagByIdSuccess = (response) => {
_logger(response);
let diag = response.item.pagedItems;
setDiagnostics((prevState) => {
const pd = { ...prevState };
pd.arrayOfDiagnostics = diag;
pd.diagComponents = pd.arrayOfDiagnostics.map(diagMapper);
_logger(pd.diagComponents);
return pd;
});
setDateComponents((prevState) => {
const dc = { ...prevState };
dc.arrayOfDates = diag;
dc.dateCreatedComponents = dc.arrayOfDates.map(dateMapper);
_logger(dc.dateCreatedComponents);
return dc;
});
};
const diagMapper = (diag) => {
_logger("Mapping single Diagnostic -->", diag);
return (
<SingleDiagnostic
diagnostic={diag}
key={diag.id}
onEditClick={onEditClick}
></SingleDiagnostic>
);
};
const dateMapper = (diag) => {
_logger("Mapping Diagnostic Dates -->", diag);
return (
<option key={diag.id} value={diag.id}>
{formatDateTime(diag.dateCreated)}
</option>
);
};
I tried to update the selected state value and then filter the state with the selected value, and then to update the render state with the result of the filter, but I am receiving an undefined in my filter. The e.target.value and diag.id are the same id, but the filteredDiag is returning undefined.
const filterDiagnostics = (e) => {
_logger(e.target.value);
const filterDiag = (diag) => {
_logger(diag.id);
let result = false;
if (e.target.value === diag.id) {
result = true;
} else {
result = false;
}
return result;
};
let filteredDiag = diagnostics.arrayOfDiagnostics.filter(filterDiag);
_logger(filteredDiag);
setDiagnostics((prevState) => {
let diagnostic = { ...prevState };
diagnostic = filteredDiag.map(diagMapper);
return diagnostic;
});
setRenderDiag(!renderDiag);
};
In my return statement:
<Col xs={8}>
{toggleDiagSearch && (
<Card className="mt-4">
<Form onSubmit={handleSubmit}>
<Card.Header>
<strong> Diagnostics History</strong>
<Field
as="select"
className="form-select"
name="diagComponents"
onChange={filterDiagnostics}
value={values.id}
>
<option label="Select" value="select"></option>
{dateComponents.dateCreatedComponents}
</Field>
</Card.Header>
</Form>
</Card>
)}
<Card className="mt-3">
{renderDiag && diagnostics.diagComponents}
</Card>
</Col>
I tried to set the diagComponents[0] to initially display only the first index, but I don't think that will let me display the other records when I select a new record from the dropdown. Where do I have to make adjustments to show only the first record and get my filter to function correctly?

ReactJS: make a select that load all values from api

I'm creating a select (at the moment i'm using React-Select component) to retrive all the result from the api.
The problem is that API gives me back 20 values, so I should find a method to load other 20 values ( as I make another api call )
const option = personList && personList .map((spl) => {
return {
value: spl.perCod,
label: spl.perName
}
})
<Row>
<Col>
<Select
id="perCod"
name="perCod"
options={option}
/>
</Col>
</Row>
the personList is populated calling the api:
useEffect(() => {
sortEntities();
}, [paginationState.activePage, paginationState.order, paginationState.sort]);
const sortEntities = = () => {
//...
props.getFilteredEntities(
search, // i pass there the parameters for the research
paginationState.activePage - 1,
paginationState.itemsPerPage,
`${paginationState.sort},${paginationState.order}`
),
}
props.getFilteredEntities in my reducer is:
export const getFilteredEntities: ICrudSearchAction<Person> = (search, page, size, sort) => {
const params = new URLSearchParams(search) ? new URLSearchParams(search).toString() : null;
const requestUrl = `${apiUrl}${sort ? `?page=${page}&size=${size}&sort=${sort}` : ''}${sort ? '&' : '?'}${params}`;
return {
type: ACTION_TYPES.FETCH_PERSON_LIST,
payload: axios.get<Person>(`${requestUrl}${sort ? '&' : '?'}cacheBuster=${new Date().getTime()}`),
};
};
At the moment my select has the first 20 results from api. I should need to load others. How can I do? thank you.
change your <Select> code with this,
you have to add option tag within iteration, to render all options within select tag,
<Select id="perCod" name="perCod">
{option.map(o=><option key={Math.random()} value={o.perCod} >{o.perName}</option>)}
</Select>

ReactJS: Form values doesn't take values

I have a form and when it is submitted I take the values with the function saveEntity.
Now in this form, I have some fields that are showed after some other values are chosen. From these fields, I don't receive any values on my saveEntity function.
export const MyFunctionName = (props) => {
// code...
const saveEntity = (event, errors, values) => {
console.log('values ', values);
// other code
}
const RuoliChoosen = ruolo => {
if (!ruolo.ruolo) {
return <div />;
}
return (
<div>
<Row>
<Col md="6">
<AvGroup>
<Label for="accessNumber">{'Access Number'}</Label>
<AvInput
id="accessNumber"
data-cy="accessNumber"
type="text"
className="form-control"
name="accessNumber"
/>
</AvGroup>
</Col>
//.....
}
return(
<AvForm model={isNew ? {} : userClientAuthorityEntity} onSubmit={saveEntity}>
<AvInput
id="user-client-authority-application"
data-cy="application"
type="select"
className="form-control"
name="applicationId"
onChange={handleChange}
required
>
<option value="" key="0">
Select Application
</option>
{applicationList
? applicationList.map(value => {
return (
<option value={value.appCod} key={value.appCod}>
{value.appDesapplicazione}
</option>
);
})
: null}
</AvInput>
// this RuoliChoosen receive "ruoli" from another function (it works)
<RuoliChoosen ruolo={ruoli} />
)}
When I submit the form, I expect to see the values ​​in the saveEntity, in this case only values for application and accessNumber, but I receive only value for application.
How can I fix it? Thank you.
Please format your code well when coding, since normally the format keeps up with your logic. Sometimes people are picky about the format, but really what they are saying is that they are not comfortable to read your code. Trust me, you don't want to get uncomfortable reading code ;)
const RuoliChoosen = ruolo => {
This isn't a component, instead
const RuoliChoosen = ({ ruolo }) => {
Because ruolo is a prop, not the entire props
You sent ruolo as a prop where the component is called. But You sent it as an object. And then in that component where you receive this ruolo as prop it comes as an object. If you want to access it you have to destructure it. So change it.
from
const RuoliChoosen = ruolo => {
return()
}
to
const RuoliChoosen = ({ruolo}) => {
return()
}
thanks.

ES6: Loop through objects and assign one of it value to Select fields value and text

I'm trying to loop through the below data structure and set 'Select' fields options. This is where I have an issue. I'm trying to assign 'nameCombined' & 'codeCombined' to the value and text of the Select Form field.
DataStructure:
{
"Bucks":{
"countyCode":"42017",
"globalStateCode":"PA",
"stateCode":"PA",
"nameCombined":"42017 (PA)",
"codeCombined":"42017 PA Bucks"
},
"Montgomery":{
"countyCode":"42091",
"globalStateCode":"PA",
"stateCode":"PA",
"nameCombined":"42091 (PA)",
"codeCombined":"42091 PA Montgomery"
}
}
React JSX
Select Component
const {
name,
options,
actions: { handleFieldChange },
} = props;
<select id={name} onChange={(e) => handleValueChange(e)}>
{options.map((option, idx) => (
<option key={`${option.value}_${idx}`} value={option.value}>
{value === option.text}
</option>
))}
</select>;
// The component that consumes Select Component
// ============================================
<Select
inputData={{
name:name,
options:Object.entries(counties).map(([key, item]) => ({'value': item.nameCombined, 'text': item.codeCombined})), // <- this line needs attention
type:INPUT_TYPES.DROPDOWN,
}},
actions={{
handleFieldChange: handleDropdownChange
}}
/>
Try Object.keys instead of Object.entries.
Object.keys(counties).map(key => ({value: counties[key].nameCombined, item: counties[key].codeCombined}))
The conceptual limitation that you have in your assumption is that you need a proper array to iterate and Object.entries gives you [[key1, object1],...] while Object.keys gives you [key1, key2].

When selecting an option in a dropdown, how do I change the state with onChange but with a value other than the one inside the option tags

Need some help with dropdowns. I can't seem to figure out a way to pass in the ID specific to that particular dropdown <option> so that when the user makes a selection that value can be used (for example to setState). The usual case is to read the value of the dropdown itself, but in my case I don't want the displayed value but another value that is 'behind the scenes' or hidden.
Please note I've already researched about controlled components in react for forms etc, this issue is a bit different and my attempts to find an answer have come up empty.
I'm using react and javascript but I think if you know a little about javascript you may be able to help.
The dropdowns are set up like shown below where on changing the dropdown selection it runs the function handleStatusChange.
<select
id="inputIdentity"
className="select-form"
onChange={this.handleStatusChange}
>
<option value="" hidden>
please choose
</option>
<option>status1</option>
<option>status2</option>
<option>status3</option>
</select>
In actual practice the options are mapped out from data fetched via an api as they can be altered in the backend:
<select
id="inputIdentity"
className="form-control content-input"
onChange={this.handleStatusChange}
>
<option value="" hidden>
Please Choose
</option>
{statusData ? (
statusData.map((status) => (
<option>{status.title}</option>
))
) : (
<option>Loading...</option>
)}
</select>
this.handleStatusChange checks the value of the event (which is the act of changing the selection in the dropdown, the value is accessed with: e.target.value) to read the value inside the <option> that was chosen... The method for reading the chosen dropdown value is something like:
handleStatusChange = (e) => {
this.setState({
// set it to state
status: e.target.value
});
};
I
This is the standard way to do it.
Now we get back to the question - is there a way to read a value from each dropdown that is NOT shown (read from e.target.value) instead, ie. if they each had an ID or something, how do I pass that in so that e.target.value would be able to access this id.
If you take a look at the version where I map out (or loop out if you aren't familiar with the map function) the <option> tags and in each iteration I pass in the title with {status.title}. I can access the id with status.id but the onChange handler is in the <select> tag and can't be within the map / loop, so to handle it by passing in the id into the onChange handler is not possible either.
What would be the correct way for the onChange handler to have access to that specific value which is not displayed between <option> tags?
You can keep the options in your component state and use the array method find to find the option that corresponds to the selected option and use that.
Example
class App extends React.Component {
state = {
options: [
{ value: "status1", label: "Status 1", secretValue: "foo" },
{ value: "status2", label: "Status 2", secretValue: "bar" },
{ value: "status3", label: "Status 3", secretValue: "baz" }
],
selectedOption: ""
};
handleStatusChange = e => {
const { value } = e.target;
this.setState(prevState => {
const { secretValue } = prevState.options.find(
option => option.value === value
);
console.log(secretValue);
return { selectedOption: value };
});
};
render() {
const { options, selectedOption } = this.state;
return (
<select value={selectedOption} onChange={this.handleStatusChange}>
<option value="" hidden>
please choose
</option>
{options.map(option => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
you can pass that value to the loop showing the Options.
{statusData ? (
statusData.map((status) => (
<option value={status.value}>{status.title}</option>
))
) : (
<option>Loading...</option>
)}
and in order to do that you have to modify your array statusData.
hiddenData = ['a', 'b', 'c'];
statusData.map((s, index) => {
s.value = hiddenData[index]
return s;
});
is there a way to read a value from each dropdown that is NOT shown (read from e.target.value) instead, ie. if they each had an ID or something, how do I pass that in so that e.target.value would be able to access this id.
e.target is a reference to the HTMLSelectElement where the change occurred. You can find the option with the matching value in its options list, and then use that HTMLOptionElement's properties, like this:
handleStatusChange({target}) {
const value = target.value;
const optionElement = Array.from(target.options).find(opt => opt.value === value);
// If found, use information from `optionElement` to find the
// entry in `statusData` in a state change, e.g.:
if (optionElement) {
const id = optionElement && optionElement.id;
if (id) {
this.setState(state => {
const entry = state.statusData.find(e => e.id === id);
if (entry) {
// Use `entry`'s information
}
}
}
}
}
React example, using a details property on the entries in statusData:
class Example extends React.Component {
constructor(...args) {
super(...args);
this.handleStatusChange = this.handleStatusChange.bind(this);
this.state = {
detail: "",
statusData: [
{id: "one", title: "One", detail: "Details for one"},
{id: "two", title: "Two", detail: "Details for two"},
{id: "three", title: "Three", detail: "Details for three"}
]
};
}
handleStatusChange({target}) {
const value = target.value;
const optionElement = Array.from(target.options).find(opt => opt.value === value);
const id = optionElement && optionElement.id;
if (id) {
this.setState(state => {
const entry = state.statusData.find(e => e.id === id);
if (entry) {
return {
detail: entry.detail
}
}
});
}
}
render() {
const {statusData, detail} = this.state;
return (
<div>
<select
id="inputIdentity"
className="form-control content-input"
onChange={this.handleStatusChange}
>
<option value="" hidden>
Please Choose
</option>
{statusData ? (
statusData.map((status) => (
<option id={status.id}>{status.title}</option>
))
) : (
<option>Loading...</option>
)}
</select>
<div>Detail: {detail}</div>
</div>
);
}
}
ReactDOM.render(
<Example />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Categories