Option values inside a select with React - reactjs

I'm noobie on React and I want to fill a <select>
The problem that I have is when I want to click one of my items... dropdown only show different options if I put my items harcoded. Here's my code
Parent component
import React, { Fragment, useState, useEffect } from "react";
import Country from "./Country";
const CountriesList = ({ handleOnChange }) => {
const [countriesLoaded, setCountriesLoaded] = useState(false);
const [countriesList, setCountriesList] = useState([]);
const getCountries = async () => {
const api = "https://restcountries.eu/rest/v2/all";
const response = await fetch(api);
const countrieslst = await response.json();
setCountriesList(countrieslst);
setCountriesLoaded(true);
};
useEffect(() => {
getCountries();
}, [countriesList]);
return (
<Fragment>
<select id="country" name="country" onChange={handleOnChange}>
<option value="">-- Select a country --</option>
{countriesLoaded
? countriesList.map((country) => (
<Country key={country.alpha2Code} countryItem={country} />
))
: null}
</select>
</Fragment>
);
};
export default CountriesList;
child component
import React from "react";
const Country = ({ country }) => {
return <option value={country.alpha2Code}>{country.name}</option>;
};
export default Country;
If I check DOM it's ok but only shows my first option
Thnx 4 support and have a nice day!

[Edit]
just add <select id="country" name="country" onChange={handleOnChange} style={{display: 'block'}}> in your code. you will be able to view a select box rendering data
here is the link i made few changes in your code as well but this is optional:
https://codesandbox.io/s/nd58i?file=/src/components/Form.jsx

Related

keep value after page refresh in React

Actually, I passed dropdown value in the URL, but how to show the selected value after the page refresh? please solve this issue. I want to try when user select any option then value show on url and after page refresh same selected value show .Thank you
import React, { useState, useEffect } from "react";
import { Link ,navigate} from "gatsby";
export default function IndexPage() {
const [data, setData] = useState("black");
const Vdata = [{
title:"black"
},
{
title:'red'
}]
const handleChange = (value) => {
setData(value);
navigate(`/?location=${value}`);
};
return (
<div className="grid place-items-center">
<select
value={data}
autocomplete="off"
name=""
id=""
className="border p-2 shadow-xl"
onChange={(event) => handleChange(event.target.value)}
>
{Vdata.map((i) => (
<option value={i.title} selected>
{i.title}
</option>
))}
</select>
<p>{window.location.href}</p>
</div>
);
}
// export default IndexPage
The Window localStorage object allows you to save key/value pairs in the browser.
detail info
Initiate data with localStorage stored info, if null default set "black"
const [data, setData] = JSON.parse(localStorage.getItem('title')) || "black";
Add useEffect function to store this selected value when set data.
const handleChange = (value) => {
setData(value);
navigate(`/?location=${value}`);
};
React.useEffect(() => {
localStorage.setItem('title', JSON.stringify(data));
}, [data]);
Finally, Vdata.map((i) => ( should add some condition to set selected attribute.

(Reactjs) "delay" when fetching data using useEffect and useState

The below is my code.
What i am doing is a currency converter app by fetching external API. It is guaranteed process.env.REACT_APP_DATA_SOURCE is always correct.
The problem here is: If I change the currency, either inputCurrency or outputCurrency, I need to click "Submit" button twice instead of once to display a correct answer. Why is it the case?
I can't come up with any better wording so I used "delay" in the heading.
Thank you!
import './App.css';
import { useState, useEffect } from 'react';
import { Select, Button, ButtonGroup } from '#chakra-ui/react';
function App() {
const [exchangeRate, setExchangeRate] = useState(7.8491);
const [inputCurrency, setInputCurrency] = useState('USD');
const [inputAmount, setInputAmount] = useState(1);
const [outputCurrency, setOutputCurrency] = useState('HKD');
const [outputAmount, setOutputAmount] = useState(7.8491);
useEffect(() => {
const fetchData = async (url) => {
await fetch(url)
.then((response) => response.json())
.then((data) => setExchangeRate(data.conversion_rates[outputCurrency]));
};
try {
let url = `${process.env.REACT_APP_DATA_SOURCE}${inputCurrency}`;
fetchData(url);
} catch (err) {
console.log(err);
}
}, [inputCurrency, outputCurrency]);
const handleSubmit = async (e) => {
e.preventDefault();
setInputCurrency(e.target.inputCurrency.value);
setInputAmount(e.target.inputAmount.value);
setOutputCurrency(e.target.outputCurrency.value);
let result = inputAmount * exchangeRate;
setOutputAmount(result);
};
return (
<div className="main-body">
<form onSubmit={handleSubmit}>
<h3>Set input amount:</h3>
<input className="inputAmount" name="inputAmount" type="number" />
<h3>Set input currency:</h3>
<Select name="inputCurrency">
<option value="USD">USD</option>
<option value="HKD">HKD</option>
<option value="CNY">CNY</option>
<option value="GBP">GBP</option>
<option value="CAD">CAD</option>
</Select>
<h3>Set output currency:</h3>
<Select name="outputCurrency">
<option value="USD">USD</option>
<option value="HKD">HKD</option>
<option value="CNY">CNY</option>
<option value="GBP">GBP</option>
<option value="CAD">CAD</option>
</Select>
<Button colorScheme="blue" type="submit">
Submit
</Button>
</form>
<h3>
{inputAmount} {inputCurrency} is equal to:
</h3>
<h3>
{outputAmount} {outputCurrency}
</h3>
</div>
);
}
export default App;
You are calculating the output amount at the wrong place. First, you update the inputCurrency and outputCurrency in handleSubmit. This means the useEffect will be 'triggered' and the new exchange rate will be fetched. But that's not gonna happen immediately. Definitely not before result is calculated.
const handleSubmit = async (e) => {
e.preventDefault();
setInputCurrency(e.target.inputCurrency.value);
setInputAmount(e.target.inputAmount.value);
setOutputCurrency(e.target.outputCurrency.value);
//This will ALWAYS use the old previous exchange rate.
let result = inputAmount * exchangeRate;
setOutputAmount(result);
};
What you want is to recalculate outputAmount whenever the exchangeRate or inputAmount changes. You can use another useEffect like:
useEffect(() => {
//Take these out from handleSubmit.
let result = inputAmount * exchangeRate;
setOutputAmount(result);
}, [inputAmount, exchangeRate])

Select disabled when selecting a value - reactjs

When selecting a value from my select I need to disable all the select as such and only show the value that I select, I have tried in several ways but I have not been able to make it work for me.
import React,{useState} from 'react'
function Pruebas() {
const [select, setSelect] = useState();
const elselect = () => {
}
return (
<div>
<form>
<select onChange={ elselect }>
<option value="uno">uno</option>
<option value="uno">dos</option>
</select>
</form>
</div>
)
}
export default Pruebas

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

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