How can I return text with useState? - javascript

I am waiting for the data (when the user submit) i fetch the data then return the Temperature with useState() but i wanted to return a header with it like Temperature:12°C.
Something else is that i wanna round the temperature to 2 decimal places but i don't know how to do so
here is my code:
import axios from 'axios';
import React from 'react'
import {useState, useEffect} from 'react'
import './style.css'
import rain from './use1.png'
function App() {
const [name,setName] = useState('Cairo')
const [res, setRes] = useState(null)
const [pic, setPic] = useState(null)
const [temp, setTemp] = useState('')
const getApi = e => {
axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${name}&appid=my_key`)
.then(response=> {
console.log(response.data);
setRes(response.data.name)
setTemp(response.data.main.feels_like-273.15+'°C');
Math.round(temp)
setPic(`https://openweathermap.org/img/w/${response.data.weather[0].icon}.png`)
})}
const handleChange = e => {
setName(e.target.value);
};
return (
<div>
<img className="wallpaper" src={rain}></img>
<div className="content">
<input placeholder="Search.." className="input" onChange={handleChange}></input>
</i>
</div>
<div className="content2">
<h1 className="name">{res}</h1>
<img src={pic}></img>
<h1 className="temp">{temp}</h1>
</div>
</div>
);
}
export default App;

Add a useEffect hook so your component re-renders after your temp state changes.
useEffect(() => {}, [temp]);
In order to round to two decimal places...well, usually I don't like telling people this but that's an extremely easy thing to find out from just using Google or another search engine.
JavaScript math, round to two decimal places

You could do it like:
`Temperature ${(response.data.main.feels_like-273.15).toFixed(2)} °C`

You can do like this
const [temp, setTemp] = useState('')
setTemp(response.data.main.feels_like-273.15+'°C');
return (
<div>
<h1 className="temp"> Temperature {temp?.toFix(2)}</h1>
</div>
)

Could you please provide the full component's code, or at least where is response coming from? Anyway, If the response is being passed from a parent component, then I see no reason to use useState at all.
But if you are fetching the data in the same component, then you need to fetch the data asynchronously:
function component(){
const [temp, setTemp] = useState('Loading temperature...')
useEffect(() => {
fetchTemp().then((response) => {
setTemp(response.data.main.feels_like-273.15+'°C');
})
}, [])
return (
<div>
<h1 className="temp">{temp}</h1>
</div>
)
}
About rounding the number, you can use toFixed(decimals) method.
Example:
let number = 900.1234
console.log(number.toFixed(2))

Related

React, problem with my Todo list app, my search engine only finds notes on the page I'm on

Hello I am making an application to practice React, my notes app has a pagination which works perfectly, the problem is in the search engine, which only looks for notes from the page I'm on, for example, if I'm on page 2 and I look for a note on page 2, it shows it, however if the note is on a different page, it doesn't show it, it doesn't find it.
I know where the problem is but I'm not sure how to solve it, I'm a bit new to React and I was asking for your help.
I was able to do my pagination with the package react-paginate here is the documentation https://www.npmjs.com/package/react-paginate
My code:
Component App.js
import { useState, useEffect } from "react";
import { nanoid } from 'nanoid';
import './App.css';
import Search from "./components/Search";
import Header from "./components/Header";
import Pagination from "./components/Pagination";
const App = () => {
const [notes, setNotes] = useState([]);
const [searchText, setSearchText] = useState('');
const [darkMode, setDarkMode] = useState(false);
const [showNote, setShowNote] = useState(true); //eslint-disable-line
useEffect(() => {
const saveNotes = JSON.parse(localStorage.getItem('notes-data'));
if (saveNotes){
setNotes(saveNotes);
}
}, []);
useEffect(() => {
localStorage.setItem('notes-data', JSON.stringify(notes))
},[notes])
const addNote = (inputText, text) => {
const date = new Date();
const newNote = {
id: nanoid(),
title: inputText,
text: text,
date: date.toLocaleString()
}
const newNotes = [newNote, ...notes];
setNotes(newNotes)
}
const deleteNote = (id) => {
var response = window.confirm("Are you sure?");
if (response){
const notesUpdated = notes.filter((note) => note.id !== id)
setNotes(notesUpdated);
}
}
return (
<div className={darkMode ? 'dark-mode' : ''}>
<div className="container">
<Header
handleToggleTheme={setDarkMode}
/>
<Search
handleSearchNote={setSearchText}
setShowNote={setShowNote}
/>
<Pagination
data={notes}
handleAddNote={addNote}
handleDeleteNote={deleteNote}
searchText={searchText}
/>
</div>
</div>
)
}
export default App;
Component Pagination.js
import React, { useEffect, useState } from 'react'
import ReactPaginate from 'react-paginate';
import '../styles/Pagination.css';
import NoteList from './NoteList';
import { MdSkipPrevious, MdSkipNext } from 'react-icons/md';
const Pagination = (props) => {
const { data, searchText, handleAddNote, handleDeleteNote } = props;
// We start with an empty list of items.
const [currentItems, setCurrentItems] = useState([]);
const [pageCount, setPageCount] = useState(0);
// Here we use item offsets; we could also use page offsets
// following the API or data you're working with.
const [itemOffset, setItemOffset] = useState(0);
const itemsPerPage = 9;
useEffect(() => {
// Fetch items from another resources.
const endOffset = itemOffset + itemsPerPage;
console.log(`Loading items from ${itemOffset} to ${endOffset}`);
setCurrentItems(data.slice(itemOffset, endOffset));
setPageCount(Math.ceil(data.length / itemsPerPage));
}, [itemOffset, itemsPerPage, data]);
// Invoke when user click to request another page.
const handlePageClick = (event) => {
const newOffset = (event.selected * itemsPerPage) % data.length;
console.log(
`User requested page number ${event.selected}, which is offset ${newOffset}`
);
setItemOffset(newOffset);
};
return (
<>
<NoteList
notes={currentItems.filter((noteText) =>
noteText.title.toLowerCase().includes(searchText)
)}
handleAddNote={handleAddNote}
handleDeleteNote={handleDeleteNote}
/>
<div className="pagination-wrapper">
<ReactPaginate
breakLabel="..."
nextLabel={<MdSkipNext
className='icons'
/>}
onPageChange={handlePageClick}
pageRangeDisplayed={3}
pageCount={pageCount}
previousLabel={<MdSkipPrevious
className='icons'
/>}
renderOnZeroPageCount={null}
containerClassName="pagination"
pageLinkClassName="page-num"
previousLinkClassName="page-num"
nextLinkClassName="page-num"
activeLinkClassName="activee boxx"
/>
</div>
</>
);
}
export default Pagination;
Component NoteList.js
import React from 'react'
import Note from './Note'
import '../styles/NoteList.css'
import AddNote from './AddNote'
const NoteList = ({ notes, handleAddNote, handleDeleteNote }) => {
return (
<>
<div className="add-notes-wrapper">
<AddNote
handleAddNote={handleAddNote}
/>
</div>
<div className='notes-list'>
{notes.map((note =>
<Note
key={note.id}
id={note.id}
title={note.title}
text={note.text}
date={note.date}
handleDeleteNote={handleDeleteNote}
/>
))}
</div>
</>
)
}
export default NoteList;
Component Search.js
//import React, { useState } from 'react'
import {MdSearch, MdAdd} from 'react-icons/md'
import '../styles/Search.css'
const Search = ({ handleSearchNote, setShowNote }) => {
const handleShowAddNote = () => {
if (setShowNote){
let addNote = document.querySelector('.new');
addNote.classList.add('wobble-horizontal-top')
addNote.style.display='flex';
document.querySelector('.notes-list').style.display='none';
document.querySelector('.pagination').style.display='none';
}
}
return (
<div className='search'>
<div className="input-wrapper">
<MdSearch
className='icon search-icon'
/>
<input
type="text"
placeholder='What note are you looking for?'
onChange={(event) => handleSearchNote(event.target.value) }
/>
</div>
<div className="btn-wrapper-search">
<button
className='btn-addNote'
onClick={handleShowAddNote}>
Nueva Nota
</button>
<MdAdd
className='icon add-icon'
/>
</div>
</div>
)
}
export default Search
The problem is in the component Pagination.js because I'm filtering the notes on each page with the currentItems variable, if I did it with the data variable it would work, but then it would show all the notes, and I don't want that, I currently want to show 9 notes per page.
greetings and thanks in advance.
Edit:
#Newbie I'm doing what you said, but I don't know if you mean this, in my Pagination.js component I did:
useEffect(() => {
const filterNotes=data.filter((noteText) =>
noteText.title.toLowerCase().includes(searchText)
)
setItemOffset(0);
}, [data, searchText])
It doesn't work, do I have to pass a prop to my components additionally?
greetings.
As I suggested to you, search all the notes with searchText in your App.js and pass the results into the Pagination component and it will solve your problem.
Codesandbox: https://codesandbox.io/s/youthful-thompson-xugs0c
Edit
All changes are as per what we talked about in the email.
Codesandbox: https://codesandbox.io/s/green-fast-3k76wx
Search and pagination do not play well together, one of the common solutions is to jump to page 1 each time the filter term changes.
So use an useEffect on searchText to filter data and reset itemOffset to 0, then redo pagination as if the data changed.
The user will jump to page 1 at each keystroke of the search, then he can navigate pages (if there are more than one). This will lead to a less confusing UX.

How to conditionally fetch within useEffect

I was able to fetch a new quote every time I re-render, but now I want to add a functionality where I have the option to replay the same quote and not fetch a new one if I press the replay quote button, however I still want to call the useEffect. The solution to this sounds super straight forward, however, I do not know how to approach it with useEffect in play.
Also, when using react is it better to use ref hooks, instead of document.getElementByID ?
Code SandBox Link
import "./styles.css";
import { useEffect, useState } from "react";
async function newQuote() {
const response = await fetch("https://api.quotable.io/random");
const data = await response.json();
return data.content;
}
async function genQuote() {
document.getElementById("quoteDisplay").innerText = await newQuote();
}
export default function App() {
const [quote, fecthNewQuote] = useState(0);
useEffect(() => {
genQuote();
document.getElementById(
"newQuoteCounter"
).innerText = `New Quote Counter: ${quote}`;
document.getElementById("quoteReplayedCounter").innertext = ``;
}, [quote]);
console.log();
return (
<div className="App">
<div id="quoteDisplay"></div>
<div id="newQuoteCounter">New Quote Counter: 0</div>
<div id="quoteReplayedCounter"> Quote Replayed Counter: 0 </div>
<button id="newQuoteButton" onClick={() => fecthNewQuote((c) => c + 1)}>
Fetch New Quote
</button>
<button id="keepSameQuote">Keep Same Quote</button>
</div>
);
}
You can refactor like this with async function inside useEffect and you do not need to use these kind of document.getElementById("quoteDisplay").innerText js solutions. You can do it with react states.
Here is the working example https://codesandbox.io/embed/distracted-shamir-sfinbd?fontsize=14&hidenavigation=1&theme=dark
import "./styles.css";
import { useEffect, useState } from "react";
export default function App() {
const [quote, fecthNewQuote] = useState(0);
const [display, setDisplay] = useState("");
useEffect(() => {
async function newQuote() {
const response = await fetch("https://api.quotable.io/random");
const data = await response.json();
setDisplay(data.content);
}
newQuote();
}, [quote]);
console.log();
return (
<div className="App">
<div id="quoteDisplay">{display}</div>
<div id="newQuoteCounter">New Quote Counter: {quote}</div>
<div id="quoteReplayedCounter"> Quote Replayed Counter: 0 </div>
<button id="newQuoteButton" onClick={() => fecthNewQuote(quote + 1)}>
Fetch New Quote
</button>
<button id="keepSameQuote">Keep Same Quote</button>
</div>
);
}

How to solve setState is always one step behind on React JS [duplicate]

This question already has answers here:
Update variable and use it immediately React
(2 answers)
Closed 11 months ago.
I am a beginner. I am learning react js. I am having an problem. setState is always one step behind.
Here is a sample:
Here, when I typed i then the console is showing nothing. Next, when I typed the m it shows i and as it is one step behind.
I have created two functions named handleChange and handleKeyword. The functions are behaving the same. I searched on the internet and got useEffect() suggestion to solve the problem but that has not solved my problem or I can't properly implement it.
Here is my codes:
Home.jsx
import React, { useState, useEffect } from 'react';
import Search from '../../components/searchBar/Search';
import './home.scss';
const Home = () => {
const [search, setSearch] = useState('');
const [keyword, setKeyword] = useState('');
const handleChange = event => {
setSearch(event.target.value);
console.log('Search: ', search);
};
const handleKeyword = () => {
setKeyword(search);
console.log('Keyword:', keyword);
};
return (
<div className="container pb-5">
<Search
handleChange={handleChange}
handleKeyword={handleKeyword}
keyword={keyword}
/>
</div>
);
};
export default Home;
Search.jsx
import React from 'react';
import './search.scss'
const Search = props => {
return (
<div className="d-flex input-group justify-content-center">
<input
type="text"
className="form-control searchBox"
placeholder="Search for copyright free images & videos..."
value={props.value}
onChange={event => props.handleChange(event)}
/>
<button className="btn btn-primary" onClick={() => props.handleKeyword()}>
Search
</button>
</div>
);
};
export default Search;
How can I solve the problem?
In Home.jsx, you can move the console statments inside useEffect with states search and keyword as dependencies to get the updated values. This issue is because react is declarative in nature so it decides when to setState runs. It can even be batched together for performance optimisations. So useEffect can be used in such cases to listen to change in states.
import React, { useState, useEffect } from 'react';
import Search from '../../components/searchBar/Search';
import './home.scss';
const Home = () => {
const [search, setSearch] = useState('');
const [keyword, setKeyword] = useState('');
useEffect(() => {
console.log('Search: ', search);
console.log('Keyword:', keyword);
}, [search, keyword])
const handleChange = event => {
setSearch(event.target.value);
};
const handleKeyword = () => {
setKeyword(search);
};
return (
<div className="container pb-5">
<Search
handleChange={handleChange}
handleKeyword={handleKeyword}
keyword={keyword}
/>
</div>
);
};
export default Home;
The problem is setState just promise you that value will be updated It does not affect your code, just move console.logs outside handleClicks
So, when you set a new state and you will see a new value only after rerender component.
const handleKeyword = () => {
setKeyword(search);
console.log("Keyword:", keyword);
};
console.log("Keyword:2", keyword);
console.log("Keyword:", keyword); will be called in the first render with the old value
console.log("Keyword:2", keyword); will be called in the second render with a new value.
setState is async so changes to the state are not applied immediately.
see here https://reactjs.org/docs/react-component.html#setstate

Is there a way to populate option html tag with an array in react?

I'm trying to make an option in jsx to be populated by the values in an array (currencyOptions). I used this approach but it is not working as the options still remain to be blank. The array is passed down to the component as a prop. I set the array using usestate and the data is gotten from an API. Please help.
import React from "react";
function Currencyrow(props) {
const {
currencyOptions,
selectedCurrency,
onChangeCurrency,
amount,
onChangeAmount,
} = props;
// console.log(currencyOptions);
return (
<>
<input
type="number"
className="input"
value={amount}
onChange={onChangeAmount}
></input>
<select value={selectedCurrency} onChange={onChangeCurrency}>
{currencyOptions.map((option) => {
<option key={option} value={option}>
{option}
</option>;
})}
</select>
</>
);
}
export default Currencyrow;
That is the component where I pass down currencyOptions as a prop from my main app.js
import "./App.css";
import React from "react";
import Currencyrow from "./Components/Currencyrow";
import { useEffect, useState } from "react";
const BASE_URL =
"http://api.exchangeratesapi.io/v1/latest?access_key=1fe1e64c5a8434974e17b04a023e9348";
function App() {
const [currencyOptions, setCurrencyOptions] = useState([]);
const [fromCurrency, setFromCurrency] = useState();
const [toCurrency, setToCurrency] = useState();
const [exchangeRate, setExchangeRate] = useState();
const [amount, setAmount] = useState(1);
const [amountInFromCurrency, setAmountInFromCurrency] = useState(true);
let toAmount, fromAmount;
if (amountInFromCurrency) {
fromAmount = amount;
toAmount = fromAmount * exchangeRate;
} else {
toAmount = amount;
fromAmount = amount / exchangeRate;
}
useEffect(() => {
fetch(BASE_URL)
.then((res) => res.json())
.then((data) => {
const firstCurrency = Object.keys(data.rates)[0];
setCurrencyOptions([Object.keys(data.rates)]);
setFromCurrency(data.base);
// console.log(currencyOptions);
setToCurrency(firstCurrency);
setExchangeRate(data.rates[firstCurrency]);
});
}, []);
function handleFromAmountChange() {
// setAmount(e.target.value);
setAmountInFromCurrency(true);
}
function handleToAmountChange() {
// setAmount(e.target.value);
setAmountInFromCurrency(false);
}
return (
<>
<h1>Convert</h1>
<Currencyrow
currencyOptions={currencyOptions}
selectedCurrency={fromCurrency}
onChangeCurrency={(e) => {
setFromCurrency(e.target.value);
}}
amount={fromAmount}
onChangeAmount={handleFromAmountChange}
/>
<div className="equals">=</div>
<Currencyrow
currencyOptions={currencyOptions}
selectedCurrency={toCurrency}
onChangeCurrency={(e) => {
setToCurrency(e.target.value);
}}
amount={toAmount}
onChangeAmount={handleToAmountChange}
/>
</>
);
}
export default App;
When I run the app the option element is still blank.
Is there a way to populate option html tag with an array in react?
This is possible. Just as a tip, you can always try hardcoding currencyOptions in your CurrencyRow and test it out.
Looking through your code, firstly it may be not what you want wrapping Object.keys() in an additional array in setCurrencyOptions([Object.keys(data.rates)]). Object.keys() already returns an array. You probably are not accessing the actual options in your currencyOptions.map((option) => ..). Try setting the keys array directly like this setCurrencyOptions(Object.keys(data.rates)).
Secondly, you should return the desired value inside map by either using it as an arrow function or adding the return keyword in front of the option JSX.
Other than that, is there any error displayed in the browser console? And it would certainly help you to log the mapped option to the console and see what you are actually getting from it.
Your map function should return a value.
<select>{numbers.map((m)=>{return(<option>{m}</option>)})}</select>

Validation failed for parameter 'id'. Invalid number?

I'm connecting my Node backend(connected the database using Microsoft SQL Server) to React front-end. Note: The backend works great and tested with postman for all endpoints.
While connecting to it, throws me this error : "Validation failed for parameter 'id'. Invalid number."
App.js
import React, { useState, useEffect } from "react";
import axios from "axios";
import './App.css';
import Header from "./components/Header";
import Search from "./components/Search";
import Events from "./components/Events";
const App = () => {
const [customers, setCustomers] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [searchQuery, setSearchQuery] = useState("");
useEffect(() => {
const fetchEvents = async () => {
const res = await axios(
`http://localhost:8080/api/event/${setSearchQuery}`
);
console.log(res.data);
setCustomers(res.data);
setIsLoading(false);
};
fetchEvents();
}, [searchQuery]);
return (
<div className="container">
<Header />
<Search getQuery={(sq) => setSearchQuery(sq)} />
<Events isLoading={isLoading} customers={customers} />
</div>
);
};
export default App;
Event.js
import React from "react";
const Event = ({ customer }) => {
return (
<div className="card">
<div className="card-inner">
<div className="card-front">
<img src={customer.img} alt="" />
</div>
<div className="card-back">
<h1>{customer.id}</h1>
<ul>
<li>
<strong>Customer ID:</strong> {customer.id}
</li>
<li>
<strong>Customer Number:</strong> {customer.Cus_no}
</li>
<li>
<strong>Range Name:</strong> {customer.RangeName}
</li>
<li>
<strong>Color:</strong> {customer.Color}
</li>
<li>
<strong>Blade Text:</strong> {customer.BladeTExt}
</li>
<li>
<strong>Special Notes:</strong> {customer.SpecialNotes}
</li>
<li>
<strong>Box Quantity:</strong> {customer.box_qty}
</li>
</ul>
</div>
</div>
</div>
);
};
export default Event;
Events.js
import React from "react";
import Event from "./Event";
import Spinner from "./Spinner.js";
const Events = ({ customers, isLoading }) => {
return isLoading ? (
<Spinner />
) : (
<section className="cards">
{customers.map((customer) => (
<Event key={customer.id} customer={customer}></Event> //key={customer.id}
))}
</section>
);
};
export default Events;
Search.js
import React, { useState } from "react";
const Search = ({ getQuery }) => {
const [number, setNumber] = useState(0);
const onChange = (sq) => {
setNumber(sq);
getQuery(sq);
};
return (
<section className="search">
<form>
<input
type="Number"
className="form-control"
placeholder="Search Customer Numbers"
value={number}
onChange={(e) => onChange(e.target.value)}
autoFocus
/>
</form>
</section>
);
};
export default Search;
For better understanding here is a Sample SQL Table output with the data types I created : https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=c925fb80326b4c3ed2117beddb73b451
Now comes the great part, whenever I use the endpoint "http://localhost:8080/api/events" the whole customer data set displays correctly as it supposed to. But for the endpoint "http://localhost:8080/api/event/${setSearchQuery}" it gives me the error.
I think your problem is here:
const [searchQuery, setSearchQuery] = useState("");
your backend API for fetching event needs an id for it and when every your component mounts to the dom, your useEffect kicks in and calls the API! but meanwhile your state of searchQuery is an empty string!
NOTE:
const res = await axios(
`http://localhost:8080/api/event/${setSearchQuery}`
);
you are using setSearchQuery in your endpoint string instead of searchQuery!
that being said, I think still you will get an error even after changing that bug in your URL! because you still send an empty string as id for your backend API!
So the best solution would be to provide a default id for your searchQuery in your useState instead of passing an empty string, or if you cannot provide a default value, make sure that your api does not broke when you providing empty string to it!

Categories

Resources