The initial chart before the chart data changes automatically in reactjs - reactjs

I'm having trouble here when I want to put chart data at the beginning.
So, before the user clicks on the name "province", the chart will first display provincial data.
Then, the user clicks on the province name, the chart will change the previous chart data to city chart data. like this
However, I am stuck here, displaying the initial chart data, namely provincial chart data.
Please help me to solve my current problem, that is, displaying province charts before changing to city charts. Thank you
My Code
import React, { useState, useEffect, useRef } from "react";
import {
Accordion,
AccordionItem,
AccordionItemHeading,
AccordionItemButton,
AccordionItemPanel,
} from "react-accessible-accordion";
import { MdKeyboardArrowDown } from "react-icons/md";
import {
getStreetDallProvinsi,
// getStreetImageDallProvinsi,
getStreetallKota,
getStreetallKecamatan,
getStreetallKelurahan,
} from "../../../service/building";
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
} from "chart.js";
import { Bar } from "react-chartjs-2";
import SearchComponent from "./SearchPoi";
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend
);
const Data = () => {
const [filterdata, setFilterdata] = useState([]);
const [query, setQuery] = useState("");
const [dataProvinsi, setDataProvinsi] = useState([]);
const [dataKota, setDataKota] = useState([]);
const [dataKecamatan, setDataKecamatan] = useState([]);
const [dataKelurahan, setDataKelurahan] = useState([]);
// const [dataImageProvinsi, setDataImageProvinsi] = useState([]);
const [dataChart, setDataChart] = useState();
const [isLoading, setIsLoading] = useState(false);
const [isLoadingKota, setIsLoadingKota] = useState(false);
const [isLoadingKecamatan, setIsLoadingKecamatan] = useState(false);
const provinsiRef = useRef([]);
const kotaRef = useRef([]);
const kecamatanRef = useRef([]);
const getDataAllProvinsi = () => {
setIsLoading(true);
getStreetDallProvinsi()
.then((resolve) => {
setDataProvinsi(resolve);
setFilterdata(resolve);
console.log(resolve);
})
.catch((reject) => {
console.log(reject);
})
.finally(setIsLoading(false));
};
const handlesearch = (event) => {
const getSearch = event.target.value;
if (getSearch.length > 0) {
const searchdata = dataProvinsi.filter((item) =>
item.provinsi.toLowerCase().includes(event.target.value.toLowerCase())
);
setDataProvinsi(searchdata);
} else {
setDataProvinsi(filterdata);
}
setQuery(getSearch);
};
// const getDataImageAllProvinsi = () => {
// setIsLoading(true);
// getStreetImageDallProvinsi()
// .then((resolve) => {
// setDataImageProvinsi(resolve);
// console.log();
// })
// .catch((reject) => {
// console.log(reject);
// })
// .finally(setIsLoading(false));
// };
const handleProvinsi = async (index) => {
try {
const provinsi = provinsiRef.current[index].dataset.prov;
setIsLoading(true);
const result = await getStreetallKota(provinsi);
setDataKota(result);
setDataChart(state.data.dataKota);
console.log(result);
} catch (error) {
console.log("salah")
} finally {
setIsLoading(false);
}
};
const handleKota = async (provinsi, index) => {
try {
const kota = kotaRef.current[index].dataset.city;
setIsLoadingKota(true);
const result = await getStreetallKecamatan(provinsi, kota);
setDataKecamatan(result);
setDataChart(state.data.dataKecamatan);
console.log(result);
} catch (error) {
console.log("salah")
} finally {
setIsLoadingKota(false);
}
};
const handleKecamatan = async (provinsi, kota, index) => {
try {
const kecamatan = kecamatanRef.current[index].dataset.camat;
setIsLoadingKecamatan(true);
const result = await getStreetallKelurahan(provinsi, kota, kecamatan);
setDataKelurahan(result);
console.log(result);
} catch (error) {
console.log("salah")
console.log(error)
} finally {
setIsLoadingKecamatan(false);
}
};
useEffect(() => {
getDataAllProvinsi();
// getDataImageAllProvinsi();
}, [dataChart]);
const colorCode = "#0066FF";
const colorFont = "#8E9093";
const state = {
data: {
dataProv: {
labels: dataProvinsi.map((o) => o.provinsi),
datasets: [
{
fill: true,
label: null,
backgroundColor: colorCode,
borderColor: colorCode,
borderWidth: 2,
borderRadius: 12,
data: dataProvinsi.map((o) => o.total_street),
},
],
},
dataKota: {
labels: dataKota.map((o) => o.kota),
datasets: [
{
fill: true,
label: null,
backgroundColor: colorCode,
borderColor: colorCode,
borderWidth: 2,
borderRadius: 12,
data: dataKota.map((o) => o.total_street),
},
],
},
dataKecamatan: {
labels: dataKecamatan.map((o) => o.kecamatan),
datasets: [
{
fill: true,
label: null,
backgroundColor: colorCode,
borderColor: colorCode,
borderWidth: 2,
borderRadius: 12,
data: dataKecamatan.map((o) => o.total_street),
},
],
},
},
options: {
plugins: {
legend: {
display: false,
labels: {
font: {
color: colorFont,
},
},
},
},
scales: {
x: {
grid: {
display: false,
},
beginAtZero: false,
ticks: {
color: colorFont,
},
},
y: {
grid: {
display: false,
},
beginAtZero: true,
ticks: {
color: colorFont,
},
},
},
},
};
const plugins = [
{
beforeDraw: function (chart) {
if (chart.chartArea) {
let ctx = chart.ctx;
let chartArea = chart.chartArea;
let barArray = chart.getDatasetMeta(0).data;
ctx.fillStyle = "#B2D1FF85";
for (let i = 0; i < barArray.length; i++) {
const { x, width } = barArray[i];
ctx.fillRect(
x - width / 2,
chartArea.top,
width,
chartArea.bottom - chartArea.top
);
}
}
},
},
];
return (
<>
<div className="mx-auto mt-8 w-[70rem] h-[32rem] bg-white px-5 py-3 md:px-8 md:py-5 rounded-xl drop-shadow-xl">
<h1 className="font-bold text-lg mb-4">Diagram Data</h1>
<Bar
type={"bar"}
height={120}
data={state.data.dataKota}
options={state.options}
plugins={plugins}
/>
</div>
<div className="mx-auto my-8 w-[70rem] bg-white px-5 py-3 md:px-8 md:py-5 rounded-md drop-shadow-xl">
<SearchComponent value={query} onChange={(e) => handlesearch(e)} />
<div className="flex justify-between mt-6">
<h3 className="font-bold">Nama Daerah</h3>
<div className="flex gap-8">
<h3 className="font-bold">TOTAL IMAGE</h3>
<h3 className="font-bold">TOTAL LABELLING</h3>
</div>
</div>
<Accordion allowZeroExpanded>
{dataProvinsi.map((provinsi, index) => (
<AccordionItem className="p-1" key={index}>
<AccordionItemHeading
onClick={() => {
handleProvinsi(index);
}}
>
<AccordionItemButton>
<div className="w-full inline-flex justify-between items-center">
<div className="inline-flex items-center gap-2">
<div>
<MdKeyboardArrowDown size={20} />
</div>
<div
ref={(ref) => provinsiRef.current.push(ref)}
data-prov={provinsi?.provinsi}
>
{provinsi?.provinsi}
</div>
</div>
<div>{provinsi?.total_street}</div>
</div>
</AccordionItemButton>
</AccordionItemHeading>
<AccordionItemPanel className="ml-4">
<Accordion className="flex flex-col gap-2" allowZeroExpanded>
{isLoading
? "Loading ... "
: dataKota.map((kota, index) => (
<AccordionItem className="p-1" key={kota?.id}>
<AccordionItemHeading
onClick={() => {
handleKota(provinsi?.provinsi, index);
}}
>
<AccordionItemButton>
<div className="w-full inline-flex justify-between items-center">
<div className="inline-flex items-center gap-2">
<div>
<MdKeyboardArrowDown size={20} />
</div>
<div
ref={(ref) => kotaRef.current.push(ref)}
data-city={kota?.kota}
>
{kota?.kota}
</div>
</div>
<div>{kota?.total_street}</div>
</div>
</AccordionItemButton>
</AccordionItemHeading>
<AccordionItemPanel className="ml-8">
<Accordion
className="flex flex-col gap-2"
allowZeroExpanded
>
{isLoadingKota
? "Loading..."
: dataKecamatan.map((kecamatan, index) => (
<AccordionItem
className="p-1"
key={kecamatan?.id}
>
<AccordionItemHeading
onClick={() => {
handleKecamatan(
provinsi?.provinsi, kota?.kota,
index
);
}}
>
<AccordionItemButton>
<div className="w-full inline-flex justify-between items-center">
<div className="inline-flex items-center gap-2">
<div>
<MdKeyboardArrowDown
size={20}
/>
</div>
<div
ref={(ref) => kecamatanRef.current.push(ref)}
data-camat={kecamatan?.kecamatan}
>
{kecamatan?.kecamatan}
</div>
</div>
<div>{kecamatan?.total_street}</div>
</div>
</AccordionItemButton>
</AccordionItemHeading>
<AccordionItemPanel className="ml-12">
<Accordion
className="flex flex-col gap-2"
allowZeroExpanded
>
{isLoadingKecamatan
? "Loading..."
: dataKelurahan.map(
(item, index) => (
<AccordionItem
className="p-1"
key={index}
>
<AccordionItemHeading>
<AccordionItemButton>
<div className="w-full inline-flex justify-between items-center">
<div className="inline-flex items-center gap-2">
<div>
<MdKeyboardArrowDown
size={20}
/>
</div>
<div>
{item?.kelurahan}
</div>
</div>
<div>
{item?.total_street}
</div>
</div>
</AccordionItemButton>
</AccordionItemHeading>
</AccordionItem>
)
)}
</Accordion>
</AccordionItemPanel>
</AccordionItem>
))}
</Accordion>
</AccordionItemPanel>
</AccordionItem>
))}
</Accordion>
</AccordionItemPanel>
</AccordionItem>
))}
</Accordion>
</div>
</>
);
};
export default Data;

Related

moving cards with react-sortablejs

I wanted to try out Strapis guide to create a Trello Clone
https://strapi.io/blog/build-a-trello-clone-application-with-react-and-strapi-1
But right now I'm having a bit of an issue moving my cards...
The cards do show up when moving them but for some reason it doesn't seem to be switching the "state".
this is what my Board.js looks like:
import { ReactSortable } from "react-sortablejs";
import { useState, useEffect } from "react";
import axios from "axios";
const Board = () => {
const [tasks, settasks] = useState([]);
const [drafts, setdrafts] = useState([]);
const [progresstasks, setprogresstasks] = useState([]);
const [approvals, setapprovals] = useState([]);
const [finished, setfinished] = useState([]);
const [onholds, setonholds] = useState([]);
const [newTask, setnewTask] = useState("");
const addTask = async () => {
if(newTask !== ""){
let res = await axios
.post("http://localhost:1337/api/tasks", {
data :{
state: "draft",
Description: newTask,
}
})
.catch((err) => alert("Error occured"));
}
await getTasks();
setnewTask("");
};
const getTasks = async () => {
let Tasks = await axios.get("http://localhost:1337/api/tasks?populate=*");
// return;
settasks(Tasks.data.data);
// For Drafts
let Drafts = tasks.filter((res) => {
return res.attributes.state=== "draft";
});
setdrafts(Drafts);
// For Progress
let Progress = tasks.filter((res) => {
return res.attributes.state === "progress";
});
setprogresstasks(Progress);
// For Finished
let Finished = tasks.filter((res) => {
return res.attributes.state === "finished";
});
setfinished(Finished);
// For Approval
let Approval = tasks.filter((res) => {
return res.attributes.state === "approval";
});
setapprovals(Approval);
// For Onhold
let Onhold = tasks.filter((res) => {
return res.attributes.state=== "onhold";
});
setonholds(Onhold);
};
const getCustomers = async () => {
let Customers = await axios.get("http://localhost:1337/api/customers?populate=*");
}
useEffect(() => {
getTasks();
getCustomers();
});
return (
<>
<div className="container mt-5 mb-5">
<div
className="row"
style={{
height: "80vh",
}}
>
<div className="col mx-2 px-2 py-3 bg-light border rounded">
<h6>Entwurf</h6>
<div
style={{
minHeight: "500px",
}}
>
<ReactSortable
list={tasks}
setList={setdrafts}
groupp={"group-1"}
group={{ name: "group-1", put: true }}
>
{tasks.filter((task) => task.attributes.state === "draft")
.map((filteredTask) => (
<div
className="card p-3 border rounded mt-2"
key={filteredTask.id}
>
{filteredTask.attributes.Description}
</div>
))}
</ReactSortable>
</div>
<div>
<textarea
rows={"1"}
cols={30}
style={{ float: "left", borderBlockColor: "#007bff" }}
value={newTask}
onChange={(event) => setnewTask(event.target.value)}
></textarea>
<button
type="button"
style={{ float: "right", marginTop: "2px" }}
class="btn btn-primary btn-sm"
onClick={addTask}
>
Add Task
</button>
</div>
</div>
<div className="col mx-2 px-2 py-3 bg-light border rounded">
<h6>In Bearbeitung</h6>
<div
style={{
minHeight: "500px",
}}
>
<ReactSortable
list={tasks}
setList={setprogresstasks}
grouppp={"group-1"}
// group={{ name: "group-1", put: true }}
>
{tasks.filter((task) => task.attributes.state === "progress")
.map((filteredTask) => (
<div
className="card p-3 border rounded mt-2"
key={filteredTask.id}
>
{filteredTask.attributes.Description}
</div>
))}
</ReactSortable>
</div>
</div>
<div className="col mx-2 px-2 py-3 bg-light border rounded">
<h6>Fertig</h6>
<div
style={{
minHeight: "500px",
}}
>
<ReactSortable
list={tasks}
setList={setfinished}
groupppp={"group-1"}
// group={{ name: "group-1", put: true }}
>
{tasks.filter((task) => task.attributes.state === "finished")
.map((filteredTask) => (
<div
className="card p-3 border rounded mt-2"
key={filteredTask.id}
>
{filteredTask.attributes.Description}
</div>
))}
</ReactSortable>
</div>
</div>
<div className="col mx-2 px-2 py-3 bg-light border rounded">
<h6>On-Hold</h6>
<div
style={{
minHeight: "500px",
}}
>
<ReactSortable
list={tasks}
setList={setonholds}
grouppppp={"group-1"}
// group={{ name: "group-1", put: true }}
>
{tasks.filter((task) => task.attributes.state === "onhold")
.map((filteredTask) => (
<div
className="card p-3 border rounded mt-2"
key={filteredTask.id}
>
{filteredTask.attributes.Description}
</div>
))}
</ReactSortable>
</div>
</div>
<div className="col mx-2 px-2 py-3 bg-light border rounded">
<h6>Wartet auf Freigabe</h6>
<div
style={{
minHeight: "500px",
}}
>
<ReactSortable
list={tasks}
setList={setapprovals}
groupppppp={"group-1"}
// group={{ name: "group-1", put: true }}
>
{tasks.filter((task) => task.attributes.state === "approval")
.map((filteredTask) => (
<div
className="card p-3 border rounded mt-2"
key={filteredTask.id}
>
{filteredTask.attributes.Description}
</div>
))}
</ReactSortable>
</div>
</div>
</div>
</div>
</>
);
};
export default Board;
Is there something I'm missing like executing a function when moving the cards?

Show/Hide multiple elements of each button click with matching indexes in React JS

I have a scenario where I have 2 different components ( buttons and an info container). On each button click I am trying to display each matched info container. I am able to achieve the desired functionality in my buttons, but when I pass state back to my other component I am only able to display the matched index. My desired result is if I clicked a button in my nav and it has an active class all my "info container" should remain visible until the "active" class is toggled/removed.
JS:
...
const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
"& > *": {
margin: theme.spacing(1)
}
},
orange: {
color: theme.palette.getContrastText(deepOrange[500]),
backgroundColor: deepOrange[500],
border: "4px solid black"
},
info: {
margin: "10px"
},
wrapper: {
display: "flex"
},
contentWrapper: {
display: "flex",
flexDirection: "column"
},
elWrapper: {
opacity: 0,
"&.active": {
opacity: 1
}
}
}));
const ToggleItem = ({ onChange, id, styles, discription }) => {
const [toggleThisButton, setToggleThisButton] = useState(false);
const handleClick = (index) => {
setToggleThisButton((prev) => !prev);
onChange(index);
};
return (
<>
<Avatar
className={toggleThisButton ? styles.orange : ""}
onClick={() => handleClick(id)}
>
{id}
</Avatar>
{JSON.stringify(toggleThisButton)}
{/* {toggleThisButton && <div className={styles.info}>{discription}</div> } */}
</>
);
};
const ToggleContainer = ({ discription, className }) => {
return <div className={className}> Content {discription}</div>;
};
export default function App() {
const data = ["first", "second", "third"];
const classes = useStyles();
const [value, setValue] = useState(false);
const handleChange = (newValue) => {
setValue(newValue);
console.log("newValue===", newValue);
};
return (
<>
<div className={classes.wrapper}>
{data.map((d, id) => {
return (
<div key={id}>
<ToggleItem
id={id}
styles={classes}
discription={d}
onChange={handleChange}
/>
</div>
);
})}
</div>
<div className={classes.contentWrapper}>
{data.map((d, id) => {
return (
<ToggleContainer
className={
value === id
? clsx(classes.elWrapper, "active")
: classes.elWrapper
}
key={id}
styles={classes}
discription="Hello"
/>
);
})}
</div>
</>
);
}
Codesanbox:
https://codesandbox.io/s/pedantic-dream-vnbgym?file=/src/App.js:0-2499
Codesandbox : https://codesandbox.io/s/72166087-zu4ev7?file=/src/App.js
You can store the selected tabs in a state. That way you don't need to render 3 (or more) <ToggleContainer>. In <ToggleContainer> pass the selected tabs as props and render the selected tabs content in <ToggleContainer>.
import React, { useState } from "react";
import "./styles.css";
import { makeStyles } from "#material-ui/core/styles";
import Avatar from "#material-ui/core/Avatar";
import { deepOrange } from "#material-ui/core/colors";
import clsx from "clsx";
const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
"& > *": {
margin: theme.spacing(1)
}
},
orange: {
color: theme.palette.getContrastText(deepOrange[500]),
backgroundColor: deepOrange[500],
border: "4px solid black"
},
info: {
margin: "10px"
},
wrapper: {
display: "flex"
},
contentWrapper: {
display: "flex",
flexDirection: "column"
},
elWrapper: {
opacity: 0,
"&.active": {
opacity: 1
}
}
}));
const ToggleItem = ({ onChange, id, styles, discription }) => {
const [toggleThisButton, setToggleThisButton] = useState(false);
const handleClick = (index) => {
onChange(discription, !toggleThisButton);
setToggleThisButton((prev) => !prev);
};
return (
<>
<Avatar
className={toggleThisButton ? styles.orange : ""}
onClick={() => handleClick(id)}
>
{id}
</Avatar>
{JSON.stringify(toggleThisButton)}
{/* {toggleThisButton && <div className={styles.info}>{discription}</div> } */}
</>
);
};
const ToggleContainer = ({ className, selected }) => {
return (
<div className={className}>
{selected.map((item, idx) => (
<div key={idx}>Content {item}</div>
))}
</div>
);
};
export default function App() {
const data = ["first", "second", "third"];
const classes = useStyles();
const [selected, setSelected] = useState([]);
// action : False -> Remove, True -> Add
const handleChange = (val, action) => {
let newVal = [];
if (action) {
// If toggle on, add content in selected state
newVal = [...selected, val];
} else {
// If toggle off, then remove content from selected state
newVal = selected.filter((v) => v !== val);
}
console.log(newVal);
setSelected(newVal);
};
return (
<>
<div className={classes.wrapper}>
{data.map((d, id) => {
return (
<div key={id}>
<ToggleItem
id={id}
styles={classes}
discription={d}
onChange={handleChange}
/>
</div>
);
})}
</div>
<div className={classes.contentWrapper}>
<ToggleContainer styles={classes} selected={selected} />
</div>
</>
);
}

React Framer motion- get child component y position to parent and pass it to another child

I'm trying to do shared transition animation using framer motion.
This is the preview what i got so far https://ibb.co/y466L5c
I need to pass y position of a selected card to detailview component
I already passed it to parent component.But its only showing on console.log. Cannot pass it to next component
This is the card component
const Vibes: React.FC<Props> = ({ events,printYValue}) => {
let currentTop: number = 0;
const [position, setPosition] = useState<number>();
const inputRef = useRef<HTMLDivElement>(null);
const handleTap = () => {
if (inputRef.current)
{
currentTop = -Math.abs(inputRef.current.getBoundingClientRect().y);
setPosition(currentTop);
printYValue(currentTop);
}
};
return (
<div className="oholder">
<div
ref={inputRef}
className="VibeCard-card background-img"
style={{ backgroundImage: `url('${events.image_url}')` }}
onClick={() => handleTap()}>
<div className="mask"></div>
<div className="card-caption">
<IonRow className="ion-align-items-end">
<IonCol className="caption-text">
<div className="card-title ">
<h1>{events.title}</h1>
</div>
<div className="caption-title">{events.caption}</div>
<div className="caption-time">discover</div>
</IonCol>
</IonRow>
</div>
</div>
</div>
);
};
This is the detailsview component
type Props =
{
isVisible: boolean;
onClose: () => void;
id:number,
prevYPosition:number
};
const Overlay: React.FC<Props> = ({ isVisible, onClose, id,prevYPosition}) => {
const events: any[] = [...EVENTS];
const [position, setPosition] = useState<number>();
const singlecard = events.find((item)=>item.id === id)
const targetRef = useRef();
const variants = {
open: {
zIndex: 1,
scale: 1,
y: 0,
},
collapsed: {
zIndex: 100,
y: position,
scale: 1.2,
transition: { duration: 0.2 }
},
};
useEffect(() => {
setPosition(prevYPosition)
}, []);
const handleTap = () => {
if (onClose) {
onClose();
}
};
return (
<div className="over-back">
<AnimatePresence>
{isVisible && (
<motion.div
className="overlayy"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.5 }}
onClick={() => handleTap()}
>
<motion.div
className="VibeCard-card background-img"
style={{ backgroundImage: `url('${singlecard.image_url}')` }}
onClick={() => handleTap()}
initial={{ zIndex: 1,
scale: 1, y: position, }}
animate={{
zIndex: 100,
y: 0,
scale: 1.2,
transition: { duration: 0.5 }
}}
exit={{ opacity: 0,y: 300,}}
>
<div className="overlay"></div>
<div className="card-caption">
<IonRow className="ion-align-items-end">
<IonCol className="caption-text">
<div className="card-title ">
<motion.h1>
{singlecard.title}
</motion.h1>
</div>
<div className="caption-title">{singlecard.caption}</div>
<div className="caption-time">discover {id}</div>
</IonCol>
</IonRow>
</div>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</div>
);
};
Overlay.defaultProps = {};
export default Overlay;
This is parent component
Props = {
};
const Discover: React.FC<Props> = ({ }) => {
const events: any[] = [...EVENTS];
const [selected, setSelected] = useState(0);
const [isActive, setActive] = useState(false);
const [prevYposi,setPrevYPosi] = useState<number>(0);
let currentTop: number = 0;
const handleStart = (actnum: number) => {
setActive(true);
if (isActive === false) {
setSelected(actnum);
}
console.log(prevYposi)
};
const HnadleOverlay = () => {
setTimeout(() => {
setActive(false);
console.log("overlay removed");
}, 500);
};
const printyvalue = (arg:number) =>{
console.log(arg). //here i'm getting correct value.But can't
it won't update the Yposi in other component
setPrevYPosi(arg);
}
useEffect(() => {
console.log(currentTop);
}, []);
return (
<IonPage>
<IonHeader className="header-custom"></IonHeader>
<IonContent className="discover-page ion-padding">
<IonRow>
<IonCol size="12">
{events.map((event, index) => (
<IonCol
size="6"
key={event.id}
onClick={() => handleStart(event.id)}
>
<Vibes printYValue={printyvalue} events={event} />
</IonCol>
))}
</IonCol>
</IonRow>
</div>
</div>
<Overlay
prevYPosition={prevYposi} // prevYposi always getting the start value
isVisible={isActive}
onClose={HnadleOverlay}
id={selected}
/>
</IonContent>
</IonPage>
);
};
Discover.defaultProps = {};
export default Discover;

Why my props are undefined when sending to components?

I have a table in my project and I have an edit / view / add page that I can access from this table. My goal is to send the clicked data to the other component without any problem, but no matter how hard I try, I get an undefined error and the project is broken. I would be glad if you could help.
I am sharing my codes from parent to child component
Table page.
import React, { useState, useEffect, useCallback, useMemo } from "react";
import ManagementTable from '../components/ManagementTable'
import {
getApps,
updateStopRisk,
countLiveCountry,
updateAppShow,
deleteApp,
} from "../api/apiCalls";
import VisibilityIcon from "#material-ui/icons/Visibility";
import DeleteIcon from "#material-ui/icons/Delete";
import EditIcon from "#material-ui/icons/Edit";
import Switch from "#material-ui/core/Switch";
import DeleteModal from "../components/DeleteModal";
import { Link } from "react-router-dom";
const Management = () => {
const [apps, setApps] = useState([]);
const [modalVisible, setModalVisible] = useState(false);
const [currentApp, setCurrentApp] = useState("");
const [appID, setAppID] = useState(0);
const fetchData = useCallback(async () => {
const { data: appsResponse } = await getApps();
const countLiveCountries = await fetchLiveCountriesForApps(appsResponse);
setApps(
appsResponse.map((app, idx) => ({
...app,
countLiveCountry: countLiveCountries[idx],
}))
);
}, []);
useEffect(() => {
fetchData();
}, [fetchData]);
const fetchLiveCountriesForApps = async (appwLive) => {
const countLiveCountries = await Promise.all(
appwLive.map((app) => countLiveCountry(app.appID))
);
return countLiveCountries.map(({ data: liveCountries }) => liveCountries);
};
const removeApp = async () => {
await deleteApp(appID);
setModalVisible(false);
fetchData();
};
const onClickCancel = () => {
setModalVisible(false);
};
const columns = useMemo(() => [
{
Header: "Application Name",
accessor: "app_name",
},
{
Header: "Business Area",
accessor: "businessarea.businessarea_name",
},
{
Header: "Live Plants",
accessor: "countLiveCountry",
},
{
Header: "Line Stop Risk",
accessor: "app_stoprisk",
Cell: ({ row: { original } }) => {
const changeCheck = async (id) => {
await updateStopRisk(id);
fetchData();
};
return (
<input
checked={original.app_stoprisk}
onClick={() => {
changeCheck(original.appID);
}}
id="appStopRisk"
type="checkbox"
style={{ width: 18, height: 18, marginTop: 5 }}
/>
)
},
sortType: (a, b, id) => {
if (a.original[id] > b.original[id]) return -1;
if (b.original[id] > a.original[id]) return 1;
},
},
{
Header: "Actions",
Cell: ({ row: { original } }) => {
const changeTrack = async (id) => {
await updateAppShow(id);
fetchData();
};
return (
<>
<Link
className="btn btn-manage-link btn-sm col-2"
to={{
pathname: `/management/${original.app_name}`,
mode: "view",
id: original.appID
}}
>
<VisibilityIcon></VisibilityIcon>
</Link>
<Link
to={{
pathname: `/management/${original.app_name}`,
mode: "edit",
id: original.appID
}}
className="btn btn-manage-link btn-sm col-2"
>
<EditIcon></EditIcon>
</Link>
<button
onClick={() => {
setModalVisible(true);
setCurrentApp(original.app_name);
setAppID(original.appID);
}}
className="btn btn-manage-link btn-sm col-3"
>
<DeleteIcon></DeleteIcon>
</button>
<Switch
onClick={() => changeTrack(original.appID)}
checked={original.app_show}
className="col-3"
></Switch>
</>
)
},
},
],
[fetchData]
);
return (
<div className="container">
<h2 style={{ float: "left", font: "bold" }}>Management</h2>
<div style={{ float: "right" }}>
<Link className="btn btn-danger btn-sm" to={{ pathname: `/management/add`, mode: "add" }}>
Add New App
</Link>
<Link className="btn btn-danger btn-sm ml-3" exact to="/management/plants">
Plant Management
</Link>
</div>
<ManagementTable columns={columns} data={apps} />
<DeleteModal
message={<strong>{currentApp}</strong>}
variety="app"
onClickCancel={onClickCancel}
onClickOk={removeApp}
visible={modalVisible}
/>
</div>
);
};
export default Management;
The page where I transfer the props.
import React, { useState, useEffect } from "react";
import Accordion from "../components/Accordion";
import Details from '../components/Details'
import {
getByIdApps,
} from "../api/apiCalls";
const ApplicationManagement = (props) => {
const [appById, setAppById] = useState([]);
const { id } = props.location;
const [selectOption, setSelectOption] = useState('add')
useEffect(() => {
getData();
getMode();
}, [])
const getData = async () => {
console.log(props.location.id)
if (props.location.id) {
await getByIdApps(props.location.id).then((response) => setAppById(response.data))
console.log(appById)
console.log(props)
}
else {
setSelectOption('add')
}
}
const getMode = () => props.location.mode ? setSelectOption(props.location.mode) : setSelectOption('add')
const handleOptionChange = (event) => {
console.log(event.target.value)
setSelectOption(event.target.value)
}
return (
<>
<div style={{ margin: 20 }}>
<h1>
{appById.app_shortcode} - {appById.app_fullname}
</h1>
<div className="float-right mb-auto">
<label><input type="radio" value="view" checked={selectOption === 'view'} onChange={handleOptionChange} />View</label>
<label> <input type="radio" value="add" checked={selectOption === 'add'} onChange={handleOptionChange} />Add</label>
<label> <input type="radio" value="edit" checked={selectOption === 'edit'} onChange={handleOptionChange} />Edit</label>
</div>
<br></br>
<div style={{ marginLeft: 50, marginRight: 50 }} >
<Accordion
title={
<div style={{ width: 1350 }}>
<h3>Details</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}
content={
<Details appID={id} data = {appById}></Details>
}
/>
<Accordion title={
<div style={{ width: 1350 }}>
<h3>Links</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}></Accordion>
<Accordion title={
<div style={{ width: 1350 }}>
<h3>Factory Management</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}></Accordion>
<Accordion title={
<div style={{ width: 1350 }}>
<h3>Issues Management</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}></Accordion>
<Accordion title={
<div style={{ width: 1350 }}>
<h3>Middleware Management</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}></Accordion>
</div>)
{selectOption === 'add' ? (
<div>
Add Mode
</div>
) : selectOption === 'view' ? (<div>View Mode</div>) : (<div>eidt</div>)}
</div>
</>
);
};
export default ApplicationManagement;
and the section where the details are kept on the ApplicationManagement page (My code is quite long, I just share the problem part.)
import React, { useState, useEffect } from 'react'
import axios from "axios";
import {
getResponsibleTeams,
getBusinessAreas
} from '../api/apiCalls'
const Details = (props) => {
const [rTeams, setrTeams] = useState([]);
const [bAreas, setbAreas] = useState([]);
const { data } = props;
useEffect(() => {
async function fetchData() {
const getrTeams = await getResponsibleTeams();
const getbAreas = await getBusinessAreas();
axios.all([getrTeams, getbAreas]).then(
axios.spread((...allData) => {
const allrTeams = allData[0].data;
const allbAreas = allData[1].data;
setrTeams(allrTeams);
setbAreas(allbAreas);
})
);
}
fetchData();
}, []);
return (
<div>
<div
style={{
float: "left",
width: 1350,
height: 340,
}}
>
<div className="form-group">
<label style={{ float: "left" }} htmlFor="appFullName">
Frontend:{" "}
</label>
<input
id="appFullName"
type="text"
class="form-control"
placeholder="dsfdsdsf"
value={data.frontend.frontend_name} // error here
//onChange={handleInputChange}
name="appShortCode"
style={{ width: 400, marginLeft: 150 }}
/>
</div>
</div>
</div>
)
}
export default Details;
Later I realized that using asynchronous functions caused some problems. I came up with a solution to this and the problem was solved.
Error Code Here :
<Accordion
title={
<div style={{ width: 1350 }}>
<h3>Details</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}
content={
<Details appID={id} data = {appById}></Details>
}
/>
and the solution to the problem
{appById &&
<Accordion
title={
<div style={{ width: 1350 }}>
<h3>Details</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}
content={
<Details appID={id} data = {appById}></Details>
}
/>}

Where can I place these react hooks?

I had some hooks that I was using in my render of the code below - and they were working until I added a few useEffects. I am unceretain where to put these hooks to make them work again though - now I get the error "rendered more hooks than last time" -
The hooks I am trying to use are FormatDate and BandIcon. FormatDate gets data from props.post and BandIcon gets data from props.band. Can you help me figure out where to put these hooks so they work again?
I tried using them in the first useEffect by calling the hook to set a new part of state to hold that value... but that didnt work either.
import React, { useState, useEffect } from 'react'
import { Card, CardTitle, Badge, Button, CardFooter } from 'reactstrap'
import BandIcon from '../../../../uitls/BandIcon'
import FormatDate from '../../../../uitls/FormatDate'
import { Modal, ModalBody } from 'reactstrap'
import ReactMapGL, { Marker } from 'react-map-gl';
import { useAuth0 } from "../../../../react-auth0-spa";
let shouldUpdate = 0
let shouldUpdateStatus = 0
let shouldUpdateFavorites = 0
export default function ShowsPosts(props) {
const { user } = useAuth0();
const { post, band, _id, addRockOn, addFavorites, favorites } = props
const [ day, setDay ] = useState('')
const [ client, setClient ] = useState()
const [ approved, setApproved ] = useState('')
const [customQuotes, setCustomQuotes] = useState([])
const [modal, setModal] = useState(false);
const [ perfromanceDate, setPerformanceDate ] = useState('')
const [ venueAddress, setVenueAddress ] = useState([])
const [ viewPort, setViewport ] = useState();
const [ total, setTotal ] = useState(0);
const [ baseCost, setBaseCost ] = useState(0);
const { getTokenSilently } = useAuth0();
const [isUser, setIsUser ] = useState(null)
const [ rocks, setRocks ] = useState([])
const [ renderIcon, setRenderIcon ] = useState('')
useEffect(() => {
//Required Gig Details
post.details.map(detail => {
if(detail.title === "Performance Date"){
setPerformanceDate(detail.detail)
}
if(detail.title === "Venue Address"){
setVenueAddress(detail.detail)
}
if(detail.title === 'Base Cost'){
setBaseCost(detail.money)
}
return null
})
//Client
setClient(post.client)
let slicedDay = new Date(post.details.map(detail => {
if (detail.title === "Performance Date"){
return detail.detail
}else{
return null
}
})).toDateString().slice(0,3)
//Create Day Icon
switch (slicedDay){
case 'Mon':
setDay('monday.svg')
break;
case 'Tue':
setDay('tuesday.svg')
break;
case 'Wed':
setDay('wednesday.svg')
break;
case 'Thu':
setDay('thursday.svg')
break;
case 'Fri':
setDay('friday.svg')
break;
case 'Sat':
setDay('saturday.svg')
break;
default:
console.log('switch didnt work')
}
//Gig Status
setApproved(post.approved)
//Add Custom Qutoes - I added the return in the first part
post.details.filter(detail => {
if(detail.title !== "Performance Date" || detail.title !== "Base Cost" || detail.title !== "Venue Address"){
return setCustomQuotes(quotes => [...quotes, detail])
}else {
return null
}
})
//Set Total
setTotal(post.total)
setRocks(post.rockOn)
}, [post])
useEffect(() => {
if(shouldUpdate >= 1){
setViewport({
latitude: post.details.filter(detail => {
if(detail.title === "Venue Address"){
return detail.detail[2][1]
}else {
return null
}
})[0].detail[2][0] ,
longitude: post.details.filter(detail => {
if(detail.title === "Venue Address"){
return detail.detail[2][1]
}else {
return null
}
})[0].detail[2][1],
width: '100%',
height: 100,
zoom: 12,
})
}else {
shouldUpdate += 1
}
}, [venueAddress, post.details])
const saveBookingStatus = async (newStatus) => {
const token = await getTokenSilently();
try {
await fetch(`/api/autoquotegenerators/posts/${_id}/${post.postId}`, {
method: 'PUT',
headers: {
"Content-Type": "application/json; charset=UTF-8",
Authorization: `bearer ${token}`
},
body: JSON.stringify(
{
"approved": newStatus
}
)
})
} catch (error) {
console.log(error)
}
}
useEffect(() => {
if(user.sub.slice(6, user.sub.length) === band.userId){
setIsUser(true)
}else{
setIsUser(false)
}
}, [band, user])
const toggle = () => setModal(!modal);
if( !post || !band || !user || !rocks || !approved ){
console.log(post)
console.log(band)
console.log(user)
console.log(rocks)
console.log(approved)
return <div>...loading</div>
}
const handleRockOn = () => {
if(rocks.includes(user.sub.slice(6, user.sub.length))){
setRocks(rocks => [...rocks.filter(rock => { return rock !== user.sub.slice(6, user.sub.length)})])
addRockOn(post.postId, rocks.filter(rock => { return rock !== user.sub.slice(6, user.sub.length)}), props._id)
}else {
setRocks(rocks => [...rocks, user.sub.slice(6, user.sub.length)])
addRockOn(post.postId, rocks.concat(user.sub.slice(6, user.sub.length)), props._id)
}
}
const handleFavorites = () => {
addFavorites(user.sub.slice(6, user.sub.length), band)
}
return (
<Card color="light" className="my-1 d-flex flex-column justify-content-start">
<span className="d-flex flex-row align-self-start mx-2 mt-2 mb-1">
<img onClick={()=> {window.location.href=`/band/${_id}`}} src={BandIcon(band.bandGenre)} alt="Band Icon" style={{
width: "30px",
height: "30px",
cursor: 'pointer'
}}/>
<span className="h6 mt-2 mx-1">
<strong onClick={()=> {window.location.href=`/band/${_id}`}} style={{cursor: 'pointer'}}>{band.bandName}</strong> - {FormatDate(post.date)}
</span>
</span>
<Button
color="light"
id="dateCardPost"
onClick={toggle}
className="d-flex flex-row align-self-start p-4 mx-5 mt-1 mb-3 w-75"
style={{cursor: "pointer",
pointerEvents: isUser ? "auto" : "none" }}
>
<img src={`/calendarIcons/${day}`}
className="mr-2"
alt=""
style={{
width: "85px",
height: "85px",
}}/>
<Badge color="primary" className="align-self-start mx-1 mt-1" style={{
display: approved === 'pending' ? "block" : "none"
}}>?</Badge>
<Badge color="danger" className="align-self-start mx-1 mt-1" style={{
display: approved === 'false' ? "block" : "none"
}}>X</Badge>
<Badge color="success" className="align-self-start mx-1 mt-1" style={{
display: approved === 'true' ? "block" : "none"
}}>✓</Badge>
<CardTitle className="d-flex flex-column mx-1 align-items-start">
<span><strong>{band.bandName}'s</strong> Show</span>
<span>on <strong>{perfromanceDate.slice(4)}</strong></span>
<span>at <strong>{venueAddress[1]} </strong></span>
<span>
is {approved === 'pending' ? 'un-confirmed!' : null}
{approved === 'false' ? 'cancelled!' : null}
{approved === 'true' ? 'confirmed!' : null}
</span>
</CardTitle>
</Button>
<Modal isOpen={modal} toggle={toggle}>
<ReactMapGL
style={{zIndex: "2"}}
{...viewPort}
mapboxApiAccessToken={"pk.eyJ1Ijoibmlja2lzeW91cmZhbiIsImEiOiJjazh5azhjNjUxNzIwM21zZmdoZTBoM243In0.4ijWndTzChYeKo67PYSuAw"}
onViewportChange={viewport => setViewport(viewport)}
mapStyle="mapbox://styles/nickisyourfan/ck8ylgfk90kcb1iqoemkes76u"
>
<Marker
latitude={post.details.filter(detail => {
if(detail.title === "Venue Address"){
return detail.detail[2][1]
}else {
return null
}
})[0].detail[2][0]}
longitude={post.details.filter(detail => {
if(detail.title === "Venue Address"){
return detail.detail[2][1]
}else {
return null
}
})[0].detail[2][1]}
offsetLeft={-25}
offsetTop={-20}>
<img src={BandIcon(band.bandGenre)} alt="Band Icon" style={{
width: "30px",
height: "30px",
}}/>
</Marker>
</ReactMapGL>
<ModalBody className="d-flex flex-column align-items-start">
<Card color="light" className="d-flex flex-column py-3 w-75 align-self-center justify-content-center align-items-center h5">{band.bandName}</Card>
<Card color="light" className="d-flex flex-column py-2 w-75 align-self-center justify-content-center align-items-center h6">
<span className="h6">{perfromanceDate}</span>
<span><strong>Venue: </strong>{venueAddress[1]}</span>
<span><strong>Address: </strong>{venueAddress[0]}</span>
</Card>
<Card color="light" className="d-flex flex-column py-2 w-75 align-self-center justify-content-center align-items-center h6">
<span><strong>Client: </strong>{client}</span>
<span><strong>Base Cost: </strong>${baseCost}</span>
{customQuotes.map(quote => {
if(quote.title !== 'Venue Address' || quote.title !== 'Base Cost' || quote.title !== 'Performance Date'){
return null
}else {
return <span key={quote.chargeId}><strong>{quote.title}: </strong>{quote.detail} ${quote.money}</span>
}
})}
</Card>
<Card color="light" className="d-flex flex-column py-2 w-75 align-self-center justify-content-center align-items-center h6">
<span><strong>Total: </strong>${total}</span>
</Card>
</ModalBody>
<Button
id="cancelBookingButton"
onMouseEnter={() => {
if(approved === 'true'){
document.getElementById('cancelBookingButton').innerHTML = 'Cancel Booking?'}
}}
onMouseLeave={() => {
if(approved === 'true'){
document.getElementById('cancelBookingButton').innerHTML = 'Booking Approved'
}
}}
onClick={() => {
setApproved('false')
saveBookingStatus('false')
}}
className="w-100 rounded-0"
color={approved === 'pending' ? 'danger' : 'success'}
style={{
display: approved === 'pending' || approved === 'true' ? 'block' : 'none',
}}>
{approved === 'pending' ? 'Decline Booking' : 'Booking Approved'}
</Button>
<Button
id="approveBookingButton"
onMouseEnter={() => {
if(approved === 'false'){
document.getElementById('approveBookingButton').innerHTML = 'Re-Confirm Booking?'
}
}}
onMouseLeave={() => {
if(approved === 'false'){
document.getElementById('approveBookingButton').innerHTML = 'Booking Cancelled'
}
}}
onClick={() => {
setApproved('approved')
saveBookingStatus('approved')
}}
className="w-100 rounded-0"
color={approved === 'false' ? 'danger' : 'success'}
style={{
display: approved === 'false' || approved === 'pending' ? 'block' : 'none',
}}>
{approved === 'pending' ? 'Approve Booking' : 'Booking Cancelled'}
</Button>
</Modal>
<CardFooter className="d-flex flex-row justfiy-content-between p-0">
<Button onClick={handleRockOn} color={rocks.includes(user.sub.slice(6, user.sub.length)) ? 'light active' : 'light'} className="d-flex w-50 h-100 rounded-0 justify-content-center shadow-none" >
<img src='/Rock-On.svg' style={{width: '27px', height: '27px'}} className="mr-2"/>
<span className="ml-2">{rocks.length}</span>
</Button>
<Button onClick={handleFavorites} color={favorites.includes(user.sub.slice(6, user.sub.length)) ? 'light active' : 'light'} className='w-50 h-100 rounded-0' style={{boxShadow: 'none'}}>
<img src='/FavoriteBand.svg' style={{width: '25px', height: '25px'}} />
</Button>
</CardFooter>
</Card>
)
}
It seems you have lots of logic inside one component. I'll suggest beginning with splitting pieces of related logic into custom hooks in separate so you have less code inside your components and can you can find where are the errors while you are extracting them.
Implementation example:
function useViewport(venueAddress, postDetails) {
const [viewPort, setViewport] = useState();
useEffect(() => {
if (shouldUpdate >= 1) {
setViewport({
latitude: post.details.filter((detail) => {
if (detail.title === 'Venue Address') {
return detail.detail[2][1];
} else {
return null;
}
})[0].detail[2][0],
longitude: post.details.filter((detail) => {
if (detail.title === 'Venue Address') {
return detail.detail[2][1];
} else {
return null;
}
})[0].detail[2][1],
width: '100%',
height: 100,
zoom: 12,
});
} else {
shouldUpdate += 1;
}
}, [venueAddress, postDetails]);
return viewPort;
}
Inside your ShowsPosts component:
useViewport(venueAddress, post.details);

Resources