fetch data in select react - javascript

There is a form with selects, in the first one a film from a series of Star Wars is selected. And the other two should display spaceships from the selected movie. I don't really understand how to write the condition correctly, if you do it on if, then a lot of code turns out, I'm sure there is a more rational way.
Component code:
class StarshipsCompare extends Component {
swapiService = new SwapiService();
state = {
filmList: [],
};
componentDidMount() {
this.swapiService.getAllFilms().then((filmList) => {
this.setState({ filmList });
});
}
renderItems(arr) {
return arr.map(({ id, title }) => {
return (
<option value={title} key={id}>
{title}
</option>
);
});
}
render() {
const { filmList } = this.state;
const items = this.renderItems(filmList);
return (
<div>
<form>
<p>Выберите фильм корабли из которого хотите сравнить:</p>
<select className="custom-select">{items}</select>
<div className="row p-5">
<div className="col">
<p>Выберите корабль для сравнения:</p>
<select className="custom-select"></select>
</div>
<div className="col">
<p>Выберите корабль для сравнения:</p>
<select className="custom-select"></select>
</div>
</div>
</form>
</div>
);
}
}
And fetch data:
export default class SwapiService {
_apiBase = "https://swapi.dev/api";
async getResource(url) {
const res = await fetch(`${this._apiBase}${url}`);
if (!res.ok) {
throw new Error(`Could not fetch ${url}, status: ${res.status}`);
}
return await res.json();
}
getAllFilms = async () => {
const res = await this.getResource("/films/");
return res.results.map(this._transformFilms);
};
}

This is a simple example of how I would do it. The usage of the && is an elegant replacement for if statements in JSX.
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/#babel/standalone/babel.min.js"></script>
<div id="app_root"></div>
<script type="text/babel">
// Constants representing API Data.
const MAIN_SELECT_OPTIONS = ["A", "B", "C"];
const SUB_SELECT_OPTIONS = {
A: ["A1", "A2", "A3"],
B: ["B1", "B2", "B3"],
C: ["C1", "C2", "C3"]
};
const SelectState = () => {
const [mainSelect, setMainSelect] = React.useState("");
const [subSelect, setSubSelect] = React.useState("");
const [subSelectOptions, setSubSelectOptions] = React.useState([]);
const onMainSelectChange = (event) => {
setMainSelect(event.target.value);
setSubSelect("");
if (event.target.value) {
setSubSelectOptions(SUB_SELECT_OPTIONS[event.target.value]);
} else {
setSubSelectOptions([]);
}
};
const onSubSelectChange = (event) => {
setSubSelect(event.target.value);
};
return (
<div>
<h1> Main Select </h1>
<select onChange={onMainSelectChange} value={mainSelect}>
<option value="">Select Main</option>
{MAIN_SELECT_OPTIONS.map((option) => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
<h1> Sub Select </h1>
<select onChange={onSubSelectChange} value={subSelect}>
{!subSelect && <option>Select a State</option>}
{subSelectOptions.map((option) => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
</div>
);
};
ReactDOM.render(<SelectState />, document.getElementById("app_root"));
</script>

Related

Create a multi level dropdown list in react from JSON list of objetcs

Hello im new to react and Im hoping someone can help out. So I have a JSON file from an api i made and this is the request to get all theaters, I only have one theater for this api but that theater has multiple movies, showtimes
{
"theatreId": 1,
"theatreName": "Scotiabank Theater Chinook",
"movies": [
{
"movieId": 4,
"movieName": "Now You See Me",
"dateAdded": "2022-12-31",
"showtimes": [
{
"showtimeId": 14,
"showtimeDate": "2022-12-10",
"showtimeTime": "11:00:00",
"seats": [
{
"seatId": 218,
"seatNum": 10,
"theTicket": null
},
{
"seatId": 220,
"seatNum": 12,
"theTicket": null
},
{
"seatId": 214,
"seatNum": 6,
"theTicket": null
},
{
"seatId": 210,
"seatNum": 2,
"theTicket": null
},
{
"seatId": 219,
"seatNum": 11,
"theTicket": null
},
....{more data points here}
I am trying to create a multi level select option dropdown list i.e. when I select a menu from the first dropdown, based on that option the next dropdown would be a list of options of what was in the previous option was.
In this case, I have just one cinema, "Scotibank Chinook" when I select that cinema option, I want the next option which is movie to be a list of movies based on "movies" since its values is an array of objects and the same for showtimes and seats as seen in the JSON file.
I have a react code where I make each name that has an array of objects to make useState list of those object names, my main issue is to navigate into the array of objects and setState of the List to be the List of objects
import React, {useState,useEffect} from 'react';
import axios from 'axios';
function Form() {
const [theaterName, setTheaterName] = useState("")
const [movieName, setMovieName] = useState("")
const [showTime, setShowTime] = useState("")
const [theaterList, setTheaterList] = useState([{'theatreId':''}])
const [movieList, setMovieList] = useState([{'movieId':''}])
const [showTimeList, setShowTimeList] = useState([{'showtimeId':''}])
useEffect(() =>{
fetchTheater();
fetchMovie();
fetchShowTime()
}, [])
const fetchTheater = () => {
axios.get('http://localhost:8080/api/v1/theatre')
.then(response => {
console.log(response.data)
setTheaterList(response.data);
})
.catch(error => console.log(error));
}
const fetchMovie = () => {
axios.get('http://localhost:8080/api/v1/movie')
.then(response => {
console.log(response.data)
setMovieList(response.data);
})
.catch(error => console.log(error));
}
const fetchShowTime = () => {
axios.get('http://localhost:8080/api/v1/showtime')
.then(response => {
console.log(response.data)
setShowTimeList(response.data);
})
.catch(error => console.log(error));
}
const handleTheaterChange = (event) =>{
setTheaterName(event.target.value);
}
const handleMovieChange = (event) =>{
setMovieName(event.target.value);
}
const handleShowTimeChange = (event) =>{
setShowTime(event.target.value);
}
const saveBtn = (e) => {
e.preventDefault();
console.log('Theater Selected',theaterName);
console.log('Movie Selected',movieName);
console.log('Showtime Selected',showTime);
}
return (
<div>
<div className="container-fluid">
<div className="row">
<div className="col-sm-4">
<h2 className="alert alert-warning">Movies</h2>
<br />
<select className="form-control" value={theaterName} onChange={handleTheaterChange}>
<option value="">Choose theatre</option>
{theaterList.map(theater => (
<option key={theater.theatreId} >
{theater.theatreName}
</option>
))}
</select>
<br/>
<select className="form-control" value={movieName} onChange={handleMovieChange}>
<option value="">Choose Movie</option>
{movieList.map(movie => (
<option value={movie.movieName} key={movie.movieId} >{"Hello"}</option>
))}
</select>
<br/>
<select className="form-control" value={showTime} onChange={handleShowTimeChange}>
<option value="">Choose showtime</option>
{showTimeList.map(showtime => (
<option value={showtime.showtimeDate} key={showtime.showtimeId} >{showtime.showtimeDate}</option>
))}
</select>
<button className="btn btn-primary" onClick={saveBtn}>Save</button>
</div>
</div>
</div>
</div>
)
}
export default Form
currently I am accessing each option by fetching the individual api endpoints for the the options and setting the state to a setState List
You only need one api which is the theater endpoint.
I don't know which backend you are using, but you need one response, it should contain all these nested lists.
then simply in react, use this logic
import React, {useState,useEffect} from 'react';
import axios from 'axios';
function Form() {
const [theater, setTheater] = useState("")
const [movie, setMovie] = useState("")
const [showTime, setShowTime] = useState("")
const [theaterList, setTheaterList] = useState([])
const [movieList, setMovieList] = useState([])
const [showTimeList, setShowTimeList] = useState([])
useEffect(() =>{
fetchTheater();
}, [])
const fetchTheater = () => {
/*using fetch
fetch(`http://localhost:8080/api/v1/theatre`,
{
headers : {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
}).then(res => res.json()).then(
data => {
console.log(data);
setTheaterList(data);
//setReady(true);
console.log("theater id: "+data[0].theaterId);
}).catch(error => {
console.log(error);
});*/
// using axios
axios.get('http://localhost:8080/api/v1/theatre')
.then(response => {
console.log(response.data)
setTheaterList(response.data);
})
.catch(error => console.log(error));
}
const handleTheaterChange = (event) =>{
let id = event.target.value;
console.log("theater changed: "+id);
//setTheater(event.target.value);
theaterList.map(theater => {
console.log(theater.theaterId);
if(theater.theaterId === parseInt(id)){
console.log("theater found "+theater.theaterId);
setMovieList(theater.movies);
setTheater(theater);
}
});
}
const handleMovieChange = (event) =>{
let id = event.target.value;
console.log("movie changed: "+id);
movieList.map(movie => {
console.log(movie.movieId);
if(movie.movieId === parseInt(id)){
console.log("theater found "+movie.movieId);
setShowTimeList(movie.showtimes)
setMovie(movie);
}
});
}
const handleShowTimeChange = (event) =>{
let id = event.target.value;
console.log("showtime changed: "+id);
showTimeList.map(showtime => {
console.log(showtime.showtimeId);
if(showtime.showtimeId === parseInt(id)){
console.log("showtime found "+showtime.showtimeId);
// furthermore, you can set the seat list
//setSeatList(showtime.seats)
setShowTime(showtime);
}
});
}
const saveBtn = (e) => {
e.preventDefault();
console.log('Theater Selected',theater.theaterName);
console.log('Movie Selected',movie.movieName);
console.log('Showtime Selected',showTime.showtimeTime);
}
return (
<div>
<div className="container-fluid">
<div className="row">
<div className="col-sm-4">
<h2 className="alert alert-warning">Movies</h2>
<br />
<select className="form-control" onChange={handleTheaterChange}>
<option value="">Choose theatre</option>
{theaterList.map(theater => (
<option value={theater.theaterId} key={theater.theaterId}>
{theater.theaterName}
</option>
))}
</select>
<br/>
<select className="form-control" onChange={handleMovieChange}>
<option value="">Choose Movie</option>
{movieList.map(movie => (
<option value={movie.movieId} key={movie.movieId} >{movie.movieName}</option>
))}
</select>
<br/>
<select className="form-control" onChange={handleShowTimeChange}>
<option value="">Choose showtime</option>
{showTimeList.map(showtime => (
<option value={showtime.showtimeId} key={showtime.showtimeId} >{showtime.showtimeDate}</option>
))}
</select>
<button className="btn btn-primary" onClick={saveBtn}>Save</button>
</div>
</div>
</div>
</div>
)
}
export default Form
important! change the word 'theatre' to 'theater', or you use either of them, but consistently.

React : why I have to click twice on the same option to set in select box

import ProductCard from "./ProductCard";
import "../Styles/Products.css";
import "../Styles/Filter.css";
import { v4 as uuidv4 } from "uuid";
const Products = (props) => {
const skins = props.skins;
// const [filteredSkins, setFilteredSkins] = useState();
const [gameFilter, setGameFilter] = useState("DEFAULT");
const [qualityFilter, setqualityFilter] = useState("DEFAULT");
let skinsObj = {};
let qualityObj = {};
for (let i = 0; i < skins.length; i++) {
skinsObj[skins[i].gameName] = i;
qualityObj[skins[i].quality] = i;
}
const setGame = (e) => {
setGameFilter(e.target.value);
console.log(gameFilter, qualityFilter);
};
const setquality = (e) => {
setqualityFilter(e.target.value);
console.log(gameFilter, qualityFilter);
};
console.log(gameFilter, qualityFilter);
return (
<React.Fragment>
<div className="filter_option">
<div className="filter_by_game Filter-Box">
<label htmlFor="games">Game : </label>
<select name="games" id="games" onChange={(e) => setGame(e)}>
<option value="DEFAULT">All</option>
{Object.keys(skinsObj).map((game) => {
return (
<option value={game} key={uuidv4()}>
{game}
</option>
//
);
})}
</select>
</div>
<div className="filter_by_quality Filter-Box">
<label htmlFor="quality">Quality : </label>
<select name="quality" id="quality" onChange={(e) => setquality(e)}>
<option value="all">All</option>
{Object.keys(qualityObj).map((quality) => {
return (
<option value={quality} key={uuidv4()}>
{quality}
</option>
//
);
})}
</select>
</div>
</div>
<div className="product-wrapper">
{skins &&
skins.map((skin) => {
return (
<ProductCard
key={uuidv4()}
className="product-list"
name={skin.name}
icon={skin.gameName}
price={skin.price}
quality={skin.quality}
picture={skin.picture}
/>
);
})}
</div>
</React.Fragment>
);
};
export default Products;
I need to click on any option from the list twice in a row to be applied in the box but the state change when I log it, also the second select affected when I change the other one both get reset to default try some solution but nothing help don't know if it is problem with the way of importing data or what
You currently don't set the value prop on <select>.

Fetch the value of props other than "value" from a custom component?

So I have the below code:
function Crafting(props) {
const craftItems = [
{
key: 1,
name: "Bronze sword",
},
{
key: 2,
name: "Iron sword",
},
{
key: 3,
name: "Steel sword",
},
];
const [item, setItem] = useState();
const [itemKey, setItemKey] = useState();
function onChangeHandler(event) {
setItem(event.target.value);
setItemKey(event.target.itemID);
console.log(event);
}
function onSubmitHandler(event) {
event.preventDefault();
console.log(item);
console.log(itemKey);
}
return (
<Card className={classes.renderwin}>
<form onSubmit={onSubmitHandler}>
<label htmlFor="items">Select an item</label>
<select name="items" onChange={onChangeHandler}>
{craftItems.map((item) => (
<option key={item.key} itemID={item.key}> {item.name} </option>
))}
</select>
<Button type="submit">Craft item</Button>
</form>
</Card>
);
}
I want to be able to retrieve the original key value against the item. I tried using "key", then added in a custom prop called "itemID" but they both just return as undefined. How do I fetch the ID back based on the value selection?
The issue is that you can basically store only an option value and retrieve that in the handler to save into state.
I would place the item.key on the option element's value attribute.
<select name="items" onChange={onChangeHandler}>
{craftItems.map((item) => (
<option key={item.key} value={item.key}>
{item.name}
</option>
))}
</select>
Access the onChange event object's value property in the handler and convert it back to a Number type before storing in state.
function onChangeHandler(event) {
const { value } = event.target;
setItemKey(Number(value));
}
When accessing in the submit handler use the stored itemKey to search the craftItems array for the matching object.
function onSubmitHandler(event) {
event.preventDefault();
const item = craftItems.find(({ key }) => key === itemKey);
console.log(item?.name);
console.log(item?.key);
}
function Crafting(props) {
const craftItems = [
{
key: 1,
name: "Bronze sword"
},
{
key: 2,
name: "Iron sword"
},
{
key: 3,
name: "Steel sword"
}
];
const [itemKey, setItemKey] = React.useState();
function onChangeHandler(event) {
const { value } = event.target;
setItemKey(Number(value));
console.log({ value });
}
function onSubmitHandler(event) {
event.preventDefault();
const item = craftItems.find(({ key }) => key === itemKey);
console.log(item && item.name);
console.log(item && item.key);
}
return (
// <Card className={classes.renderwin}>
<form onSubmit={onSubmitHandler}>
<label htmlFor="items">Select an item</label>
<select name="items" onChange={onChangeHandler}>
{craftItems.map((item) => (
<option key={item.key} value={item.key}>
{item.name}
</option>
))}
</select>
<button type="submit">Craft item</button>
</form>
// </Card>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<Crafting />,
rootElement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="root" />
Place a value on the option like this:
{craftItems.map((item) => (
<option value={item.key} key={item.key}>{item.name}</option>
))}

How to get individual items to display on a page?

database
I have an interesting bug in my code that I can`t figure out. It should be a simple React+Firestore setup, listing items on one page, and showing more details on each item on the next. Unfortunately, it only shows details for the first item on the list.
Have been digging through the Firestore documentation, where I found the following solution. It's not working...
Details
import React, { useState, useEffect } from "react";
import firebase from "./firebase";
import Servis from "./funkc/servisni";
export default function FireDetail({ match }) {
// console.log(match);
console.log(match.params.id);
const [item, setItem] = useState([]);
const [loading, setLoading] = useState(true);
const getIt = () => {
setLoading(true);
const item = [];
const docRef = firebase
.firestore()
.collection("polja")
.doc("id", "==", match.params.id);
docRef.onSnapshot((doc) => {
if (doc.exists) {
console.log("Document data:", doc.data());
setItem(doc.data());
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
});
setLoading(false);
};
useEffect(() => {
getIt();
}, [match]);
if (loading) {
return <h3>samo malo...</h3>;
}
return (
<div className="container">
<div>
Kontakt: tip - email
<p> {item.Kontakt} </p>
</div>
<div>
<p>Datum rodjenja: {item.Datum}</p>
{item.Prezime} {item.Ime}
</div>
</div>
);
}
List
the component that lists all of the items in the database...
const SORTER = {
"Prezime A-Z": { column: "Prezime", direction: "asc" },
"Prezime Z-A": { column: "Prezime", direction: "desc" },
"Email A-Z": { column: "Kontakt", direction: "asc" },
};
const PAGER = {
5: { Max: "5" },
30: { Max: "30" },
45: { Max: "45" },
};
export default function FireList() {
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(false);
const [sortBy, setSortBy] = useState("Prezime A-Z");
const [displayMax, setDisplayMax] = useState("5");
const [query, setQuery] = useState("");
// function routeTo() {
// const { id } = useParams();
// }
const ref = firebase
.firestore()
.collection("polja")
.orderBy(SORTER[sortBy].column, SORTER[sortBy].direction)
.limitToLast(PAGER[displayMax].column);
// console.log(ref);
function getEm() {
setLoading(true);
ref.get().then((querySnapshot) => {
const items = [];
querySnapshot.forEach((doc) => {
const item = {
...doc.data(),
id: doc.id,
};
items.push(item);
});
setItems(items);
// console.log(items);
setLoading(false);
});
}
useEffect(() => {
getEm();
}, [query, sortBy, displayMax]);
return (
<div>
<div>
{" "}
<label>Poredaj</label>
<select
value={sortBy}
onChange={(e) => setSortBy(e.currentTarget.value)}
>
<option value="Prezime A-Z"> Prezime A-Z </option>
<option value="Prezime Z-A"> Prezime Z-A </option>
<option value="Email A-Z"> Email A-Z </option>
</select>
</div>
<div>
<label> Max. po stranici </label>
<select
value={displayMax}
onChange={(e) => setDisplayMax(e.currentTarget.value)}
>
<option value="5">5</option>
<option value="30">30</option>
<option value="45">45</option>
</select>
</div>
<ul>
<input
type="text"
value={query}
onChange={(event) => setQuery(event.target.value)}
></input>
</ul>
{loading ? <h1>Loading...</h1> : null}
{items.map((val) => (
<div key={val.id}>
<p>
{val.Ime} {val.Prezime}
<Link to={`/kontakt/detalji/${val.id}`}> ajd </Link>
</p>
</div>
))}
</div>
);
}
App component, providing navigation
return (
<BrowserRouter>
<div className="App">
login
<div>
<Header />
</div>
<Route path="/" exact component={Header} />
<Route path="/adresar" component={FireList} />
<Route path="/kontakt" exact component={ContactEdit} />
<Route path="/kontakt/detalji/:id" component={FireDetail} />
</div>
</BrowserRouter>
);
}
export default App;
I think you must read the url parameter on each render. Thath meanes useEffect( yourfunction, [match]) (avoid using [ ] as second parameter this time)
Remember, ReactRouter doesnt reload or load a page when route somwhere. All is in memory, except the first time.

Search does not work like expect in react

In my application i use a search input to search values, and select input also to filter values from my data. Now my component looks like below:
export default function App() {
const [myData, setMydata] = useState([]);
const searchData = e => {
const v = e.target.value;
const res= data.filter(i =>
i.name.toLowerCase().includes(v.toLowerCase())
);
setMydata(res);
};
function handleChange(value) {
const res= data.filter(i => i.age === Number(value));
setMydata(res);
}
return (
<div className="App">
<Select defaultValue="" style={{ width: 120 }} onChange={handleChange}>
<Option value="2">2</Option>
<Option value="32">32</Option>
</Select>
<input onChange={searchData} />
<h1>Data</h1>
{myData.map((i, k) => (
<div key={k}>
{i.name} is <span>{i.age}</span>
</div>
))}
</div>
);
}
Now, the functionality works. If you search something, appear results, and if you try to select a value, also appears the value that you selected.
Issue: If i select from dropdown, for example: 32, appears:
Julia is 32
Bill is 32
Bill is 32
And now if i want to search from the list above just Julia, i type Julia in search, it search from the whole list of data, not just from the list which i get after i selected 32. How to solve this, and how to get the result from the last results, not to search from the whole list, but from the last result?
Note: the same issue is when i search first and after that i select a value from dropdown.
Your two filters always work with the same object data, and not previously filtered state data myData. Best practice save value of filters in state and each render filter data:
export default function App() {
const [age, setAge] = useState('');
const [name, setName] = useState('');
const filteredData = data
.filter(i => Boolean(age) ? i.age === Number(age) : true)
.filter(i => i.name.toLowerCase().includes(name.toLowerCase()));
return (
<div className="App">
<Select value={age} style={{ width: 120 }} onChange={setAge}>
<Option value="2">2</Option>
<Option value="32">32</Option>
</Select>
<input value={name} onChange={e => setName(e.target.value)} />
<h1>Data</h1>
{filteredData.map((i, k) => (
<div key={k}>
{i.name} is <span>{i.age}</span>
</div>
))}
</div>
);
}
Try this one out:
import React, { useState } from "react";
export default function App() {
const data = [
{ age: 2, name: 'John' },
{ age: 32,name: 'Mark' },
{ age: 22,name: 'Dell' },
{ age: 14,name: 'Linda' },
{ age: 16,name: 'Jon' },
{ age: 18,name: 'Ron' }
];
const [myData, setMydata] = useState([]);
const searchData = e => {
const v = e.target.value;
const res = data.filter(i =>
i.name.toLowerCase().includes(v.toLowerCase())
);
setMydata(res);
};
function handleChange(value) {
const res = data.filter(i => i.age === Number(value.target.value));
console.log("This is the value and respos", value.target.value);
setMydata(res);
}
return (
<div>
<select defaultValue="" style={{ width: 120 }} onChange={handleChange}>
<option value="2">2</option>
<option value="32">32</option>
</select>
<input onChange={searchData} />
<h1>Data</h1>
{myData.map((i, k) => (
<div key={k}>
{i.name} is <span>{i.age}</span>
</div>
))}
</div>
);
}
Here is the codesandbox demo: Demo

Categories

Resources