I am building a booking Hotel application and I am trying to achieve search by multiple inputs with react.js. I tried with the code below but I have two errors :
first error: that whenever I change the value of type room or the number of guests I get the result with the previous state, I always get the result with the previous state.the search is working fine but it using the previous state.
second error: How to achieve intersection of the two results of objects.
Or if anyone can come with a better solution.
import React, { useState } from 'react';
import './SearchPage.css';
import RoomList from './RoomList';
import { v4 as uuidv4 } from 'uuid';
const SearchPage = () => {
const Rooms = [
{
id: uuidv4(),
description: 'Chnambre du luxe 1',
size: 250,
guests: '4',
roomType: 'family room',
pets: false,
picture: 'pictures/pic.jfif',
price: 1000,
},
{
id: uuidv4(),
description: 'chambre du lux2',
picture: 'pictures/pic2.jfif',
price: 2000,
size: 300,
guests: '4',
roomType: 'single room',
pets: true,
},
{
id: uuidv4(),
description: 'chambre du luxe 3',
picture: 'pictures/pic2.jfif',
price: 2500,
size: 350,
guests: '2',
roomType: 'family room',
pets: true,
},
];
const [foundRooms, setFoundRooms] = useState(Rooms);
const [roomType, setRoomType] = useState('All');
const [guestNumber, setGuestNumber] = useState('0');
const HandleGuestNumber = (state) => {
const results = Rooms.filter((room) => room.guests === state);
return results;
};
const HandleRoomType = (state) => {
let results = [];
results = Rooms.filter((room) => {
let rooms = room.roomType.match(state);
return rooms;
});
return results;
};
const HandleOnChange = (event) => {
switch (event.target.name) {
case 'roomType':
setRoomType(event.target.value);
break;
case 'guestNumber':
setGuestNumber(event.target.value);
break;
default:
console.log(`Sorry, we are out of.`);
}
let foundRooms = HandleRoomType(roomType) && HandleGuestNumber(guestNumber);
setFoundRooms(foundRooms);
};
return (
<div>
<div className="SearchPage">
<h1> Search</h1>
</div>
<div className="SearchContainers">
<div className="SearchItem">
<span>Room Type : </span>
<select onChange={HandleOnChange} name="roomType">
<option value="All">All</option>
<option value="family room">Family Room</option>
<option value="single room">Single Room</option>
<option value="Luxiourious Family Room">
Luxiourious Family Room
</option>
<option value="Luxiourious Single Room">
Luxiourious Single Room
</option>
</select>
</div>
<div className="SearchItem">
<span> Guests : </span>
<select onChange={HandleOnChange} name="guestNumber">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
</div>
</div>
);
};
export default SearchPage;
In your HandleOnChange function, you are doing this:
let foundRooms = HandleRoomType(roomType) && HandleGuestNumber(guestNumber);
setFoundRooms(foundRooms);
That is almost certainly not doing what you think it is doing.
Since HandleRoomType always returns an array, foundRooms always evaluates to HandleGuestNumber(guestNumber).
It sounds like you want foundRooms to be the rooms that are included in the arrays returned by both HandleRoomType and HandleGuestNumber.
One way you could do that is by doing another filter.
const roomsMatchingType = HandleRoomType(roomType)
const roomsMatchingGuestNumber = HandleGuestNumber(guestNumber)
const intersection = Rooms.filter(room => roomsMatchingType.some(r => r.id === room.id) && roomsMatchingGuestNumber.some(r => r.id === room.id))
Related
I have a svelte component where i want to connect a selected input with a declared attribute.
My problem is that the binding of the selected value of status to the attribute'status' declared in 'flightschedules' doesnt work.
The options are from the attribute questions: on-time, delayed, cancelled
Can somebody help me please ?
Here is my code (its a component to create form, e.g create a flightschedule):
<script>
import axios from "axios";
import { onMount } from "svelte";
export let params = {};
let flightschedule = {
timeofdeparture: "",
flightnumber: "",
gatenumber: "",
status: "",
privatejetline_id: null,
};
let questions = [
{ text: "on-time" },
{ text: "delayed" },
{ text: "cancelled" },
];
let selected;
let privatejetline_ids = [];
onMount(() => {
getPrivateJetLineIds();
selected = params.status;
});
function getPrivateJetLineIds() {
axios
.get("http://localhost:8080/flights/privatejetline")
.then((response) => {
privatejetline_ids = [];
for (let privatejetline of response.data) {
privatejetline_ids.push(privatejetline.id);
}
flightschedule.privatejetline_id = privatejetline_ids[0];
});
}
function addFlightSchedule() {
axios
.post("http://localhost:8080/flights/flightschedule", flightschedule)
.then((response) => {
alert("Flight Schedule added");
console.log(response.data);
})
.catch((error) => {
console.log(error);
alert(error);
});
}
</script>
<div class="mb-3">
<label for="" class="form-label">Status</label>
<select bind:value={flightschedule.status} class="from-select">
<option value="" disabled>-- Select Status --</option>
{#each questions as question}
<option value={selected} selected={selected===flightschedule.status}>{question.text}</option>
{/each}
</select>
</div>
Actually, no need for selected variable, just bind the flightschedule.status. Try following in REPL.
<script>
let flightschedule = {
timeofdeparture: "",
flightnumber: "",
gatenumber: "",
status: "",
privatejetline_id: null,
};
let questions = [
{ text: "on-time" },
{ text: "delayed" },
{ text: "cancelled" },
];
$: console.log('---->', flightschedule.status)
</script>
<div class="mb-3">
<label for="" class="form-label">Status</label>
<select bind:value={flightschedule.status} class="from-select">
<option value="" disabled>-- Select Status --</option>
{#each questions as question}
<option value={question.text}>{question.text}</option>
{/each}
</select>
</div>
<option value={selected} this line can’t be right. You’re binding all three options to the same value.
You probably want following:
<select bind:value={selected} class="from-select">
<option value="" disabled>-- Select Status --</option>
{#each questions as question}
<option value={question.text}>{question.text}</option>
{/each}
</select>
I'm creating JSX element with the onClick on the button:
<div onClick={() => addWeek()} >
add week
</div>
then I update state (add new items to array) :
//my state that has one object initially (I want to add more)
const [weekly, setweekly] = useState([
{
id: 'n1',
day_week: null,
start_time: null,
end_time: null,
},
]);
const addWeek = () => {
setweekly([
...weekly,
{
id: `n${weekly.length + 1}`,
day_week: null,
start_time: null,
end_time: null,
},
]);
}
after I create JSX element I have an onChange event on that element :
NOTE: This element created with onClick and I have two objects inside my state now.
<select
onChange={(event) => handleWeekly(event)}
id={`n${weekly.length + 1}`}
>
//Some options
</select>
but in here I can't access the updated state I get one object.
const handleWeekly = (event) => {
// I get one object
console.log(weekly);
};
CODE SAND BOX :
https://codesandbox.io/s/strange-nightingale-l6qg3?file=/src/App.js:0-1372
I would approach this problem differently. Instead of putting markup in the state you can map through your data and render your components this way:
export default function App() {
const [weeks, setWeek] = useState([
{
id: "n1",
day_week: null,
start_time: null,
end_time: null
}
]);
const addWeek = () => {
setWeek([
...weeks,
{
id: `n${weeks.length + 1}`,
day_week: null,
start_time: null,
end_time: null
}
]);
};
const handleWeekly = (event) => {
console.log(weeks);
};
return (
<div className="App">
<div onClick={() => addWeek()}>add week</div>
{weeks.map((week) => {
return (
<select
onChange={(event) => handleWeekly(event)}
id={`n${week.id}`}
key={`n${week.id}`}
>
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
<option value="audi">Audi</option>
</select>
);
})}
</div>
);
}
It's easier to keep track of your state this way.
Sandbox Example
I am currently using ReactJs to make a chat system that allows some African students to be able to speak about mental health issues with one another in an anonymous environment. I have a little name input, and I'm currently trying to create a select option so that these topics can show up, and can be filtered on the chat system, but I'm having a hard time getting it to actually render, it keeps crashing and saying TypeError: room.map is not a function.
const [room, setRoom] = useState([
{ label: "Depression", value: "Depression" },
{ label: "Anxiety", value: "Anxiety" },
{ label: "Chat", value: "Chat" }
]);
<select className="joinInput mt-20" onChange={(event) => setRoom(event.currentTarget.value)}>
{room.map(rooms => (
<option
key={rooms.value}
value={rooms.value}
>
{rooms.label}
</option>
))}
</select>
Solution:
const [currentRoom, setCurrentRoom] = useState()
const [room] = useState([
{ label: "Depression", value: "Depression" },
{ label: "Anxiety", value: "Anxiety" },
{ label: "Chat", value: "Chat" },
]);
return (
<select
className="joinInput mt-20"
onChange={(event) => setCurrentRoom(event.currentTarget.value)}
value={currentRoom}
>
<option value="">Select value...</option>
{room.map((rooms) => (
<option key={rooms.value} value={rooms.value}>
{rooms.label}
</option>
))}
</select>
);
I need to display only part of the selected option:
const refertiItems = this.state.referti.map((referti, i) => {
return (
<option key={referti.hash_referto} >
[ {referti.proprietario} - {referti.tipo_esame} - {referti.data_esame.split('T')[0]} ] {referti.hash_referto}
</option>
)
});
and then
Label for="type" text="Seleziona un referto (proprietario - tipo esame - data)" />
<select
name="careGiver"
placeholder="Selezionare Referto"
onKeyPress={this.onEnter}
value={this.codiceReferto}
onChange={this.handleInputChange}
>
<option default value="vuoto" />
{refertiItems}
</select>
When i select an option, i want to be displayed only {referti.proprietario} and {referti.tipo_esame}, but not {referti.hash_referto}.
I can't just take it off from the option.
Any advice?
You most definitely can update the text. You can do this by updating your state array when an item is selected to present the appropriate display text. This is just a rough example, but the concept of how it could be done is shown:
const {
useState,
useEffect,
useRef
} = React;
function App() {
const [opts, setOpts] = useState([{
label: 'one',
active: 'one',
inactive: 'one remove_me'
},
{
label: 'two remove_me',
active: 'two',
inactive: 'two remove_me'
},
{
label: 'three remove_me',
active: 'three',
inactive: 'three remove_me'
},
]);
return (<div>
<select onChange={({target: {value}})=>{
// There are better ways to deep copy, should look into replacing this
let deepCopy = JSON.parse(JSON.stringify(opts)).map(e=>{
e.label = e.active==value?e.active:e.inactive;
return e;
});
setOpts(deepCopy);
}}>
{opts.map(o=>
<option key={o.active} value={o.active}>{o.label}</option>)}
</select>
</div>);
}
const el = document.querySelector("#app");
ReactDOM.render( < App / > , el);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>
I have JSON file like this
[
{
"id": 1,
"country": "Afghanistan",
"city": ["Eshkashem","Fayzabad","Jurm","Khandud"]
},
{
"id": 2,
"country": "Italy",
"city": ["Milano","Rome","Torino","Venezia"]
}
]
and I want to iterate through array placed in the city. Idea is to have two selects, where the first select is reserved for countries and the second is reserved for cities. Whenever the user selects a country, I want to populate the second select with a list of cities. Problem is that I receive only one array of all cities for that country. Here is my code:
export default class DiffCountries extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
contacts: [],
selectedCountry: [],
selectedCity: []
}
}
onChangeHandler = (event) => {
const test = CountriesData[event.target.value - 1];
this.setState({
selectedCountry: test,
selectedCity: this.state.selectedCountry.city
})
console.log(this.state.selectedCity);
}
render() {
const { contacts } = this.state;
return (
<div>
<select name="" id="" onChange={this.onChangeHandler}>
{CountriesData.map(item => {
const { id, country } = item;
return <option key={id} value={id}>{country}</option>
})}
</select>
<select name="" id="">
{this.state.selectedCountry !== undefined ?
<option value="">{this.state.selectedCountry.city}</option> :
null
}
</select>
</div>
<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>
And here is the screenshot of my problem
Thank you in advance!
You need to use map() on the city array.
<select name = "" id = "" > {
this.state.selectedCountry !== undefined ?
this.state.selectedCountry.city.map((x,i) => <option value={x} key={i}>{x}</option>)
:null
}
</select>
You need to iterate through the array.
this.state.selectedCountry.city.map((city, index) => {
return <option value={city} key={index}>{city}</option>
})
Be aware, that using the index as a key is considered an anti pattern. You could use the name of the city as a key as well. E.g.:
this.state.selectedCountry.city.map(city => {
return <option value={city} key={city}>{city}</option>
})
edit to add link to mdn docs as suggested in comments: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
Example:
const CountriesData = [
{
id: 1,
country: 'Afghanistan',
city: ['Eshkashem', 'Fayzabad', 'Jurm', 'Khandud'],
},
{
id: 2,
country: 'Italy',
city: ['Milano', 'Rome', 'Torino', 'Venezia'],
},
];
class DiffCountries extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedCountry: null,
};
}
onChangeHandler = event => {
const selectedCountry = CountriesData[event.target.value - 1];
this.setState({
selectedCountry,
});
};
render() {
const { selectedCountry } = this.state;
return (
<div>
<select
name="country"
defaultValue="country"
onChange={this.onChangeHandler}
>
<option disabled value="country">
Select country
</option>
{CountriesData.map(({ id, country }) => (
<option key={id} value={id}>
{country}
</option>
))}
</select>
{selectedCountry && (
<select name="city" defaultValue="city">
<option disabled value="city">
Select city
</option>
{selectedCountry.city.map(item => (
<option key={item} value={item}>
{item}
</option>
))}
</select>
)}
</div>
);
}
}
ReactDOM.render(<DiffCountries />, document.getElementById('container'));