keep value after page refresh in React - reactjs

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.

Related

React 18: Button click in map function to reflect information only related to one item and not all items

I am new to React and using React 18 in this app. My problem is that if I click one button inside a map function, it reflects information about all the items. I want only that item information to show for which I clicked the button. The isShown === true part in the CountryInfo.js file is what should reflect only one item; currently clicking the show button shows all item information on the UI (I don't want this to happen). How do I do this?
Visually, this is my UI,
If you see the image above, clicking any show button returns all countries information, which should not happen.
Below is my code:
App.js
import { useState, useEffect } from 'react';
import axios from "axios";
import CountryInfo from './components/CountryInfo';
const App = () => {
const [countries, setCountries] = useState([]);
const [searchCountry, setSearchCountry] = useState("");
const handleCountryChange = event => {
setSearchCountry(event.target.value);
}
const getAllCountriesData = () => {
axios.get("https://restcountries.com/v3.1/all")
.then(response => {
setCountries(response.data);
})
}
useEffect(() => {
getAllCountriesData();
}, []);
return (
<>
<h2>Data for countries</h2>
find countries:
<input value={searchCountry} onChange={handleCountryChange} />
{searchCountry.length > 0 && <CountryInfo countries={countries} searchCountry={searchCountry} />}
</>
)
}
export default App;
CountryInfo.js
import React from "react";
import { useState } from "react";
const CountryInfo = ({ countries, searchCountry }) => {
const [isShown, setIsShown] = useState(false);
let filteredList = countries.filter(country =>
country.name.common.toLowerCase().includes(searchCountry.toLowerCase()));
const handleClick = () => {
setIsShown(true);
}
if (filteredList.length > 10) {
return <div>Too many matches, specify another filter</div>
}
else {
return filteredList.map(country => {
return (
<>
<div key={country.name.common}>
{!isShown &&
<div>
{country.name.common}
<button type="submit" onClick={handleClick}>show</button>
</div>
}
{isShown &&
<div key={country.name.common}>
<h2>{country.name.common}</h2>
<p>
Capital: {country.capital}
{'\n'}
Area: {country.area}
</p>
Languages:
<ul>
{
Object.values(country.languages)
.map((language, index) => <li key={index}>{language}</li>)
}
</ul>
<img src={country.flags.png} alt={`${country.name.common} flag`} height={150} />
</div>
}
</div>
</>
)
})
}
}
export default CountryInfo;

Persisting state change with localStorage for each input

So I have two radio button images, one checked and one not. I am trying to persist the change of state to view the corresponding image on button click for each of the inputs.
Please help.
Here's my code:
import React, { useState, useEffect } from 'react';
const Option = (props) => {
const img1 = <img alt='' src='/radio-active.png' className='radio__img' />;
const img2 = <img alt='' src='/radio-inactive.png' className='radio__img' />;
const [state, setState] = useState(false);
const handleStateChange = () => {
state === true ? setState(false) : setState(true);
};
useEffect(() => {
setState(JSON.parse(window.localStorage.getItem('state')));
}, []);
useEffect(() => {
window.localStorage.setItem('state', state);
}, [state]);
return (
<div className='option'>
<div className='radio'>
<button className='radio__button' onClick={handleStateChange}>
{state ? img1 : img2}
</button>
<p className='option__text radio__text'>{props.optionText}</p>
</div>
<button
className='button button--link'
onClick={(e) => {
props.handleDeleteOption(props.optionText);
}}
>
remove
</button>
</div>
);
};
export default Option;
All of your Option components are saving the state using the same key ("state"). You'll want each Option to have its own saved state. For each Option, add a new "optionName" property that is the key you want to use when saving the option's value to local storage.
// Change these:
window.localStorage.setItem('state', state);
setState(JSON.parse(window.localStorage.getItem('state')));
// To these:
window.localStorage.setItem(props.optionName, state);
setState(JSON.parse(window.localStorage.getItem(props.optionName)));

Option values inside a select with React

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

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 to update state based on checkbox status of being checked

I have a bunch of checkboxes with the following markup
<input type='checkbox' data-id='123' data-label='abc' ref={checkboxRef} />
<input type='checkbox' data-id='456' data-label='xyz' ref={checkboxRef} />
And a state which is initially set as an empty array
const [contacts, setContacts] = useState([])
What I want to do is update the state with an object of a checkbox's data based on whether it's checked or not. If checked, it's data is to be added to the state and if unchecked, remove it.
Expected state after a checkbox is checked
[
{ id: '123', label: 'abc' }
]
I've used a ref for now to the input and getting the data of it but can't figure out how to go about updating the state.
const handleToggle = () => {
setIsChecked(prevState => !isChecked)
const id = checkboxRef.current.getAttribute('data-id')
const label = checkboxRef.current.getAttribute('data-label')
}
I have solved it. Check it here.
https://codesandbox.io/s/affectionate-fermi-f6bct
Full code hereby is
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [contacts, setContacts] = useState([]);
const ref1 = React.createRef();
const ref2 = React.createRef();
const handleClick = (ref) => {
const id = ref.current.getAttribute("data-id");
const label = ref.current.getAttribute("data-label");
if (contacts.map((e) => e.id).includes(id)) {
setContacts(contacts.filter((e) => e.id !== id));
} else {
setContacts([...contacts, { id, label }]);
}
console.log(contacts);
};
return (
<div className="App">
<input
type="checkbox"
data-id="123"
data-label="abc"
ref={ref1}
onClick={() => {
console.log("hi");
handleClick(ref1);
}}
/>
<input
type="checkbox"
data-id="456"
data-label="xyz"
ref={ref2}
onClick={() => handleClick(ref2)}
/>
</div>
);
}

Resources