I'm trying to make a function that shows the overall cost of tickets. Adult tickets are $25 each, and minor tickets are $10 each. I have a script that creates the counter, and another script to calculate the sum, but whenever I try to use it to count the overall cost (the second script), it doesn't work, I just get NaN. What am I doing wrong here? Console log doesn't say anything.
function TicketCounter() {
const { useState } = React;
const [counterAdult, setCounter] = useState(0);
const [counterMinor, counterSet] = useState(0);
let adultTickets = [];
let minorTickets = [];
const incrementCounterAdult = () => {
setCounter(counterAdult + 1);
adultTickets.push(counterAdult);
};
const decrementCounterAdult = () => {
if (counterAdult !== 0) {
setCounter(counterAdult - 1);
adultTickets.pop(counterAdult);
}
};
const incrementCounterMinor = () => {
counterSet(counterMinor + 1);
minorTickets.push(counterMinor);
};
const decrementCounterMinor = () => {
if (counterMinor !== 0) {
counterSet(counterMinor - 1);
minorTickets.pop(counterMinor);
}
};
return (
<div className="ticket-options">
<div className="option-adult">
<p>Adult Tickets (16yrs+)</p>
<div className="ticket-amount">
<img
src={'../images/arrowup.png'}
alt="arrow up"
className="arrow-up-adult"
onClick={incrementCounterAdult}
/>
<span className="number-adult">
{counterAdult}
</span>
<img
src={'../images/arrowdown.png'}
alt="arrow down"
className="arrow-down-adult"
onClick={decrementCounterAdult}
/>
</div>
</div>
<div className="option-minor">
<p>Minor Tickets (15yrs-)</p>
<div className="ticket-amount">
<img
src={'../images/arrowup.png'}
alt="arrow up"
className="arrow-up-minor"
onClick={incrementCounterMinor}
/>
<span className="number-minor">
{counterMinor}
</span>
<img
src={'../images/arrowdown.png'}
alt="arrow down"
className="arrow-down-minor"
onClick={decrementCounterMinor}
/>
</div>
</div>
</div>
)
}
ReactDOM.render(<TicketCounter />, document.querySelector(".ticket-counter"));
function TotalCost() {
let overallCost = 0;
let adultCost = () => {
adultTickets.forEach(element => {
adultCost = element * 25;
});
}
let minorCost = () => {
minorTickets.forEach(element => {
minorCost = element * 10;
});
}
overallCost = Number(adultCost) + Number(minorCost);
return(
<div className="option-cost">
<p>Ticket cost: $</p>
{overallCost}
</div>
)
}
ReactDOM.render(, document.querySelector('.cost'));
To get the overall cost you just need to multiply counterAdult and counterMinor and update the value when these counters change:
function TotalCost() {
const [overallCost, setOverallCost] = useState(0);
useEffect(() => {
const newOverall = counterAdult * 25 + counterMinor * 10;
setOverallCost(newOverall);
}, [counterAdult, counterMinor]);
return (
<div className='option-cost'>
<p>Ticket cost: $</p>
{overallCost}
</div>
);
}
Related
How to partially add data to an array and display it? The array is reset each time. Is it possible to save the previous state of an array before adding new data?
const ICONS_PER_PAGE = 5
const IconsList = () => {
const { iconArray, setIconArray } = useContext(IconArray)
const [triggerToLoad, setTriggerToLoad] = useState(true)
const [iconArrayLazy, setIconArrayLazy] = useState([])
const scrollHandler = (e) => {
let scrollGalleryValue = document.querySelector('.list__container').getBoundingClientRect().y + document.querySelector('.list__container').clientHeight - window.innerHeight
if (scrollGalleryValue <= 0 ) {
setTriggerToLoad(!triggerToLoad)
console.log(iconArrayLazy); // [] empty array
}
}
useEffect(() => {
setIconArrayLazy(iconArrayLazy => [...iconArrayLazy, ...iconArray.slice(iconArrayLazy.length, iconArrayLazy.length + ICONS_PER_PAGE)])
}, [triggerToLoad])
useEffect(() => {
setIconArrayLazy(iconArrayLazy => [...iconArrayLazy, ...iconArray.slice(iconArrayLazy.length, iconArrayLazy.length + ICONS_PER_PAGE)])
document.addEventListener('scroll', scrollHandler)
return function () {
document.removeEventListener('scroll', scrollHandler)
}
}, [])
return (
<>
<ul className="list__container">
{iconArrayLazy.map(({id, title, img}) =>
<li className="icon_container" key={id}>
<Link to={`/icon-${id}`}>
<img className="icon_container__image" src={img} alt={title} />
</Link>
</li>
)}
</ul>
</>
)
}
export default IconsList
I am making a mern ecommerce website i just want to see how useEffect works so i console.log in some part of useEffect and loadFilteredResults i saw that --->
initial
entry
skip
entry1
screen shot
but i think it shoud be-->
initial
entry
entry1
skip
why console give this?? i am a begginer, i am a self learner , so please if you need any extra info please comment.
code snippet-->
const loadFilteredResults = (newFilters) => {
console.log("entry")
getFilteredProducts(skip, limit, newFilters).then((data) => {
console.log("entry1")
if (data.error) {
setError(data.error);
} else {
//console.log(data);
setFilteredResults(data.data);
//console.log("size-->");
//console.log(data.size);
setSize(data.size);
setSkip(0);
}
});
};
....
....
useEffect(() => {
init();
console.log("initial");
loadFilteredResults(skip, limit, myFilters.filters);
console.log("skip");
}, []);
//full code of shop.js
import React, { useEffect, useState } from "react";
import Layout from "./Layout";
import Card from "./Card";
import { getCategories, getFilteredProducts } from "./apiCore";
import Checkbox from "./Checkbox";
import RadioBox from "./RadioBox";
import { prices } from "./fixedPrices";
const Shop = () => {
const [myFilters, setMyFilters] = useState({
filters: { category: [], price: [] }
});
const [categories, setCategories] = useState([]);
const [error, setError] = useState(false);
const [limit, setLimit] = useState(3);//prpduct lesss so use 3 but sir used 6
const [skip, setSkip] = useState(0);
const [size, setSize] = useState(0);
const [filteredResults, setFilteredResults] = useState([]);
const init = () => {
getCategories().then((data) => {
if (data.error) {
//console.log("error");
setError(data.error);
} else {
//console.log("set");
//console.log(data);
setCategories(data);
//console.log(data);
}
});
};
const loadFilteredResults = (newFilters) => {
//console.log(newFilters);
console.log("entry")
getFilteredProducts(skip, limit, newFilters).then((data) => {
console.log("entry1")
if (data.error) {
setError(data.error);
} else {
//console.log(data);
setFilteredResults(data.data);
//console.log("size-->");
//console.log(data.size);
setSize(data.size);
setSkip(0);
}
});
};
const loadMore = () => {
console.log("skip"+skip);
console.log("limit"+limit);
let toSkip = skip + limit;
console.log("toSkip"+toSkip);
getFilteredProducts(toSkip, limit, myFilters.filters).then((data) => {
//console.log("filter");
//console.log( myFilters.filters)
if (data.error) {
setError(data.error);
} else {
//console.log(filteredResults);
//console.log(data.data);
setFilteredResults([...filteredResults, ...data.data]);
//console.log("after");
//console.log(...filteredResults);
//console.log(filteredResults);
//console.log(filteredResults);
//console.log([...filteredResults])
//console.log([...filteredResults, ...data.data])
setSize(data.size);
setSkip(toSkip);
}
});
};
const loadMoreButton = () => {
return (
size > 0 &&
size >= limit && (
<button onClick={loadMore} className="btn btn-warning mb-5">
load more
</button>
)
);
};
useEffect(() => {
init();
//console.log(skip);
console.log("initial");
loadFilteredResults(skip, limit, myFilters.filters);
console.log("skip");
}, []);
const handleFilters = (filters, filterBy) => {
//console.log("SHOP", filters, filterBy);
const newFilters = { ...myFilters };
//console.log(newFilters);
newFilters.filters[filterBy] = filters;
//console.log(typeof(filters));
if (filterBy === "price") {
let priceValues = handlePrice(filters);
newFilters.filters[filterBy] = priceValues;
//console.log(priceValues);
}
//console.log(myFilters.filters);
loadFilteredResults(myFilters.filters);
setMyFilters(newFilters);
};
const handlePrice = (value) => {
const data = prices;
let array = [];
//console.log(value);
for (let key in data) {
if (data[key]._id === parseInt(value)) {
array = data[key].array;
}
}
return array;
};
// const x = (filters)=>{
// console.log("filters:"+filters);
// handleFilters(filters, "category")
// }
return (
<Layout
title="Shop Page"
description="search and buy books of your choice"
className="container-fluid"
>
<div className="row">
<div className="col-4">
<h4>Filter by categories</h4>
<ul>
{/* below will be show in list show we wrap it in unorder list */}
<Checkbox
categories={categories}
handleFilters={(filters) =>
handleFilters(filters, "category")
}
/>
</ul>
<h4>Filter by price range</h4>
<div>
<RadioBox
prices={prices}
handleFilters={(filters) => handleFilters(filters, "price")}
/>
</div>
</div>
<div className="col-8">
<h2 className="mb-4">Products</h2>
<div className="row">
{filteredResults.map((product, i) => (
<Card key={i} product={product} />
))}
</div>
<hr />
{loadMoreButton()}
</div>
</div>
</Layout>
);
};
export default Shop;
getFilteredProducts must be a Promise. Please read Using promises
Callbacks added with then() will never be invoked before the
completion of the current run of the JavaScript event loop.
I have 250 objects in the array i get from the axios get request, if i want to render out only 100 of the 250 objects but i want it to always be random can i do something like this
array.[Math.floor(Math.random()*items.length)]?
Here is my code.
export default function App() {
const [data, setData] = useState([])
const [searchCountry, setSearchCountry] = useState('')
const getData = async () => {
const { data } = await axios.get(`https://restcountries.com/v3.1/all`)
setData(data)
}
useEffect(() => {
getData()
}, [])
return (
data && (
<div className="App">
<SomeContext.Provider
value={
<h2>
<input
className="search-country"
placeholder="Search Country..."
onChange={(event) => {
setSearchCountry(event.target.value)
}}
/>
<ul id="countries-ul">
{data
.filter((country) => {
if (searchCountry === '') {
return country
} else if (
country.name.common
.toLowerCase()
.includes(searchCountry.toLowerCase())
)
return country
})
.map((country) => (
<li className="li-card" key={country.name.common}>
<Link to={`/country/${country.name.common}`}>
{country.name.common}
</Link>
</li>
))}
</ul>
</h2>
}
You can iterate your logic Math.floor(Math.random() * data.length) (which will pick a single item randomly in the array) 100 times. Be sure to remove a item in a array when it is picked if you don't wait it to be duplicated.
const getData = async () => {
const { data } = await axios.get(`https://restcountries.com/v3.1/all`)
const LENGTH = 100;
const picked = [];
for (var i = 0; i < LENGTH; i++) {
const index = Math.floor(Math.random() * data.length); // pick a item randomly
const removed = data.splice(index, 1); // remove it for preveting duplication
picked.push(removed[0]);
}
setData(picked);
}
Why is the min state getting updated in the multiples of two instead of just updating by one after every 59 seconds? How do I fix it?
import { useRef, useState } from "react";
export const Timer = () => {
const [second, setSecond] = useState(0);
const [min, setMin] = useState(0);
const watch = useRef(null);
const startTimer = () => {
watch.current = setInterval(() => {
setSecond((value) => {
if (value === 59) {
setSecond(0);
setMin((v) => v + 1);
}
return value + 1;
});
}, 1000);
};
return (
<div>
<h1>
{min}:{second}{" "}
</h1>
<button onClick={startTimer}>Start</button>
<button onClick={() => clearInterval(watch.current)}>Pause</button>
<button
onClick={() => {
setSecond(0);
return clearInterval(watch.current);
}}
>
Reset
</button>
</div>
);
};
This is the component as a whole. I am new to react so please help.
I have those 2 components
FormComponent
const FormComponent = (props) => {
//keep the current step in the component state
const [currentStep, setCurrentStep] = useState(props.step);
const [componentData, setComponentData] = useState(null);
const [componentQuestions, setQuestions] = useState(null);
const [errors, setErrors] = useState();
const updateUserResponse = props.updateUserResponse;
const renderStepsTracker = props.renderStepsTracker;
/**
* Get the component content after mount
*/
useEffect(() => {
if (componentData) {
setQustionsData(componentData);
}
//fire if componentData has changed or error object is changed
}, [errors,componentData]);
/**
* Get the component content after mount
*/
useEffect(() => {
if (currentStep && !componentData) {
let stepData = currentStep.stepdata;
stepData.update = getUpdateTime();
setComponentData(stepData);
}
});
/**
* Get the current time in milliseconds
*/
const getUpdateTime = () => {
var d = new Date();
var n = d.getTime();
return n
}
/**
* Extract the questions from the step object and
* set the state accordingly
*/
const setQustionsData = (stepData) => {
let questions = stepData.questions;
//assign a react component to each question
questions.map((question, key) => {
const component = getComponent(question);
const question_id = question.question_id;
questions[key].component = component;
questions[key].error = (null != errors && 'undefined' !== typeof errors[question_id]) ? errors[question_id] : '';
})
setQuestions(questions);
}
/**
* a callback function to validate the current form
* #param {object} step
*/
const validateForm = (step) => {
const formErrors = props.validateForm(step);
setErrors(formErrors)
}
/**
* Render the form
*/
const renderForm = () => {
return null !== componentQuestions ? (
<>
<div className="row full-height">
<div className="column">
<div className="form-wrapper">
<div className="questions">
{componentQuestions.map(question => {
return (
<>
{question.component}
</>
)
})}
</div>
<NextStepButton
step={currentStep}
nextStepCallback={props.jumpToStep}
validateForm={validateForm}
/>
</div>
</div>
</div>
</>
) : ''
}
/**
* Render the image in case it exists
*/
const renderImage = () => {
return (
<div className="full-height">
<img className="full-height-image" src={componentData.image} alt="" />
</div>
)
}
/**
* Get the question component
*/
const getComponent = (question) => {
switch (question.question_type) {
case "text":
return (
<div className="field-wrap">
<label>
<span className="question-label">{question.question_label}</span>
<span className="question-title">{question.question_title}</span>
<input type="text"
name={question.question_id}
onChange={updateUserResponse}
required={question.required}
/>
{question.error ?
<span className={"error-message " + question.error.reason}>{question.error_message}</span>
: ''}
</label>
</div>
)
}
}
return (
componentData ?
<>
<div className="row full-height expanded">
{componentData.image ?
<div className="column full-height col-collapse image-wrap">
{renderImage()}
</div>
: ''}
<div className="column full-height questions-container">
{renderStepsTracker()}
{renderForm()}
</div>
</div>
</>
: ''
)} enter code hereexport default FormComponent
**
NextStepButton
**
const NextStepButton = (props) => {
const step = props.step
const validateForm = props.validateForm
return props.step.nextButtonText ? (
<span className="next-step-button" onClick={() => {
validateForm( step )
}}>
{step.nextButtonText}
</span>
) : ''
}
export default NextStepButton
When the user clicks on the next step the form is validated and returns an array of errors,
The array of errors is set to the component state and then supposed to display the error
For some reason the error appears only after the user clicks 3 times on the next step button
Why is that ?
EDIT
this is the main component that contains the global form validation
const MainContainer = () => {
//set state variables
const [nextButtonText, setNextButtonText] = useState()
const [previousButtonText, setPreviousButtonText] = useState()
const [backButtonCls, setBackButtonCls] = useState()
const [currentStep, setCurrentStep] = useState()
const [stepsData, setStepsData] = useState()
const [currentStepNum, setCurrentStepNum] = useState(null)
const [classNames, setclassNames] = useState(null)
const [userData, setUserData] = useState({})
//set refrence for callbacks that use this component state
const ref = useRef({
stepsData: stepsData,
currentStep: currentStep
})
/**
* On Initial state
*/
useEffect(() => {
if (!stepsData) {
getStepsData().then((response) => {
initProcess(response)
})
}
})
/**
* Init the process after getting data from the server
*/
const initProcess = (response) => {
const steps = setStepsComponents(response.data.steps)
const initialStep = steps[0];
ref.current.stepsData = steps
setStepsData(steps)
setStep(initialStep, 1)
}
/**
* Form submission callback
* Loop over step questions and check for the appropriate userdata
*/
const validateForm = (submittedStep) => {
let errors = {};
if ('null' !== typeof submittedStep.stepdata.questions) {
const questions = submittedStep.stepdata.questions
questions.map((question, key) => {
const question_name = question.question_id
//if there is an error add it to the questions collection
if (question.required && !userData[question_name]) {
errors[question.question_id] = {
reason : 'required'
}
}
})
}
if (errors) {
return errors
}
return true;
}
/**
* Capture user settings on change
*/
const updateUserResponse = (event) => {
userData[event.target.name] = event.target.value;
setUserData(userData)
}
/**
* Set the appropriate step component
* #param {OBJECT} steps
*/
const setStepsComponents = (steps) => {
steps.map((step, key) => {
switch (steps[key]['component']) {
case "onboarding":
steps[key]['component'] = <Onboarding step={step} setUserData />
break;
case "questions":
steps[key]['component'] = <FormComponent
step={step}
renderStepsTracker={renderStepsTracker}
updateUserResponse={updateUserResponse}
validateForm={validateForm}
/>
break;
}
})
return steps
}
/**
* Callback function that creates the steps tracker on each component
*/
const renderStepsTracker = () => {
return ref.current.stepsData ? (
<>
<div className="stepsTracker">
<div className="row">
<div className="column">
<div className="stepsTrackerWrap">
{ref.current.stepsData.map((step, key) => {
const activeClass = ref.current.currentStep === key ? "active" : ""
return key > 0 ? (
<>
<div className={"stepTracker " + activeClass}>
<div className="stepTrackerImage">
<img src={step.icon} alt="" />
</div>
<label>{step.stepdata.title}</label>
</div>
<div className="stepSpacer"></div>
</>
) : ''
})}
</div>
</div>
</div>
</div>
</>
) : ''
}
/**
* Render the steps component if data is available
*/
const renderSteps = () => {
return stepsData ? (
<StepZilla
steps={stepsData}
onStepChange={onstepChange}
nextButtonText={nextButtonText}
showSteps={false}
backButtonText={previousButtonText}
backButtonCls={backButtonCls}
/>
) : ''
}
/**
* Set the data for the current step
* #param {object} step
*/
const setStep = (step, stepNum) => {
setNextButtonText(step.showNextButton ? step.nextButtonText : true)
setPreviousButtonText(step.previousButtonText)
setBackButtonCls(step.backButtonCls)
setCurrentStep(step)
setCurrentStepNum(stepNum)
ref.current.currentStep = stepNum
//add bottom padding in case the next button appears
if (step.showNextButton) {
setclassNames('padding')
} else {
setclassNames(' ')
}
}
/**
* Perform actions when step is changed
* #param {step} step
*/
const onstepChange = (stepNum) => {
if ('undefined' === typeof stepNum) {
stepNum = '0';
}
setCurrentStepNum(stepNum)
const nextStep = stepsData[stepNum];
setStep(nextStep, stepNum)
}
return (
<>
<div className={'step-progress ' + classNames}>
{renderSteps()}
</div>
</>
)
}
export default MainContainer