Can I append div on button click in react? - arrays

I am trying to make a simple hotel reservation web app in react. Here is what I want to achieve. A user selects room, adults, and children in number and reserve he's/her own hotels. The following screenshot depicts exactly what I want to do.
As you see from the above screenshot, when I click children's Increment + button, I wanted to append a div having children and their age. In the above screenshot, as I Incremented children value 2 times Two divs with children and their age has been appended.
I already design the component as follows. I want the logic here to apply.
Here is my Component code, I use tailwindcss for styling
import React, { useState } from "react";
import rateIcon from "../../../assets/rate-cons.PNG";
import {
CheckIcon,
InformationCircleIcon,
WifiIcon,
ParkingIcon,
HeartIcon,
SwitchVerticalIcon,
AdjustmentsIcon,
MapIcon,
MinusIcon,
PlusIcon,
} from "#heroicons/react/outline";
import mapImage from "../../../assets/map.PNG";
import { LocationMarkerIcon } from "#heroicons/react/solid";
import DatePickers from "../DatePicker/DatePicker";
import {
HotelOutlined,
PeopleAltOutlined,
RoomOutlined,
RoomService,
} from "#material-ui/icons";
import {
ChildFriendly,
FamilyRestroomOutlined,
FamilyRestroomRounded,
} from "#mui/icons-material";
const Hero = () => {
const [rooms, setRooms] = useState(0);
const [show, setShow] = useState(false);
const showGuestInfo = () => setShow(!show);
const addRooms = () => {
setRooms(rooms + 1);
};
const removeRooms = () => {
if (rooms > 1) {
setRooms(rooms - 1);
} else {
setRooms(rooms);
}
};
return (
<div className="p-3 flex w-full flex flex-col relative">
<div className="flex flex-col border-b border-gray">
<div className="p-2 flex justify-between items-center ">
<div className="flex text-sm items-center space-x-2">
<HotelOutlined />
<p>Rooms</p>
</div>
<div className="flex space-x-4 border p-2">
<p>
<MinusIcon
// onClick={removeRooms}
className="bg-gray-light h-6 cursor-pointer"
/>
</p>
<p>{rooms}</p>
<PlusIcon
// onClick={addRooms}
className=" bg-gray-light h-6 cursor-pointer"
/>
</div>
</div>
<div className="p-2 flex justify-between items-center">
<div className="flex text-sm items-center space-x-2">
<PeopleAltOutlined />
<p>Adults</p>
</div>
<div className="flex space-x-4 border p-2">
<p>
<MinusIcon
// onClick={removeRooms}
className="h-6 bg-gray-light cursor-pointer"
/>
</p>
<p>{rooms}</p>
<PlusIcon
// onClick={addRooms}
className="h-6 bg-gray-light cursor-pointer"
/>
</div>
</div>
<div className="p-2 flex justify-between items-center">
<div className="flex text-sm items-center space-x-2">
<FamilyRestroomOutlined />
<p>Childrens</p>
</div>
<div className="flex space-x-4 border p-2">
<p>
<MinusIcon
onClick={removeRooms}
className="h-6 bg-gray-light cursor-pointer"
/>
</p>
<p>{rooms}</p>
<PlusIcon
onClick={addRooms}
className="h-6 bg-gray-light cursor-pointer"
/>
</div>
</div>
</div>
<div className="border-t p-2 border-gray ">
<button className="p-2 border-t border-gray bg-black w-full text-white">
Update
</button>
</div>
</div>
);
};
How can I build this logic?

The correct way to do this in React is to set the state of your component (or its parent). Then when you render the component, you use the state to decide what to render. So you don't "append" a div as much as you decide that you need a div at a certain spot due to the state.
In this case, you might have a list of children's ages as state. Clicking on the + button will add an element to this list, probably defaulting to 0. Then for each child's age, you render a <div> and a <select> with that age. If the age is 0, then you have a message telling the user to select an age. When the user selects an age, you update the list at the correct position with that age.
Note that this can be implemented as a separate component to simplify the code in the top-level view.
The key thing here is that you never manipulate the DOM directly like you would with pure JavaScript or JQuery. Instead, you keep some data and use it to decide what to render.

Since you don't have example, here is a repro on Stackblitz on how you can do it, and here is the code :
import React, { Component } from 'react';
import { render } from 'react-dom';
const App = () => {
const [children, setChildren] = React.useState(0);
return (
<div>
Children :{' '}
<input
type="number"
value={children}
onChange={(e) => setChildren(Number(e.target.value))}
/>
{Array.from(Array(Number(children)).keys()).map((child, index) => (
<div key={index}>
child of <input type="number" /> years old
</div>
))}
</div>
);
};
render(<App />, document.getElementById('root'));
The state children store the number of children. We then just render as many div as this number by creating an array and mapping over it with this particular line :
Array.from(Array(Number(children)).keys())

Related

How change LabelFormat value at y axis in syncfusion charts?

I make a chart with React and syncfusion but I dont'know what is the best way to change the y axis values. I solve so I search the id and mannualy write the DOM in useEffect.
But what is the best way I change y axis values in syncfusion charts?
import React, { useEffect } from "react";
import { skillsIcon } from "../data/dummy";
import { GoPrimitiveDot } from "react-icons/go";
import { Stacked, Pie, Button, SparkLine,BackendStacked } from "../components";
import {
earningData,
SparklineAreaData,
ecomPieChartData,
} from "../data/dummy";
import { useStateContext } from "../contexts/ContextProvider";
const Skills = () => {
const {
screenSize,
setScreenSize,
} = useStateContext();
useEffect(() => {
document.getElementById("charts1_AxisLabel_4").innerHTML = "Medior"
document.getElementById("charts1_AxisLabel_3").innerHTML = ""
document.getElementById("charts1_AxisLabel_2").innerHTML = "Junior"
document.getElementById("charts1_AxisLabel_1").innerHTML = ""
document.getElementById("charts1_AxisLabel_0").innerHTML = "Entry"
}, [screenSize,setScreenSize]);
return (
<div>
<h1 className="text-center text-4xl my-10 font-bold">Skills</h1>
<div className="flex m-3 flex-wrap justify-center gap-1 items-center">
{skillsIcon.map((skill) => (
<div
key={skill.title}
className="bg-white dark:text-gray-200 dark:bg-secondary-dark-bg md:w-40
p-4 pt-9 rounded-2xl"
>
<button
type="button"
style={{ color: skill.iconColor, backgroundColor: skill.iconBg }}
className="text-2xl opacity-0.9 rounded-full p-4 hover:drop-shadow-xl"
>
{skill.icon}
</button>
<p className="mt-3">
<span className="text-lg font-semibold">{skill.title}</span>
<span className={`text-sm text-${skill.pcColor} ml-2`}>
{skill.percentage}
</span>
</p>
</div>
))}
</div>
<div className="flex gap-10 flex-wrap justify-center">
<div
className="bg-white dark:text-gray-200
dark:bg-secondary-dark-bg m-3 p-4 rounded-2xl md:w-780"
>
<div className="">
<h2 className="font-semibold text-center text-xl ">
Skills - graph
</h2>
</div>
<div className="mt-10 flex gap-10 flex-wrap justify-center">
<div>
<Stacked height="420px" width="580px" />
</div>
<div>
<BackendStacked height="420px" width="580px" />
</div>
</div>
</div>
</div>
</div>
);
};
export default Skills;
This requirement can be achieved using the chart axisLabelRender event. Please review the code snippet and sample link shared below, which changes axis labels using an event.
export let ylabels = ["Zero", "Twenty", "Fourty", "Sixty","Eighty", "Hundred"];
export let i = 0;
<ChartComponent id='charts' axisLabelRender={this.onAxisLabelRender.bind(this)}>
</ChartComponent>
onAxisLabelRender(args){
if (args.axis.name == "primaryYAxis") {
if(ylabels.length == i)
i =0;
args.text = ylabels[i]+" %";
i++;
}
}
Sample : https://stackblitz.com/edit/react-w6o5wf
Here’a some more relevant information from our Documentation to learn more about the axisLabelRender event.
https://ej2.syncfusion.com/react/documentation/chart/axis-labels/#customizing-specific-point
Please let us know if you have any more questions.

How can I change the class of an element dynamically via url in React?

I have a navbar with several Links that route to sub-pages. I now want to apply a specific className to the element which is currently clicked/visited.
My idea was to store the new className in a const isClicked and apply this to the element which is clicked. I thought about checking the current path window.location.pathname with the name of inner html of the div in that Link for all Link elements but don't know "how to communicate with that div's content"?
const DashboardWrapper = () => {
// Class Toggler for clicked element - check via url
const isClicked = 'link-item flex items-center mb-4 rounded-md p-2 pl-4 shadow-lg bg-sx-purple text-sx-pink';
// Run the check for isClicked
const checkSite = () => {
let current_site = window.location.pathname
}
return (
<div className="body-wrapper w-6/6 h-screen flex">
<div className="sidebar w-1/6 h-full bg-sx-purple-dark-soft p-4">
<div className="sidebar-head p-4 border-b-2 border-sx-white flex flex-col">
<div className="user-name text-2xl text-sx-pink">Jonas</div>
<div className="user-name text-md text-sx-white">Example GmbH</div>
</div>
<div className="sidebar-body p-4">
<div className="link-wrapper">
<Link to="/dashboard/overview/">
<div className={isClicked}>
<img className="link-icon flex w-4 mr-4" src={imageLocation} alt="imageIcon"/>
<div className="link-text">Overview</div>
</div>
</Link>
...
</div>
</div>
</div>
)}
You can do something like that:
function CustomLink({ children, to, ...props }: LinkProps) {
let resolved = useResolvedPath(to);
let match = useMatch({ path: resolved.pathname, end: true });
return (
<div>
<Link
style={{ textDecoration: match ? "underline" : "none" }}
to={to}
{...props}
>
{children}
</Link>
{match && " (active)"}
</div>
);
}

Javascript/react not running in child component - nothing clickable

I am using tailwind and a DaisyUI to create a page with two tabs. The parent component holds the tabs and calls a child component for the content of each tab. This works fine.
This issue is that while everything displays and styles correctly in the child components, nothing alters or updates. Even mouseover doesn't change the cursor. It is as if react/javascript isn't running in those child components. The tabs work, so I know it is running on the page. There are no errors in the console.
Parent App - working - the tabs work fine
import ReportDeliveryIssue from "./reportDeliveryIssue";
import ScheduleVacationStop from "./scheduleVacationStop";
import { Link } from "react-router-dom";
function ManageMyDelivery({ tab }) {
return (
<div className="container mx-auto mt-20">
<div className="tabs tabs-wrapper">
<Link
to="/account/manageDelivery/reportDeliveryIssue"
className={`tab tab-lg tab-lifted ${
tab === "reportDeliveryIssue" ? "tab-active" : null
}`}
>
Delivery Issue
</Link>
<Link
to="/account/manageDelivery/scheduleVacationStop"
className={`tab tab-lg tab-lifted ${
tab === "scheduleVacationStop" ? "tab-active" : null
}`}
>
Stop Delivery
</Link>
</div>
<div
className="container"
style={{
border: "1px solid #CCC",
width: "100%",
minHeight: "50px",
borderRadius: "0px 10px 10px 10px",
zIndex: -99999,
position: "relative",
top: "-1px",
}}
>
{tab === "reportDeliveryIssue" && <ReportDeliveryIssue />}
{tab === "scheduleVacationStop" && <ScheduleVacationStop />}
</div>
</div>
);
}
export default ManageMyDelivery;
Child App - not working (updated with full component)
import React, { useState } from "react";
import { Link } from "react-router-dom";
import { useForm, useField } from "react-final-form-hooks";
import Calendar from "react-calendar";
import "react-calendar/dist/Calendar.css";
import ButtonWrapper from "../../../components/forms/ButtonWrapper";
import RadioWrapper from "../../../components/forms/RadioWrapper";
function ScheduleVacationStop() {
const onSubmit = () => {
console.log("Submitting");
};
const validate = (values) => {
const errors = {};
return errors;
};
const { form, handleSubmit, values, pristine, submitting } = useForm({
onSubmit,
validate,
});
const savePapers = useField("savePapers", form);
const stopPapers = useField("stopPapers", form);
const [value, onChange] = useState(new Date());
return (
<form onSubmit={handleSubmit}>
<div className={"container px-16 pt-16 mb-4 text-left"}>
<div className={"text-3xl mb-8 font-bold"}>Stop Delivery</div>
<div className="grid grid-cols-2">
<div className="mx-4">
<div className={"mb-8"}>
<div className="dropdown border-2 border-solid border-neutral-400 text-gray-400 p-2 font-bold w-full text-sm">
<label tabIndex="0" className="">
Stop delivery beginning
</label>
<div
tabIndex="0"
className="dropdown-content card card-compact w-64 p-2 shadow bg-primary text-primary-content"
>
<div className="card-body">
<h3 className="card-title">Card title!</h3>
<p>you can use any element as a dropdown.</p>
</div>
</div>
</div>
<Calendar
className={"w-full"}
calendarType="US"
onChange={onChange}
value={value}
/>
</div>
<div className="mb-4">
<RadioWrapper
name={"paperStatus"}
label={"Vacation Stop with Restart (SAVE MY PAPERS)"}
description={
"The newspaper delivery will stop during the time you are away and all papers will be delivered when you resume (14 days or less)."
}
value={0}
{...savePapers}
/>
</div>
<div>
<RadioWrapper
name={"paperStatus"}
label={"Vacation Stop with Restart"}
description={
"The newspaper delivery will stop and restart as requested."
}
value={1}
{...stopPapers}
/>
</div>
</div>
<div className={"mb-8"}>
<div className="dropdown border-2 border-solid border-neutral-400 text-gray-400 p-2 font-bold w-full text-sm">
<label tabIndex="0" className="">
Resume delivery beginning
</label>
<div
tabIndex="0"
className="dropdown-content card card-compact w-64 p-2 shadow bg-primary text-primary-content"
>
<div className="card-body">
<h3 className="card-title">Card title!</h3>
<p>you can use any element as a dropdown.</p>
</div>
</div>
</div>
<Calendar calendarType="US" onChange={onChange} value={value} />
</div>
</div>
<div className="divider"></div>
</div>
<div className="mx-12">
<div className="alert alert-info shadow-lg px-12 text-white">
<div>
<span>
To adjust an existing vacation stop, please call Customer Service
at 214-745-8383 or 1-800-925-1500.
</span>
</div>
</div>
</div>
<div className="divider"></div>
<div className={"container flex justify-between px-16 pt-2 pb-8"}>
<div className={"uppercase text-sm text-sky-500 font-bold"}>
<Link to="/account/dashboard">Return to Dashboard</Link>
</div>
<ButtonWrapper
buttonType="submit"
handleClick={() => handleSubmit(values)}
buttonText={"Submit"}
/>
</div>
</form>
);
}
export default ScheduleVacationStop;
So, this was an odd situation. There was a div that held the tabs and a div that held all tab content. I placed a condition on each child component to only be shown when the appropriate tab was selected.
To make the active tab display correctly and get rid of the border line at the top of the content box, I made the content's position relative and bumped it up one pixel. Apparently, this is what was causing the problem.
The solution was to remove that and bump the tabs down one pixel.
Not sure if this is a problem with Daisy or what.

TailwindCSS fade in Element on click

So I'm making this app and I need to fade in the menu when I click the button. I have it rendering on click using state, but I can't get it to fade in / fade out on click. When I edit the opacity value inside Chrome Dev Console the transition works fine, but when I want to change it using state it doesn't.
Any help? Thanks in advance!
import React, { useState } from "react";
import { useRouter } from "next/router";
import { MenuIcon, XIcon } from "#heroicons/react/outline";
function Header() {
const router = useRouter();
const [popCard, setPopCard] = useState("hidden");
const [fade, setFade] = useState(true);
const handleMenuClick = () => {
setPopCard("inline-block");
setFade(true);
};
const handleXClick = () => {
setPopCard("hidden");
setFade(false);
};
return (
<div className="text-center">
<header className="sticky z-50 top-0 shadow-md bg-white border-b p-5">
<div className="flex justify-between items-center">
<h1
className="text-6xl text-red-500 cursor-pointer"
onClick={() => router.push("/")}
>
Velvet
</h1>
<MenuIcon
className="h-8 text-red-500 cursor-pointer"
onClick={handleMenuClick}
/>
</div>
</header>
<div
className={
popCard +
" w-[60%] flex-col border my-10 pb-3 rounded-3xl shadow-lg transition duration-300 ease-in-out " +
`${fade === true ? "opacity-100" : "opacity-0"}`
}
>
<div className="flex justify-end">
<XIcon
className="h-6 text-red-500 cursor-pointer mt-2 mr-2 opacity-70"
onClick={handleXClick}
/>
</div>
<div className="space-y-8 text-3xl text-center mt-5 mb-10 text-red-500">
<h1>Contac</h1>
<h1>About Us</h1>
</div>
</div>
</div>
);
}
export default Header;
codesandbox: Sandbox
Just to be clear, I want the menu card to fade in when I click the menu button, and I want the menu card to fade out when I click the close button.
The solution is, you need to add duration, like this:
`transition-all duration-200 ${fade ? "opacity-100" : "opacity-0"}`
Here is my forked sandbox you had given, I've removed extra inline CSS, so it may become evident.
Here is the complete code:
function Header() {
const [popCard, setPopCard] = useState("hidden");
const [fade, setFade] = useState(false);
const handleMenuClick = () => {
setPopCard("inline-block");
setFade(true);
};
const handleXClick = () => {
setPopCard("hidden");
setFade(false);
};
console.log(fade, "fade");
return (
<div className="text-center">
<header className="sticky z-50 top-0 shadow-md bg-white border-b p-5">
<div className="flex justify-between items-center">
<h1 className="text-6xl text-red-500 cursor-pointer">Velvet</h1>
<button
className="text-3xl border rounded-lg px-5"
onClick={handleMenuClick}
>
Menu
</button>
</div>
</header>
<div className="p-10">
<div
className={`transition-all duration-200 ${
fade ? "opacity-100" : "opacity-0"
}`}
>
<div className="flex justify-end">
<button className="mt-2 mr-2 border p-2" onClick={handleXClick}>
Close
</button>
</div>
<div className="space-y-2 text-3xl text-center mt-5 mb-10 mx-5 text-red-500">
<h1>Kontakt</h1>
<h1>O Velvetu</h1>
</div>
</div>
</div>
</div>
);
}
export default Header;
Sandbox: https://codesandbox.io/s/sweet-swartz-mr3nru?file=/pages/index.js:41-1396

Pass prop to different page in Next JS/ React

I'm trying to pass props to a new page in Next JS.
I'm mapping through an array, each item should link to a page where I can use data from the array as props:
{mentors.map((mentor) => (
<Link href="/developers/developer">
<div
key="name"
className="flex flex-col text-green-50 bg-gray-800 rounded-md p-6 gap-3 border border-gray-800 hover:border-gray-700 cursor-pointer"
>
<div className="flex justify-between items-center ">
<div className="flex items-center gap-2">
<img src={mentor.pic} className="rounded-full w-10" />{" "}
{mentor.name}
</div>
{mentor.timeZone}
</div>
<p className="text-gray-300">
I am a full-stack dev with over 8 years experience in
making software and web-apps.
</p>
<div className="flex gap-2 items-center">
Can teach:
{mentor.languages.map((language) => (
<p
key={language}
className="px-3 py-1 bg-gray-900 rounded-full max-w-max text-sm"
>
{language}
</p>
))}
</div>
</div>
</Link>
))}
This is the page:
export default function DeveloperPage({ name, picture }) {
return (
<>
{name}
<img src={picture}/> </>)}
I need to pass that {name} and {picture} prop from the item in the array to the new page.
What's the most elegant way to do it in Next?
Thanks!
You can pass it trough URL query
Like this
<Link href={`/developers/developer?name=${mentor.name}&picture=${mentor.picture}`}>
and you can use useRouter on your DeveloperPage to get its values
import { useRouter } from 'next/router'
export default function DeveloperPage() {
const router = useRouter()
const {name, picture} = router.query // <-- passed datas are in here
return (
<>
{name}
<img src={picture}/>
</>
)
}

Resources