Setting or getting a value from a method to an object - javascript

I'm working on a basic Todo-app and currently implementing a progress bar to showcase how many of the added tasks have been completed.
The way I've done it is that I'm using the following method, which is called every time there is a change in my Task object. This is done with React useEffect and is working like a charm when it comes to the output of that console.log.
import {useEffect} from "react";
const Daily = ({ tasks, numberOfTasks, tasksCompleted }) => {
// Updating progress with useEffect every time something happens with tasks
useEffect(() => {
updateProgressHandler();
}, [tasks]);
// Check and update progress based on completed tasks
const updateProgressHandler = () => {
numberOfTasks = tasks.length;
tasksCompleted = tasks.filter(task => task.completed === true).length;
console.log(`Updating progress for ${numberOfTasks} tasks, where ${tasksCompleted} are completed.`);
}
// Saving the completed % to a variable
const completed = numberOfTasks / tasksCompleted * 10;
//console.log(numberOfTasks);
return (
<div className="row">
<div className="col">
<div className="progress progress-daily" style={{ height: "38px" }}>
<div
className="progress-bar progress-bar-striped progress-bar-animated bg-success"
role="progressbar"
aria-valuenow={tasksCompleted}
aria-valuemin="0"
aria-valuemax={numberOfTasks}
style={{width: `${completed}%`}}
>
{completed}%
</div>
</div>
</div>
<div className="col-1">
<button className="btn btn-success">Done</button>
</div>
</div>
)
}
export default Daily;
My issue here however, is the fact that I can't figure out how to pass the numberOfTasks and tasksCompleted to my Daily progress object.
State is set within the App.js as follows:
function App() {
// State
const [numberOfTasks, tasksCompleted] = useState(0);
const [inputText, setInputText] = useState("");
const [tasks, setTasks] = useState([]);
return ( ... );
}
So, while it works perfectly fine in the console.log, the value I'm getting in the progress bar itself is NaN. How come?

Related

Using React Hooks to pass props down from a parent to a child function

I am new to using hooks (and React in general) and I am having trouble figuring out how to pass in props from a parent function to a child function. useSquare is a custom hook and I want to pass square1_state and setSquare_state into it. I then want to pass setSquare_state to the handle_square_click function. I was following the example on this https://designcode.io/react-hooks-handbook-props but for me the square_state and setSquare_state are not recognized. Appreciate any help.
const handle_square_click = (setSquare_state) => {
setSquare_state(player)
setGetplayer(true)
}
const useSquare = (square_state, setSquare_state) => {
// Hook for square state management and rendering
return (
<button className="square" onClick={<handle_square_click setSquare_state={setSquare_state}/> }>
{square_state}
</button>
);
}
const Board = ({player}) => {
let status = "Next Player : " + player
const [square1_state, setSquare1_state] = useState(1);
return (
<div>
<div className="status">{status}</div>
<div className="board-row">
<useSquare
square_state={square1_state}
setSquare_state={setSquare1_state}
/>
Let me see if I can provide some code so you can execute it, however there will be no custom hook as there's no state management happening except in the Board
const handle_square_click = (setSquare_state) => {
setSquare_state(player)
setGetplayer(true)
}
const SquareSubmit = ({square, setSquare}) => {
// Hook for square state management and rendering
// As mentioned, this is not a custom hook at all
return (
<button className="square" onClick={() => handle_square_click(setSquare)}>
{square}
</button>
);
}
const Board = ({player}) => {
let status = "Next Player : " + player
// I removed _state as it doesn't provide any useful information
const [square1, setSquare1] = useState(1);
return (
<div>
<div className="status">{status}</div>
<div className="board-row">
<SquareSubmit
square={square1}
setSquare={setSquare1}
/>

Creating a filter in React JS

I'm trying to build a game deals watcher and I been working on my browse page. I want to add a filter based on price and title in one fetch request. However, I'm not sure how to accomplish that in my case. Here's my Browse.jsx file:
import React, { useState, useEffect } from 'react';
function Browse({currentUser}) {
const [gameDealsList, setGameDealsList] = useState([]);
const [gameTitle, setTitle] = useState('')
// const [minPrice, setMinPrice] = useState('')
// const [maxPrice, setMaxPrice] = useState('')
const defaultURL = `https://www.cheapshark.com/api/1.0/deals?`
useEffect(()=>{
fetch(defaultURL)
.then((r)=>r.json())
.then((gameList)=> setGameDealsList(gameList))
},[])
console.log(gameDealsList)
function handleRedirect(e, dealID){
e.preventDefault();
window.open(`https://www.cheapshark.com/redirect?pageSize=10&dealID=${dealID}`, '_blank');
return null;
}
return(
<div className="container-fluid">
<h1>Browse</h1>
<h4>Filter:</h4>
<input placeholder='Title' value={gameTitle} onChange={(e)=>setTitle(e.target.value)}></input>
<span>Price Range $:</span>
<input
type="range"
className="price-filter"
min="0"
value="50"
max="100"
/>
<br/><br/>
{gameDealsList.map((game) =>
<div className="container" key={game.dealID}>
<div className="row">
<div className="col">
<img src={game.thumb} className="img-thumbnail" alt='thumbnail'/>
</div>
<div className="col">
<strong><p>{game.title}</p></strong>
</div>
<div className="col">
<span><s>${game.normalPrice}</s></span><br/>
<span>${game.salePrice}</span><br/>
<span>{Math.round(game.savings)}% Off</span>
</div>
<div className="col">
<button onClick={(e)=>handleRedirect(e, game.dealID)}>Visit Store</button>
</div>
<div className="col">
{currentUser ? <button>Add to wishlist</button> : null}
</div>
</div><br/>
</div>
)}
</div>
)
}
export default Browse;
Right now, I'm only fetching deals without any filters. However, the API allows me to set filters. For instance, if I want to search deals based on video game title I can just add &title={random title}. Also, I can type in &upperPrice={maximum price} to set up max price of deals. So, I would like to figure out ways to implement these filters in my fetch request without writing multiple fetch requests.
You can try this approach. Only 1 comment in the code to be worried about.
Another thing to worry is to add a Debounce to a fetch function, because without that requests will be sent every time variables in depsArray changed, so if i try to type Nights - 6 requests will be sent while im still typing.
In order to have everything working well:
Create some utils.js file in order to keep some shared helper functions. For debounce in our case.
utils.js
export function debounce(func, wait) {
let timeout;
return function (...args) {
const context = this;
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args);
}, wait);
};
}
Import and wrap the function we want to debounce with useCallback and actual debounce:
import { debounce } from "./utils";
/* ... */
const fetchDeals = useCallback((queryObject) => {
const url = new URL(`https://www.cheapshark.com/api/1.0/deals`);
for (const [key, value] of Object.entries(queryObject)) {
if (value) url.searchParams.append(key, value);
}
console.log(url);
return fetch(url)
.then((r) => r.json())
.then((gameList) => setGameDealsList(gameList));
}, []);
const fetchDealsDebounced = useMemo(() => {
// So API call will not be triggered until 400ms passed since last
// action that may trigger api call
return debounce(fetchDeals, 400);
}, [fetchDeals]);
// Initial call will still be executed but only once, even if we have
// 3 items in depsArray (due to debounce)
useEffect(() => {
// Name object keys according to what API expects
fetchDealsDebounced({ title: gameTitle, upperPrice: maxPrice });
}, [fetchDealsDebounced, gameTitle, maxPrice]);
You should be able to append the query parameters directed by the API to your default query string
fetch(defaultURL + new URLSearchParams({
lowerPrice: minPrice,
upperPrice: maxPrice,
title: gameTitle,
}).then()...
As far as how to control this with only one request you could refactor useEffect like this.
useEffect(() => {
const queryParams = {
lowerPrice: minPrice,
upperPrice: maxPrice,
title: gameTitle,
};
if (minPrice === '') delete queryParams.lowerPrice;
if (maxPrice === '') delete queryParams.upperPrice;
if (gameTitle === '') delete queryParams.title;
fetch(defaultURL + new URLSearchParams(queryParams).then()...
}, [maxPrice, minPrice, gameTitle]);

React update page with input info when pressing a button

react newbie here once again.
I'm trying to update the page with a note when pressing 'send' button.
Honestly I tried for so long that I think my code is pretty much a mess rn.
Main func:
function MainLogged() {
const [sendingScreen, setSendingScreen] = useState(true);
let notes = [];
const [note, setNote] = useState('')
function onNoteChange(event){
const {value} = event.target;
setNote(value);
}
function afterButtonClick(event) {
notes.push(note);
setSendingScreen((prev) => !prev)
}
return (
<div id='main'>
{sendingScreen ? <MainBody noteCallback={onNoteChange} callback={afterButtonClick}/> : <ResetScreen callback={afterButtonClick}/> }
{console.log(notes)}
{
notes.map((singleNote) => {
return (<p id='single-note' className='note'>{singleNote}</p>)
})
}
</div>
)
}
then goes to component:
function MainBody(props) {
const {noteCallback, callback} = props;
return (
<div>
<p id='main-body'>{lorem}</p>
<div className='card text-center' style={{width: '18rem', alignSelf: 'center'}}>
<Input onChange={noteCallback} type='text' placeholder='My note is...' className='card-body information' />
</div>
<SendCardButton callback = {callback}/>
</div>
)
}
and finally (if necessary):
function SendCardButton(props){
const {callback} = props;
return (
<button type="button" className="btn btn-success toBeCentered" onClick={callback}>Send</button>
)
}
Using this code leaves notes array empty always.
Please if you can explain so I can learn and improve, I REALLY need it right now.
Thanks a lot!
React does not rerender your component after you click the button because notes are not stored in state. You need declare notes this way:
const [notes, setNotes] = useState([]);
And then set them on button click, not just push the new one:
setNotes((prevNotes) => ([...prevNotes, note]));
That should be enough:) Feel free to comment if you have further questions

React Child component does not rerender on prop change

I have a parent component
// generate an array of objects, which gets passed the the CalendarDateGrid Component
const calculatedCalendarData = (startDate, endDate) => {
return [
{ calcNormalOrderElectoralBoard: calcNormalOrderElectoralBoard(endDate) },
{ calcNormalDiploma: calcNormalDiploma(startDate) },
];
};
export default function Home() {
const [startDate, setStartDate] = useState(new Date());
const [endDate, setEndDate] = useState(new Date());
const [selectedRegion, setSelectedRegion] = useState(
questions[2].selections[0]
);
const [electoralProcess, setElectoralProcess] = useState(
questions[3].selections[0]
);
const [loading, setLoading] = useState(false);
const [calendarData, setCalendarData] = useState(calculatedCalendarData(startDate, endDate));
return (
<div>
<main className="font-heebo">
<div className="max-w-full mx-auto">
<Calendar
startDate={startDate}
onDateChange={setStartDate}
endDate={endDate}
onEndDateChange={setEndDate}
selectedRegion={selectedRegion}
setSelectedRegion={setSelectedRegion}
electoralProcess={electoralProcess}
setElectoralProcess={setElectoralProcess}
calendarData={calendarData}
setCalendarData={setCalendarData}
setLoading={setLoading}
/>
</div>
<div className="max-w-full mx-auto">
<CalendarDateGrid
data={calendarData}
setData={calculatedCalendarData}
isLoading={loading}
/>
</div>
</main>
</div>
);
}
Inside my Calendar component (which is a form), I am saving the data and passing that data to my parent
<div className="py-14">
<div
onClick={() => setCalendarData}
className="cursor-pointer bg-white w-72 mx-auto text-center text-wahl-red rounded"
>
<span className="inline-block border-wahl-red rotate-180 border w-5 align-middle" />
<span type="button" className="inline-block px-3 py-2 text-lg">
calculate date
</span>
</div>
</div>
After that I am trying to pass that data to my CalendarDateGrid component.
Which basically shall take the data and generate a layout mapping through that data:
export default function CalendarDateGrid({ data, isLoading }) {
const [calendarData, setCalendarData] = useState(data);
useEffect(() => {
setCalendarData(data);
}, [data]);
return (
<div className="w-4/5 2xl:w-2/3 mx-auto pb-20">
{isLoading && (
<ul
role="list"
className="mt-3 grid grid-cols-1 gap-5 sm:gap-12 md:grid-cols-2 xl:grid-cols-3"
>
{calendarData.map((calendarItem) => (
...
The issues is that my child component does not update if the data do get updated. I am trying to update using useEffect but that does not work.
The useEffect hook implement a shallow comparison on the value passed as a dependency. So If you want to pass an Object or an Array as your dependency, you should define what it exactly is.
Object Dependecy
For example, if you have an object as your state, you should define which property to be considered as a dependency:
const [user, setUser] = useState({
name: '',
age: ''
})
// If you want to run the useEffect on every changes of user
useEffect(() => {
console.log("a property in object is changing")
}, [...user])
// If you want to run the useEffect only on change of user's age
useEffect(() => {
console.log("user's age is changing")
}, [user.age])
Array Dependecy
const [data, setData] = useState([])
// If event that cause change in data, affect on its length
useEffect(() => {
console.log("array's length is changing")
}, [data.length])
If the data's length does not change, or data is an array of objects that inner fields may change, you can use one of the following ways:
Comparing the data value with its previous value by using JSON.stringify(data)
const [data, setData] = useState([])
useEffect(() => {
console.log("array is changing")
}, [JSON.stringify(data)])
You can use use-deep-compare-effect package, or write your own custom hook if you what exactly is the properties inside of the nested objects of data array.

ReactJS, How to display a random value after the element has rendered and return it to the state?

Can you please tell me how to output a random value from an array after the element has been rendered? Here is a simple sandbox example
import React, { useState } from "react";
function Main({ coinsValue, addCoins, bonusesStatus }) {
const [prize, setPrize] = useState(false);
let bonusVal = 0;
function getBonus(element) {
const bonusVals = [200, 300, 500];
bonusVal = bonusVals[Math.floor(Math.random() * bonusVals.length)];
setTimeout(() => {
setPrize(!prize);
}, 500);
addCoins(bonusVal);
return bonusVal;
}
return (
<main>
<div className="bonuses">
<div className="bonus_item"></div>
<div className="bonus_item"></div>
<div className="bonus_item"></div>
<div className="bonus_item"></div>
{/* How to show this notification after the button has been pressed, and specify the desired random value bonusVal ? */}
<div className="bonus_item">
<div className="prize">
<p className="prize_text">Congratulations you received</p>
<p className="prize_resalt">+{bonusVal} coins</p>{" "}
</div>
<button className="btn_getBonus" onClick={(e) => getBonus(e)}>
Get bonus
</button>
</div>
</div>
</main>
);
}
export default Main;
The task is as follows - After clicking the button to get a bonus, you need to show a notification (className = "prize") in which the value of this bonus will be displayed (a random value from the array of available ones). I have misunderstanding causes how to forward the {bonusVal} value into this message. I tried to generate block className = "prize" in the getBonus function, but then the question arises how to get the node of this particular bonus_item all the call from the function by the type element.closest(". Bonus_item") or element.parentNode return undefined Can you please tell me how to fix it?
Use the prize state atom you already have to store the last bonus received. When the component's state changes, it re-renders, and you can use that state value in the output.
import React, { useState } from "react";
function Main({ coinsValue, addCoins, bonusesStatus }) {
const [prize, setPrize] = useState(null);
function getBonus(element) {
const bonusVals = [200, 300, 500];
const bonusVal = bonusVals[Math.floor(Math.random() * bonusVals.length)];
setPrize(bonusVal);
setTimeout(() => setPrize(null), 500);
addCoins(bonusVal);
return bonusVal;
}
return (
<main>
<div className="bonuses">
{prize ? (
<div className="bonus_item">
<div className="prize">
<p className="prize_text">Congratulations you received</p>
<p className="prize_resalt">+{prize} coins</p>
</div>
</div>
) : (
<button className="btn_getBonus" onClick={(e) => getBonus(e)}>
Get bonus
</button>
)}
</div>
</main>
);
}
export default Main;

Categories