Cannot display 1 image from a Array from a API - reactjs

I can manage to display all and have them in roughly the right place with CSS, however in the case where there is more than 1 image, I cannot display just that first image
I eventually want to be able to click the image and display a Modal showing a gallery of these pics, but stuck on this part:
import React, { useState, useEffect } from "react";
import "./Hotel.css";
const URL = "https://obmng.dbm.guestline.net/api/hotels?collection-id=OBMNG";
const Hotel = () => {
const [hotel, setHotel] = useState([]);
useEffect(() => {
hotels();
}, []);
const hotels = async () => {
const response = await fetch(URL);
setHotel(await response.json());
};
// filter hotels button displayed by star rating
const filterHotels = (e) => {
const starRating = e.target.value;
const filteredHotels = hotel.filter(
(hotel) => hotel.starRating === starRating
);
setHotel(filteredHotels);
};
// store filteredHotels in state
const [filteredHotels, setFilteredHotels] = useState([]);
useEffect(() => {
setFilteredHotels(hotel);
}, [hotel]);
return (
<div>
<div className="selection-filter">
{/* drop down for useState */}
<label for="filter">Filter by star rating: </label>
<select onChange={filterHotels}>
<option value="0">All</option>
<option value="1">1 Star</option>
<option value="2">2 Star</option>
<option value="3">3 Star</option>
<option value="4">4 Star</option>
<option value="5">5 Star</option>
</select>
</div>
{hotel.map((data) => {
return (
<div>
<div className="list-group-item hotel-area" key={data.id}>
<div className="hotel-name">{data.name}</div>
{data.images.map((image) => (
<img
className="hotel-image"
src={data.images[0].url}
alt={image.alt}
/>
))}
<div className="hotel-address">{data.address1}</div>
<div className="hotel-address">{data.address2}</div>
<div className="star-rating fas fa-star">{data.starRating}</div>
<hr />
</div>
</div>
);
})}
</div>
);
};
export default Hotel;

It displays 3 images, because the first hotel object in the response object has 3 elements in the images array. If you want to show only one (first) image, don't use .map function, just render the first image, see this example.

Related

Can you tell me if my code is right or wrong?

Page1 code
import React, { useState } from 'react'
import Header from '../Components/Header'
import MainQuiz from '../Components/MainQuiz'
const Turner = () => {
const [select, setSelect] = useState();
return (
<>
<Header />
<div className='text-center fs-3 text-uppercase'>Turner</div>
<div className="container p-5">
<select className="form-select" aria-label="Default select example" value={select} onChange={e => setSelect(e.target.value)}>
<option selected>Select The Quiz</option>
<option value="1">Turner 1</option>
<option value="2">Turner 2</option>
<option value="3">Turner 3</option>
<option value="4">Turner 4</option>
</select>
<MainQuiz value={select}/>
</div>
</>
)
}
export default Turner
Page2 code
import React, { useEffect, useState } from 'react'
const MainQuiz = (props) => {
const [data, setData] = useState([])
const url = `https://quizstcapi.herokuapp.com/turner/${props.value}`
useEffect(() => {
fetchData()
}, [])
const fetchData = () => {
fetch(url)
.then((res) => res.json())
.then((responce) => {
setData(responce);
})
}
return (
<div>
<h4> {props.value} </h4>
<h4> {url} </h4>
{data.map((item,i) => (
<>
<h3> {i} </h3>
<h3> {item.Question} </h3>
<h5> {item.Option1} </h5>
<h5> {item.Option2} </h5>
<h5> {item.Option3} </h5>
<h5> {item.Option4} </h5>
</>
))}
</div>
)
}
export default MainQuiz
I press ctrl+s in page 2 for fetch.
My problem is I cant fetch api directly. for fetch i need to choose option than come back to my code and press ctrl+s then it fetch the api i choose but i need is when i choose it directy show data of apis
here is the some images
after choosing
https://firebasestorage.googleapis.com/v0/b/trulyrockmusics.appspot.com/o/3.png?alt=media&token=a91f05f5-eff8-4f3b-aa53-324ed58f1c8e
after pressing ctrl+s
https://firebasestorage.googleapis.com/v0/b/trulyrockmusics.appspot.com/o/4.png?alt=media&token=7b7a9935-fa0c-4835-9c0f-705bc13ee124
Kindly give me a solution for this. thanks
Although your question is a bit hard to make sense of, my crystal ball is telling the issue is your data loading isn't aware of the value prop changing, since you compute url outside of the effect that actually loads the data.
Then, you'll need to make value a dependency of the effect, so when the value changes (when you change it in the select and pass it down to MainQuiz, the effect is re-run.
import React, {useEffect, useState} from 'react'
function MainQuiz({value}) {
const [data, setData] = useState(null);
useEffect(() => {
setData(null);
fetch(`https://quizstcapi.herokuapp.com/turner/${value}`)
.then((res) => res.json())
.then(setData);
}, [value]);
if (!data) return <>Loading...</>;
return (
<div>
{data.map(...)}
</div>
);
}

Get currency rates based on currency selection

When we enter some value in text box and currency in the fromCurrency dropdown field and select appropriate currency in the toCurrency dropdown field, how do we display rates in the toCurrency based on that selection ?
https://codesandbox.io/s/rough-http-jc35u?file=/src/App.js
import React, { useState, useEffect } from "react";
import "./styles.css";
const axios = require("axios");
function App() {
const [sourceCurrency, setSourceCurrency] = useState("");
const [targetCurrency, setTargetCurrency] = useState("");
const [ratesList, setRatesList] = useState([]);
const [selectFromCurrency, setFromSourceCurrency] = useState("");
const [selectToCurrency, setSelectToCurrency] = useState("");
const getSourceCurrency = (source) => {
setSourceCurrency(source);
};
const getTargetCurrency = (target) => {
setTargetCurrency(target);
};
useEffect(() => {
const fetchData = async () => {
try {
const data = await axios.get("https://api.exchangeratesapi.io/latest");
setRatesList(data);
console.log(data);
} catch (e) {
console.log(e);
}
};
fetchData();
}, []);
const selectSourceCurrency = (sourceCurr) => {
setFromSourceCurrency(sourceCurr);
};
const selectTargetCurrency = (targetCurr) => {
setSelectToCurrency(targetCurr);
};
const convertRate = () => {
const rateCalc = sourceCurrency * targetCurrency;
console.log("print rate: " + rateCalc);
// how can we the rates list here and based on the selection ?
};
return (
<div className="App">
<div className="globalCurrencyConverter">
<h2>Currency Converter</h2>
<div className="container box">
<label>
<input
name="sourceCurrency"
type="text"
placeholder="fromCurrency"
onChange={(event) => getSourceCurrency(event.target.value)}
/>
<select
className="fromCurrency"
defaultValue={"DEFAULT"}
onChange={(event) => selectSourceCurrency(event.target.value)}
>
<option>USD</option>
<option value="DEFAULT">AUD</option>
<option>NZD</option>
<option>INR</option>
<option>UAE Dirham</option>
</select>
</label>
<label>
<input
name="targetCurrency"
type="text"
placeholder="toCurrency"
onChange={(event) => getTargetCurrency(event.target.value)}
/>
<select
className="toCurrency"
onChange={(event) => selectTargetCurrency(event.target.value)}
>
<option>USD</option>
<option>AUD</option>
<option>NZD</option>
<option>INR</option>
<option>UAE Dirham</option>
</select>
</label>
<div className="recordBtn">
<button name="convert" onClick={(event) => convertRate()}>
Convert
</button>
</div>
</div>
</div>
</div>
);
}
export default App;
I will assume that you can handle the population of those select fields with currencies yourself and instead will show you how to solve the actual conversion problem. So we shall leave those select options hardcoded as they are in your code. e.g. (USD, NZD, AUD etc.)
So we won't actually even need that useEffect for this test since we simply hardcode the currencies. Personally, I like to solve my React problems with as little re-renders as possible. So the way I would approach this specific problem is by creating references to all 4 of your fields. It will allow us to access their values any time. Check out useRef().
Then when someone enters all the info and clicks that "Convert" button, I would call your API and pass it the selected currency as base currency. like so
https://api.exchangeratesapi.io/latest?base=USD
Once axios fetches the data on it, it is just a matter of some basic match and assignment of the proper value to the "To Currency" field. So here is a working example along with a Sandbox:
import React, { useState, useEffect, useRef } from "react";
import "./styles.css";
const axios = require("axios");
function App() {
const from_select = useRef(),
to_select = useRef(),
from_input = useRef(),
to_input = useRef();
useEffect(() => {
const fetchData = async () => {
try {
const data = await axios.get("https://api.exchangeratesapi.io/latest");
//setRatesList(data);
console.log(data);
} catch (e) {
console.log(e);
}
};
fetchData();
}, []);
const convertRate = () => {
const from_cur = from_select.current.value;
const to_cur = to_select.current.value;
const from_amount = from_input.current.value;
console.log(from_cur);
axios
.get("https://api.exchangeratesapi.io/latest?base=" + from_cur)
.then((result) => {
const rate = result.data.rates[to_cur];
const converted_amount = rate * from_amount;
to_input.current.value = converted_amount;
});
};
return (
<div className="App">
<div className="globalCurrencyConverter">
<h2>Currency Converter</h2>
<div className="container box">
<label>
<input
ref={from_input}
name="sourceCurrency"
type="text"
placeholder="fromCurrency"
/>
<select
ref={from_select}
className="fromCurrency"
defaultValue={"USD"}
>
<option value="USD">USD</option>
<option value="AUD">AUD</option>
<option value="NZD">NZD</option>
</select>
</label>
{" -> "}
<label>
<input
ref={to_input}
name="targetCurrency"
type="text"
placeholder="toCurrency"
/>
<select ref={to_select} className="toCurrency" defaultValue="AUD">
<option value="USD">USD</option>
<option value="AUD">AUD</option>
<option value="NZD">NZD</option>
<option value="RUB">RUB</option>
<option value="EUR">EUR</option>
</select>
</label>
<div className="recordBtn">
<button name="convert" onClick={convertRate}>
Convert
</button>
</div>
</div>
</div>
</div>
);
}
export default App;
your ratesList would be an object extracted from data.data.rates with country keys and rate values set at initial useEffect as:
useEffect(() => {
const fetchData = async () => {
try {
const data = await axios.get("https://api.exchangeratesapi.io/latest");
setRatesList(data.data.rates);
} catch (e) {
console.log(e);
}
};
fetchData();
}, []);
your convertRate validates first if sourceCurrency is a number and if there is a ratesList. To calculate the conversion you need to multiply the amount value by the ratio (toCurrency/FromCurrency):
const convertRate = () => {
if (isNaN(sourceCurrency) || !ratesList) return;
setTargetCurrency(
(ratesList[selectToCurrency] / ratesList[selectFromCurrency]) *
sourceCurrency
);
};
set initial values for currencies:
const [selectFromCurrency, setFromSourceCurrency] = useState("USD");
const [selectToCurrency, setSelectToCurrency] = useState("NZD");
and remove default values for your select and input values. Instead pass the state value to have a controlled input like:
<select
className="fromCurrency"
value={selectFromCurrency}
onChange={(event) => selectSourceCurrency(event.target.value)}
>
<option>USD</option>
<option>AUD</option>
<option>NZD</option>
<option>INR</option>
<option>PLN</option>
</select>
for your toCurrency input make it a disabled field, since you don't user to type values on it:
<input
name="targetCurrency"
value={targetCurrency}
disabled
type="text"
placeholder="toCurrency"
/>
working demo:
note: UAE Dirham doesn't match at API response so changed for PLN

How do I loop through the Axios response to a table in React

I am trying to develop a mini project that retrieves the list of available listed jobs in React and Node as Backend. Little bit stuck at the response from the axios.
This is the response am getting from the axios response.
I want to display the array data into a table or list to show available jobs
Below is the code for that retrieves the data
import React, { useState, useEffect } from 'react'
import Layout from '../../core/Layout'
import axios from 'axios'
import { getCookie, isAuth, signout } from '../../auth/helpers';
const Find = () => {
const [values, setValues] = useState({
title:"",
duration:"",
durationSys:"",
budget:'',
addedBy:'',
category:'',
results:[],
searched: false
});
const { category} = values;
const token = getCookie('token');
const handleChange = category => event => {
console.log(event.target.value);
setValues({ ...values, [category]: event.target.value});
};
const handleClick = event =>{
event.preventDefault()
listJobs()
}
const listJobs = () =>{
axios.get( `${process.env.REACT_APP_API}/search-projects`,
{params: {category
}
})
.then(response => {
console.log('LOG SUCCESS --', response.data);
const data = response.data;
setValues({...values, results: data})
console.log('LOG STATE', data)
})
}
return (
<Layout>
<div class="form-group">
<label for="exampleFormControlSelect1">Category</label>
<select onChange={handleChange('category')} value={category} class="form-control"
id="exampleFormControlSelect1">
<option>--Select Category --</option>
<option value='Web Development'>Web Development</option>
<option value='Logo Design'>Logo Design</option>
<option value='Writing/Skills'>Writing/Skills</option>
<option value='Mobile App Development'>Mobile App Development</option>
<option value='SEO/Marketing'>SEO/Marketing</option>
</select>
</div>
<div class="d-flex justify-content-center">
<button onClick={handleClick} class="btn btn-default btn-info" style={{marginBottom: "15px"}}>Search</button>
</div>
<div>
<h5>List of available jobs</h5>
//here
</div>
</Layout>
)
}
export default Find;
Hi you can do something like this.
<ul>
(results || []).map((item, index) => <li key={index}> {item}</li>
</ul>
I would also suggest to convert your handleChange function ( and the rest ) to useCallback functions to reduce unnecessary updates.
Suppose job has some id, title and description:
{ results.map(( job, index ) => {
return (
<tr key={job.id}>
<td>{job.id}</td>
<td>{job.title}</td>
<td>{job.description}</td>
</tr>
);
})}
Or destructing:
{ results.map(({id, title, description}, index ) => {
return (
<tr key={id}>
<td>{id}</td>
<td>{jtitle}</td>
<td>{description}</td>
</tr>
);
})}
more info: https://flaviocopes.com/react-how-to-loop/

How to dynamically load a component from an object in state array?

When I make a selection from the dropdown I saved the selected value to type then when I click the button I add an object to drums, I map over thee drums and based on the type I want to render the component with the same name.
Sandbox here
import React, { useState } from "react";
import uuid from "react-uuid";
import "./styles.css";
const Snare = () => {
return <div>Snare</div>;
};
const Gong = () => {
return <div>Gong</div>;
};
export default function App() {
const [drums, setDrums] = useState([]);
const [type, setType] = useState();
return (
<div className="App">
{drums.map((Drum, index) => (
<Drum.type /> // Why cant I use `.type`?
))}
<label>
Drum type to add:
<select onChange={e => setType(e.target.value)} value={type}>
<option value="">Select...</option>
<option value="Snare">Snare</option>
<option value="Gong">Gong</option>
</select>
<button
onClick={() => {
setDrums([...drums,
{
id: uuid(),
type
}
]);
}}
>
Add drum
</button>
</label>
</div>
);
}
In your case Drum.type is not a component but a string, you need to maintain a map of the string to component and then render it
const map = {
Snare: Snare,
Gong: Gong
};
export default function App() {
const [drums, setDrums] = useState([]);
const [type, setType] = useState();
return (
<div className="App">
{drums.map((Drum, index) => {
const Component = map[Drum.type];
return <Component key={index}/>;
})}
<label>
Drum type to add:
<select onChange={e => setType(e.target.value)} value={type}>
<option value="">Select...</option>
<option value="Snare">Snare</option>
<option value="Gong">Gong</option>
</select>
<button
onClick={() => {
setDrums([
...drums,
{
id: uuid(),
type
}
]);
}}
>
Add drum
</button>
</label>
</div>
);
}
Working demo
That's because the type is a string.
You could create a mapping to solve this and use React.createElement().
Something like:
const mapping = {
'Snare': Snare,
'Gong': Gong
}
{ drums.map(({ type }, index) => (
React.createElement(mapping[type], { key: index })
))
}

onChange not updating React

I've been working on this project for the last couple of hours and I was pretty sure that this final hour would be my last. No errors are appearing. My thinking is that when I pick a hero from the drop down, the page will update depending on my choice. I may have something that isn't firing that I'm not picking up on.
import React, {useEffect, useState} from 'react'
import axios from 'axios'
require("regenerator-runtime/runtime");
const App = () => {
const [hero, selectedHero] = useState(
'Select a Hero'
);
const handleChange = event => selectedHero(event.target.value);
return(
<HeroSelect heroSelect={hero} onChangeHeadline={handleChange} />
);
};
const HeroSelect = ({heroSelect, onChangeHeadline}) => {
const [data, setData] = useState({heroes: []});
useEffect(() => {
const fetchData = async () => {
const result = await axios(
'https://api.opendota.com/api/heroStats',
);
setData({...data, heroes: result.data});
};
fetchData();
}, []);
return (
<div>
<h1>{heroSelect}</h1>
<select>
{data.heroes.map(item => (
<option key={item.id} value={heroSelect} onChange={onChangeHeadline} >
{item.localized_name}
</option>
))}
</select>
</div>
)
};
export default App
Define your onChange={onChangeHeadline} on Select tag not on option tag
<select onChange={onChangeHeadline}>
{data.heroes.map(item => (
<option key={item.id} value={item.localized_name}>
{item.localized_name}
</option>
))}
</select>
You should be firing your onChange event on the select tag itself.
<select onChange={onChangeHeadline} >
.....
.....
</select>
I reckon you didn't declare an onChange on the select.
Using This method:
<select id="lang" onChange={this.change} value={this.state.value}>
<option value="select">Select</option>
<option value="Java">Java</option>
<option value="C++">C++</option>
</select>

Resources