Re-render the same component with different parameter - javascript

I am working on a react project with similar functionality to a social media site. I have a user profile component below that has links to other user profiles(towards bottom of code below) that are being followed by the current user. Essentially what I am trying to do is re-render the same component with a different user. However, the link to the new user profile doesn't result in the getProfile action getting called so the redux state doesn't update. How can I navigate to the new profile and have the useEffect hook get called again with the new username?
Thank You!
import React, { useEffect, useState } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import GalleryItem from "../gallery-item/gallery-item.component";
import {
getProfile,
addFollow,
removeFollow
} from "../../redux/profile/profile.actions";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faSpinner, faUserEdit } from "#fortawesome/free-solid-svg-icons";
const Profile = ({
getProfile,
addFollow,
removeFollow,
auth,
profile: { userProfile, loading },
props
}) => {
const userAlias = props.match.params.alias;
useEffect(() => {
getProfile(userAlias);
}, [getProfile]);
return loading ? (
<div class="d-flex justify-content-center">
<FontAwesomeIcon
icon={faSpinner}
className="fa-spin"
style={{ height: "50px", width: "50px", color: "white" }}
/>
</div>
) : (
<div className="container ">
<div className="row">
<div className="col-md-12 align-self-center">
<img
className="rounded-circle float-left shadow-lg"
alt="100x100"
src={userProfile.userProfileImage}
data-holder-rendered="true"
style={{ height: "200px", width: "200px" }}
/>
<div
className="vertical-center"
style={{ marginLeft: "260px", marginTop: "50px" }}
>
<h2>
{auth.user === null || auth.user.alias !== userAlias ? (
<b style={{ color: "white" }}>{userAlias}</b>
) : auth.user.alias === userAlias ? (
<b style={{ color: "white" }}>
{userAlias}
<Link className="" to="/profile/edit">
<FontAwesomeIcon
icon={faUserEdit}
className="fontAwesome"
style={{ paddingLeft: "10px", color: "limegreen" }}
/>
</Link>
</b>
) : (
<b>{userAlias}</b>
)}
</h2>
<p>
<b style={{ color: "white" }}>
{" "}
{userProfile.posts.length} Posts
</b>
<b style={{ color: "white" }}>
{" "}
{userProfile.followers.length} Followers{" "}
</b>
<b style={{ color: "white" }}>
{" "}
{userProfile.following.length} Following{" "}
</b>
</p>
{auth.user === null || auth.user.alias === userAlias ? null : auth
.user.alias !== userAlias &&
userProfile.followers.some(
e => e.userAlias === auth.user.alias
) ? (
<button
type="button"
className="btn btn-primary btn-lg "
onClick={e => removeFollow(userProfile.userId)}
>
Following
</button>
) : auth.user.alias !== userAlias ? (
<button
type="button"
className="btn btn-primary btn-lg "
onClick={e => addFollow(userProfile.userId)}
>
Not Following
</button>
) : (
<button
type="button"
className="btn btn-primary btn-lg "
disabled
>
Follow/Unfollow
</button>
)}
</div>
</div>
</div>
<hr />
<div className="row d-flex justify-content-center">
{userProfile.posts.map((post, i) => (
<GalleryItem key={i} post={post} />
))}
</div>
{userProfile.following.length > 0 ? (
<div>
<h1 className="text-white text-center mt-5">Following</h1>
<hr />
**<div className="row justify-content-center text-center px-auto">
{userProfile.following.map((profile, i) => (
<Link className="" to={`/profile/${profile.userAlias}`}>
<img
className="rounded-circle border border-info m-4"
alt="100x100"
src={profile.getFollowingUserProfileImageUrl}
data-holder-rendered="true"
style={{ height: "80px", width: "80px" }}
/>
<div
className="vertical-center"
style={{ marginBottom: "20px", color: "black" }}
>
<h5>{profile.userAlias}</h5>
</div>
</Link>
))}
</div>**
</div>
) :(<div></div>)}
</div>
);
};
Profile.propTypes = {
getProfile: PropTypes.func.isRequired,
profile: PropTypes.object.isRequired,
auth: PropTypes.object.isRequired,
props: PropTypes.object.isRequired
};
const mapStateToProps = (state, ownProps) => ({
auth: state.auth,
profile: state.profile,
props: ownProps
});
export default connect(
mapStateToProps,
{ getProfile, addFollow, removeFollow }
)(Profile);

You need to track userAlias in the useEffect's dependencies list, the api callback will be called each time it changes.
const userAlias = props.match.params.alias;
useEffect(() => {
getProfile(userAlias);
}, [getProfile, userAlias]);

Related

I am trying to use a global state through the Context API but it does not read me a "Theme" state that I have created

it gives me this error
App.js:14 Uncaught TypeError: Cannot destructure property 'theme' of '(0 , react__WEBPACK_IMPORTED_MODULE_8__.useContext)(...)' as it is undefined
[IN CART.JS I CAN EASILY TAKE MY ADDTOCART STATE, BUT IN APP.JS IT DOESN'T LET ME TAKE THE "THEME" STATE AND I DON'T KNOW WHAT WILL BE THE REASON]
[CARTCONTEXT.JS]
import React, { createContext, useState } from "react";
export const CarritoContext = createContext();
export default function CartContext({ children }) {
const [addToCarrito, setAddToCarrito] = useState([]);
const [theme, setTheme] = useState("light");
async function addItem(item, quantity) {
const foundProdIndex = await addToCarrito.findIndex(
(product) => product.item.id === item.id
);
if (foundProdIndex === -1) {
setAddToCarrito([...addToCarrito, { item, quantity }]);
}
if (quantity === 0) {
setAddToCarrito([...addToCarrito]);
}
}
function clear() {
setAddToCarrito([]);
}
function removeItem(itemId) {
const newItems = addToCarrito.filter((item) => item.item.id !== itemId);
setAddToCarrito(newItems);
}
const toggleTheme = () => {
setTheme((prevTheme) => (prevTheme === "light" ? "dark" : "light"));
};
return (
<>
<CarritoContext.Provider
value={{
addToCarrito,
setAddToCarrito,
clear,
addItem,
removeItem,
theme,
toggleTheme,
}}
>
{children}
</CarritoContext.Provider>
</>
);
}
[APP.JS]
import "./App.scss";
import ItemDetailContainer from "./components/ItemDetailContainer/ItemDetailContainer";
import ItemListContainer from "./components/ItemListContainer/ItemListContainer";
import Navbar from "./components/Navbar/Navbar";
import { Routes, Route } from "react-router-dom";
import ItemList from "./components/ItemList/ItemList";
import NotFound404 from "./components/NotFound404/NotFound404";
import Cart from "./components/Cart/Cart";
import CartContext, { CarritoContext } from "./context/CartContext";
import React, { useContext } from "react";
function App() {
let { theme } = useContext(CarritoContext);
return (
<>
<CartContext>
<div className="App" id={theme}>
<Navbar />
<Routes>
<Route path="/carvel-ecommerce" element={<ItemListContainer />} />
<Route
path="/carvel-ecommerce/category/:id"
element={<ItemList />}
/>
<Route
path="/carvel-ecommerce/item/:id"
element={<ItemDetailContainer />}
/>
<Route path="/carvel-ecommerce/cart" element={<Cart />} />
<Route path="*" element={<NotFound404 />} />
</Routes>
</div>
</CartContext>
</>
);
}
export default App;
[CART.JS] (HERE THE CONTEXT WORKS FINE]
import React, { useContext } from "react";
import { Link } from "react-router-dom";
import { CarritoContext } from "../../context/CartContext";
import "./Cart.scss";
import { BsTrash } from "react-icons/bs";
import { MdOutlineCleaningServices } from "react-icons/md";
import { motion } from "framer-motion";
import { v4 as uuidv4 } from "uuid";
import { ReactComponent as Womens } from "../../assets/women-coat-svgrepo-com.svg";
import { ReactComponent as Hats } from "../../assets/accesory-hats-svgrepo-com.svg";
import { ReactComponent as Glasses } from "../../assets/glasses-svgrepo-com.svg";
import { ReactComponent as Jackets } from "../../assets/jacket-svgrepo-com.svg";
const Cart = () => {
let { addToCarrito } = useContext(CarritoContext);
let { clear } = useContext(CarritoContext);
let { removeItem } = useContext(CarritoContext);
const variants = {
hidden: { opacity: 0 },
visible: { opacity: 1 },
};
const totalPrice = addToCarrito
.map((product) => {
return product.item.price * product.quantity;
})
.reduce((item, qty) => item + qty, 0);
const pickData = addToCarrito.map((producto) => {
return (
<p key={uuidv4()}>
<small>
{producto.item.name}({producto.quantity}) :
<strong> ${producto.item.price * producto.quantity}</strong>
</small>
</p>
);
});
return (
<>
{addToCarrito.length > 0 ? (
addToCarrito.map((producto) => {
return (
<motion.div
initial="hidden"
animate="visible"
variants={variants}
key={uuidv4()}
>
<motion.div
initial="hidden"
animate="visible"
variants={variants}
className="container"
>
<motion.section
initial="hidden"
animate="visible"
variants={variants}
id="cart"
>
<motion.article
initial="hidden"
animate="visible"
variants={variants}
className="product"
>
<header>
<img
src={producto.item.imageUrl}
alt=""
style={{
backgroundColor: "white",
width: "100%",
height: "100%",
objectFit: "contain",
}}
/>
</header>
<div className="content">
<h1>
{producto.item.name}
<BsTrash
onClick={() => removeItem(producto.item.id)}
className="basurita"
/>
</h1>
{producto.item.category} | {producto.item.color}
<h5 style={{ marginTop: "10px" }}>
<span>
<small className="initial-price">
<span
style={{
backgroundColor: "#00640a",
height: "120px",
}}
>
     
</span>{" "}
Initial price{" "}
</small>
</span>
</h5>
<h5>
<span>
<small className="final-price">
<span
style={{
height: "120px",
backgroundColor: "yellow",
}}
>
   
</span>
<span
style={{
height: "120px",
backgroundColor: "black",
}}
>
   
</span>{" "}
Final price{" "}
</small>
</span>
</h5>
</div>
<footer className="content">
<span className="qt">
<strong>Qty: </strong> {producto.quantity}
</span>
<h2 className="full-price">
${producto.item.price * producto.quantity}
</h2>
<h2 className="price">${producto.item.price}</h2>
</footer>
</motion.article>
</motion.section>
</motion.div>
</motion.div>
);
})
) : (
<>
<h1
style={{
textAlign: "center",
fontSize: "42px",
borderBottom: "2px solid #ececec",
}}
>
The cart is empty... Start enjoying our products and visit us again
</h1>
<Link to={`/carvel-ecommerce`} className="button-backhome">
GO BACK TO HOME
</Link>
<div
style={{
display: "flex",
textDecoration: "none",
flexDirection: "column",
alignItems: "flex-end",
}}
>
<Link
to={"/carvel-ecommerce/category/Glasses"}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
}}
>
<span>Glasses</span>{" "}
<span>
<Glasses className="iconpower" />
</span>
</Link>
<Link
to={"/carvel-ecommerce/category/Hats"}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
}}
>
<span>Hats</span>{" "}
<span>
<Hats className="iconpower" />
</span>
</Link>
<Link
to={"/carvel-ecommerce/category/Jackets"}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
}}
>
<span>Jackets</span>{" "}
<span>
<Jackets className="iconpower" />
</span>
</Link>
<Link
to={"/carvel-ecommerce/category/Womens"}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
}}
>
<span>Womens</span>{" "}
<span>
<Womens className="iconpower" />
</span>
</Link>
{/* <Link
to={"/carvel-ecommerce/category/Mens"}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
}}
>
<span>Mens</span>{" "}
<span>
<Mens className="iconpower" />
</span>
</Link> */}
</div>
</>
)}
{addToCarrito.length > 0 ? (
<footer id="site-footer">
<div className="container clearfix">
<div className="right">
{" "}
{pickData}
<h1 className="total">
Total: <span>${totalPrice} </span>
</h1>
<Link to={"/checkout"} className="btn">
Checkout
</Link>
<MdOutlineCleaningServices
onClick={() => clear()}
className="cleared"
/>
</div>
</div>
</footer>
) : (
""
)}
</>
);
};
export default Cart;

Select only a card at a time on click in reactjs

I have a react component which has some cards, When I click the plus icon in the card, it would expand and show some data for 30sec and then the data will disappear and on click it will reappear again, here is the component
import React from "react";
import { FaPlus } from "react-icons/fa";
import useTimeout from "../../../common/useTimeout";
import { Modal } from "../../../common/Modal";
import { ToggleState } from "../../../common/Toggle";
import { BsSearch } from "react-icons/bs";
import AdvancedFilter from "./AdvancedFilter";
export default function CitiesList({ cities }: { cities: any }): JSX.Element {
const [filter, setFilter] = React.useState("");
const [sortType, setSortType] = React.useState("");
const [selectedCity, setSelectedCity] = React.useState<any | null>(null);
console.log(filter);
const sorted = cities.sort((a: { name: string }, b: { name: any }) => {
const isReversed = sortType === "asc" ? 1 : -1;
return isReversed * a.name.localeCompare(b.name);
});
const onSort = (sortType: React.SetStateAction<string>) => {
console.log("changed");
setSortType(sortType);
};
const [showMeta, setShowMeta] = React.useState(false);
const handleClick = () => setShowMeta(true);
const getSelectedCity = (selectedCity: any) => {
setSelectedCity(selectedCity);
console.log("SELECTED CITY", selectedCity);
};
const [visible, setVisible] = React.useState(true);
const hide = () => setVisible(false);
useTimeout(hide, 30000);
console.log("CITIES", cities);
console.log({ selectedCity });
return (
<div style={{ marginTop: "3rem" }}>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "20px",
}}
>
<div>List of cities</div>
<div style={{ display: "flex", alignItems: "center" }}>
<div style={{ marginRight: "1rem" }}>
<ToggleState
render={({ isOpen, open, close }) => {
return (
<>
<button
type="button"
className="btn btn-primary"
onClick={() => {
isOpen ? close() : open();
}}
>
Advanced Filter
</button>
<Modal
isActive={isOpen}
modalContentWidth={"30%"}
header={() => "Advanced Filter"}
close={() => close()}
renderBody={() => {
return <AdvancedFilter close={() => close()} />;
}}
></Modal>
</>
);
}}
/>
</div>
<div style={{ position: "relative", marginRight: "1rem" }}>
<input
type="text"
placeholder="Filter"
name="namePrefix"
style={{ padding: "0.35rem" }}
onChange={(e: any) => {
setFilter(e.target.value);
}}
/>
<div style={{ position: "absolute", top: "5px", right: "5px" }}>
<BsSearch size="16" />
</div>
</div>
<div style={{ width: "8rem" }}>
<div className="btn-group">
<button
type="button"
className="btn dropdown-toggle sort-button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
{sortType === "asc"
? "Ascending"
: sortType === "desc"
? "Descending"
: "Select"}
</button>
<ul className="dropdown-menu sort-button">
<li>
<button
className="dropdown-item"
type="button"
onClick={() => onSort("asc")}
>
Ascending
</button>
</li>
<li>
<button
className="dropdown-item"
type="button"
onClick={() => onSort("desc")}
>
Descending
</button>
</li>
</ul>
</div>
</div>
</div>
</div>
<div>
<div>
<div className="row">
{cities &&
sorted.map((item: any, index: number) => (
<div className="col-lg-3" key={index}>
<div
className="card"
style={{
textAlign: "center",
display: "flex",
justifyContent: "center",
paddingBottom: "1rem",
marginBottom: "1rem",
marginRight: "1rem",
}}
>
<div className="card-body">
<h5 className="card-title">{item.name}</h5>
</div>
{visible && showMeta ? (
<div>
<p>Longitude: {item.longitude}</p>
<p>Latitude: {item.latitude}</p>
<p>Population: {item.population}</p>
{/* <p>Time Zone: America</p> */}
</div>
) : (
<div
onClick={() => {
handleClick();
getSelectedCity(item.id);
}}
style={{ cursor: "pointer" }}
>
<FaPlus size="18" />
</div>
)}
</div>
</div>
))}
</div>
</div>
</div>
<div
style={{ marginTop: "30px", display: "flex", justifyContent: "center" }}
>
{cities && cities.length > 10 ? (
<button className="secondary-button">Load More</button>
) : (
<p>There are no more cities</p>
)}
</div>
</div>
);
}
here is the useTimeout function
import { useEffect, useRef } from "react";
function useTimeout(callback: () => void, delay: number | null) {
const savedCallback = useRef(callback);
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
if (delay === null) {
return;
}
const id = setTimeout(() => savedCallback.current(), delay);
return () => clearTimeout(id);
}, [delay]);
}
export default useTimeout;
Now currently if I click on one card, all the cards opens and also after when the data disappears after 30sec it does not reappear on button click. I need to reload the page to do reappear data again.I need to solve 2 issues here: 1. how can I open one card only at a time on clicking on the icon, 2. how can I reappear data on button click without refreshing the page.
As I understand you, some information is
<p>Longitude: {item.longitude}</p>
<p>Latitude: {item.latitude}</p>
<p>Population: {item.population}</p>
You have global statement of showing this info for all cards. To reduce it, make new component which will have local state of this info:
import React from 'react'
const timeout = 15000
export const CityCard = ({item, visible, getSelectedCity}) => {
const [showMeta, setShowMeta] = React.useState(false)
const handleClick = React.useCallback(()=>{
setShowMeta(true)
setTimeout(()=>{
setShowMeta(false)
},timeout )
}, [showMeta])
return(
<div className="col-lg-3" key={index}>
<div
className="card"
style={{
textAlign: "center",
display: "flex",
justifyContent: "center",
paddingBottom: "1rem",
marginBottom: "1rem",
marginRight: "1rem",
}}
>
<div className="card-body">
<h5 className="card-title">{item.name}</h5>
</div>
{visible && showMeta ? (
<div>
<p>Longitude: {item.longitude}</p>
<p>Latitude: {item.latitude}</p>
<p>Population: {item.population}</p>
{/* <p>Time Zone: America</p> */}
</div>
) : (
<button
onClick={() => {
handleClick();
getSelectedCity(item.id);
}}
type='button'
disabled={showMeta}
style={{ cursor: "pointer" }}
>
<FaPlus size="18" />
</button>
)}
</div>
</div>
)
}
At the end, add this component to the CityList

How can i use useParams() with an onClick button to navigate to different pages and display the data in react.js?

I'm working on a billing app. I have managed to fetch the Products from an API..... But now I'm trying to use useParams() to navigate to random pages that would display the items according to the ID by pressing a button...the navigation works fine but it wont display the data of that passed ID, it displays all the data in my API.
I would really appreciate some help or feedback, Thanks !
Item.js:
import React, { useEffect } from "react";
import { connect } from "react-redux";
import { getItems } from "../store/actions/itemsActions";
import { Link } from "react-router-dom";
import "./Items.css";
function Items({ getItems, items }) {
useEffect(() => {
getItems();
}, []);
function getRandomElFromArray(items) {
return Math.floor(Math.random() * items);
}
return (
<div>
<div className="container">
<div
className="image"
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: 200,
width: 100,
}}
>
<img src="http://i.stack.imgur.com/yZlqh.png" alt="" />
</div>
</div>
<div>
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
<div className="item-preview">
{items &&
items.items &&
items.items.map((item) => (
<div key={item.id}>
<h4>
ID:<Link to={`/bills/${item.id}`}> {item.id}
<button className="button4">Analyse Receipt</button>
</Link>
</h4>
</div>
))}
</div>
</div>
</div>
</div>
);
}
const mapStateToProps = (state) => {
return {
items: state.items,
};
};
const mapDispatchToProps = (dispatch) => {
return {
getItems: () => dispatch(getItems()),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Items);
Billing.js:
import React, { useState } from "react";
import "./Bill.css";
import { Link, useParams, Switch, Route } from "react-router-dom";
import { connect } from "react-redux";
import { getItems } from "../store/actions/itemsActions";
function BillList({ items }) {
const [counter, setCounter] = useState(1);
const { id } = useParams();
function Display(props) {
return <label style={{ marginLeft: ".5rem" }}>{props.message}</label>;
}
return (
<div className="bills">
<div className="explore-container">
{items &&
items.items &&
items.items.filter((item) => item.id === id)
.map((item) => (
<div className="item-list" key={item.id}>
<h2>Title: {item.title}</h2>
<h4>price: {item.price}</h4>
</div>
))}
</div>
<div
className="main-title"
style={{
textAlign: "center",
justifyContent: "center",
alignItems: "center",
fontSize: 14,
}}
>
<h1>Bakery</h1>
<h1>Company Gbr</h1>
<h1>Oranienburger Straße 120</h1>
<h1>10119 Berlin</h1>
</div>
<div className="bills-container">
<div></div>
{/* pass in the details */}
<div className="item-list">
{items &&
items.items &&
items.items.map((item) => (
<React.Fragment key={item.id}>
<div className="bill-time">
<div className="bill">
<h4>
{" "}
<strong>Bill: </strong>
{item.billNumber}
</h4>
</div>
<div className="time">
<h4>
{" "}
<strong>Time: </strong>
{item.created_datetime}
</h4>
</div>
</div>
----------------------------------
----------------------------------
---------------------------------- --------------------
{/* Counter */}
<div className="price-total">
<div className="title">
<h3>
{" "}
<strong>Title: </strong>
{item.title}
</h3>
<div className="counter">
<strong>
<Display message={counter} />x
</strong>
</div>
</div>
<div className="increase">
<button onClick={() => setCounter(counter + 1)}>+</button>
</div>
<div className="decrease">
<button onClick={() => setCounter(counter - 1)}>-</button>
</div>
{/* Price and total */}
<div className="price">
<h4>
<strong>Price: {parseFloat(item.price)}€</strong>
</h4>
</div>
<div className="total">
<h4>Total: {parseFloat(item.price * counter)}€</h4>
</div>
</div>
</React.Fragment>
))}
</div>
{/* <div>
<h4>
Table: Counter
Terminal:
Ust-Id: DE11111111</h4>
</div> */}
</div>
<div className="button-path">
<Link to="/items">
<div className="button">
<button className="main-button">Analyse Receipt</button>
</div>
</Link>
</div>
<Switch>
<Route path="/bills/:id" />
</Switch>
</div>
);
}
const mapStateToProps = (state) => {
return {
items: state.items,
};
};
const mapDispatchToProps = (dispatch) => {
return {
getItems: () => dispatch(getItems()),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(BillList);
The ":" of path="/bills/:id" is only used to designate id as a route parameter, it isn't meant to be part of any URL. When you link to to={`/bills/:${items.id}`} you are adding ":" to the id string value.
Remove it from the link. You should also ensure that the Items component has items to map, where each specific item mapped renders the link/button to link to the appropriate bill list page.
<Link to={`/bills/${item.id}`}>
<button className="button4">Analyse Receipt</button>
</Link>
The Items route also doesn't specify any route params, so there will be be none in the `Items component.
<Route exact path="/" component={Items} />

Pages only showing after manual refresh (React)

I have just started updating some class components into functional components but now when navigating through to other pages (using ) the page will only display after an initial refresh.
Below is an example
This is a component I have updated to use the useState() hook.
function AdminWorkstationss({ initialCount }) {
const [WSAHeaders, setWSAHeaders] = useState([{}]);
const [currentPage, setPage] = useState(1);
const [WSAPerPage, setWSA] = useState(10);
const [pageNumbers, createPageNumber] = useState([]);
const [loadingToken, setLoadingToken] = useState(null);
const indexOfLastTodo = currentPage * WSAPerPage;
const indexOfFirstTodo = indexOfLastTodo - WSAPerPage;
const currentTodos = WSAHeaders.slice(indexOfFirstTodo, indexOfLastTodo);
// const pageNumbers = [];
useEffect(async () => {
setLoadingToken(true);
let recordset = await fetch(`/admin-completed-workstations`);
let results = await recordset.json();
setWSAHeaders(results.recordset);
var pNumbers = [];
for (
let i = 1;
i <= Math.ceil(results.recordset.length / WSAPerPage);
i++
) {
// pageNumbers.push(i);
pNumbers.push(i);
}
createPageNumber(pNumbers);
setLoadingToken(false);
}, []);
function handleClick(event) {
setPage(Number(event.target.id));
}
if (!loadingToken) {
return (
<>
<Fade>
<Slide left>
<h2 style={{ textAlign: "center" }}>
Workstation Assessments(<b> Completed</b>)
</h2>
</Slide>
</Fade>
<ul>
<button disabled className="btn btn-secondary">
Workstation Assessments
</button>
<Link to="./admin-center">
<button className="btn btn-secondary">Edit Questions</button>
</Link>
<Link to="./admin-center-view-users">
<button className="btn btn-secondary">View Users</button>
</Link>
<DropdownButton
style={{ float: "right" }}
id="dropdown-basic-button"
title="WSA's Per Page"
>
<Dropdown.Item onClick={() => setWSA(10)}>10</Dropdown.Item>
<Dropdown.Item onClick={() => setWSA(20)}>20</Dropdown.Item>
<Dropdown.Item onClick={() => setWSA(40)}>40</Dropdown.Item>
<Dropdown.Item onClick={() => setWSA(100)}>100</Dropdown.Item>
</DropdownButton>{" "}
<DropdownButton
style={{ float: "right" }}
id="dropdown-basic-button"
title="Completed"
>
//HERE. This button in drop down takes me to the correct page but just requires a refresh or it will not load.
<Dropdown.Item>
{" "}
<Link to="admin-view-workstation-assessments-declined">
In Progress
</Link>
</Dropdown.Item>
</DropdownButton>{" "}
</ul>
{currentTodos.map(number => (
<ul>
{" "}
<div className="jumbotron">
//Mapping child component
<Questions
workStation={number.AssignedWorkstation}
date={number.Date}
completeToken={number.QuestionStatus}
RUId={number.RUId}
WSAId={number.WSAId}
></Questions>
</div>
</ul>
))}
<div style={{ alignContent: "center", width: "10%" }}></div>
<div style={{ textAlign: "center", alignContent: "center" }}>
{" "}
<b> Current Page </b>: {currentPage}
<br />
<div>
{pageNumbers.map(number => (
<button
className="btn btn-primary"
key={number}
id={number}
onClick={handleClick}
>
{number}
</button>
))}
</div>
</div>
<br />
</>
);
} else if (loadingToken) {
return (
<>
<ul>
<button disabled className="btn btn-secondary">
Workstation Assessments
</button>
<Link to="./admin-center">
<button className="btn btn-secondary">Edit Questions</button>
</Link>
<Link to="./admin-center-view-users">
<button className="btn btn-secondary">View Users</button>
</Link>
<DropdownButton
style={{ float: "right" }}
id="dropdown-basic-button"
title="Completed"
>
<Dropdown.Item>
{" "}
<Link to="admin-view-workstation-assessments-declined">
In Progress
</Link>
</Dropdown.Item>
</DropdownButton>{" "}
</ul>
<h3 style={{ textAlign: "center" }}>LOADING</h3>
</>
);
}
}
I then have a child component of this one (this is within the .map) called questions. This is still a class component will this be the issue?
class Questions extends React.Component {
constructor(props) {
super(props);
console.log(props);
this.state = {
...props,
questionsAccepted: [],
questionsAcceptedCounter: "",
selectedSet: [],
ViewActivityToken: false,
noteToBeAdded: "",
notesFromDB: [],
addNoteToken: false,
answeredQuestions: []
};
}
render() {
if (!this.state.ViewActivity) {
if (!this.state.viewDetails && !this.state.ViewActivityToken) {
console.log(moment.locale());
return (
<div>
<ModalCompletedQuestions
RUId={this.props.RUId}
workStation={this.props.workStation}
WSAId={this.props.WSAId}
/>
// This Link is a link to another page but this page also needs a refresh before it is visible.
<Link
to={{
pathname: "/admin-view-full-user-wsa-responses",
state: {
WSAId: this.props.WSAId
}
}}
>
<button style={{ float: "right" }} className="btn btn-primary">
View Full Details
</button>
</Link>
<br />
<li>
<b>User Id: </b>
{this.props.RUId}
</li>
<li>
<b>Workstation: </b>
{this.props.workStation}
</li>
<li>
<b>Date: </b>
{moment(this.props.date).format("L")}
</li>
<li>
<b>Complete Token: </b>
{this.props.completeToken}
</li>
</div>
);
} else if (this.state.viewDetails && !this.state.ViewActivityToken) {
return (
<div>
<button
style={{ float: "right" }}
onClick={e =>
this.setState({
ViewActivity: false,
viewDetails: false,
ViewActivityToken: false,
addNoteToken: false
})
}
className="btn btn-secondary"
>
Revert
</button>
<br />
<br />
{this.state.selectedSet &&
this.state.selectedSet.map((item, index) => {
return (
<div>
<li>
{" "}
<b>{item.QuestionWhenAnswered}</b>{" "}
</li>
<li>{item.QuestionResponse}</li>
<li>{item.Accepted}</li>
</div>
);
})}
</div>
);
}
} else if (this.state.ViewActivity && !this.state.addNoteToken) {
return (
<>
<button
style={{ float: "right" }}
onClick={e =>
this.setState({
ViewActivity: false,
viewDetails: false,
ViewActivityToken: false,
addNoteToken: false
})
}
className="btn btn-secondary"
>
Revert
</button>
<br />
<li>
<b>User Id: </b>
{this.props.RUId}
</li>
<li>
<b>Workstation: </b>
{this.props.workStation}
</li>
<li>
<b>Date: </b>
{moment(this.props.date).format("DD/MM/YYYY")}
</li>
<li>
<b>Complete Token: </b>
{this.props.completeToken}
</li>
{this.state.notesFromDB &&
this.state.notesFromDB.map((item, index) => {
return (
<div
style={{
backgroundColor: "white",
border: "inset",
borderWidth: "0.2px"
}}
>
<div style={{ float: "right" }}>
{moment(item.CreationTime).format("HH:MM DD/MM/YYYY ")}
</div>
<div>
<b>{`${item.UserStatus} `}</b>
</div>
<div style={{ textAlign: "left" }}>{item.Notes}</div>
</div>
);
})}
<br />
<button
onClick={this.AddNoteBtn}
className="btn btn-primary"
style={{ width: "100%" }}
>
Add Note
</button>
</>
);
}
}
}
Why are these pages not loading automatically and why would they need a refresh to laod? Is there any chance it is because I am using class components along with functional components? Any help is much appreciated.
This was due to me making useEffect async when this was changed so that the use effect was not using async everything worked fine again.

First modal on each page not displaying data

I am making a workstation assessment website.
I am stuck with an issue I am having.
The modal is my a grandchild component (modal complete questions). I then have a component named questions as a parent and as the parent to that I have Admin Workstations.
Hierachy
1.AdminWorkstations,
2.Questions,
3.Modal,
(this is not full functionality of these components but is just for the use case I am asking for).
1.Parent gets WSAId(just a id).Passes down to questions.
2.Questions passes the modal component this.
3.Modal gets questions using this id.
However the first modal does not display.I have paginated the results of these page if this makes any diffrence.
this is my modal
import "./ViewWorkstationModal.css";
import React, { useState, useEffect } from "react";
import { Modal, DropdownButton, Dropdown } from "react-bootstrap";
function ModalCompletedQuestions(props) {
const [show, setShowState] = useState(0);
const [loadingToken, setLoadingToken] = useState(0);
const [answeredQuestions, setAnsweredQuestions] = useState([{}]);
useEffect(() => {
setLoadingToken(true);
let data = {
WSAId: props.WSAId
};
fetch("/get-completed-questions", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(data)
})
.then(recordset => recordset.json())
.then(results => {
setAnsweredQuestions(results.recordset);
});
}, []);
function handleClose() {
setShowState(false);
}
function handleShow() {
setShowState(true);
}
return (
<>
<>
<div className="header-container">
<button
className="btn btn-primary"
style={{ float: "right" }}
onClick={handleShow}
>
Response Overview
</button>
</div>
<div>
<Modal
size="lg"
style={{ width: "100%" }}
show={show}
onHide={handleClose}
animation={true}
>
<h3 style={{ textAlign: "center" }}>{props.workStation}</h3>
{answeredQuestions &&
answeredQuestions.map(function(question, index) {
if (
question.QuestionResponse === "Y" ||
question.QuestionResponse === "N"
) {
return (
<>
<div
style={{
backgroundColor: "#E6E6E6",
padding: "1px"
}}
>
<ul>
{" "}
<b> Q :</b>
<div style={{ float: "right" }}>✔️</div>
{question.QuestionWhenAnswered}
</ul>
</div>
</>
);
} else if (question.QuestionResponse === "P") {
return (
<>
<div
style={{
backgroundColor: "#BDBDBD",
padding: "1px"
}}
>
<ul>
<b> Q :</b>
{question.QuestionWhenAnswered}{" "}
<div style={{ float: "right" }}>❌</div>
{/* <br />
<b> S :</b>
{question.SuggestedSoloution} */}
</ul>
</div>
</>
);
}
})}
</Modal>
</div>
</>
</>
);
}
this is my questions component
class Questions extends React.Component {
constructor(props) {
super(props);
console.log(props);
this.state = {
...props,
questionsAccepted: [],
questionsAcceptedCounter: "",
selectedSet: [],
ViewActivityToken: false,
noteToBeAdded: "",
notesFromDB: [],
addNoteToken: false,
answeredQuestions: []
};
}
render() {
if (!this.state.ViewActivity) {
if (!this.state.viewDetails && !this.state.ViewActivityToken) {
console.log(moment.locale());
return (
<div>
<ModalCompletedQuestions
RUId={this.props.RUId}
workStation={this.props.workStation}
WSAId={this.props.WSAId}
/>
<Link
to={{
pathname: "/admin-view-full-user-wsa-responses",
state: {
WSAId: this.props.WSAId
}
}}
>
<button style={{ float: "right" }} className="btn btn-primary">
View Full Details
</button>
</Link>
<br />
<li>
<b>User Id: </b>
{this.props.RUId}
</li>
<li>
<b>Workstation: </b>
{this.props.workStation}
</li>
<li>
<b>Date: </b>
{moment(this.props.date).format("L")}
</li>
<li>
<b>Complete Token: </b>
{this.props.completeToken}
</li>
</div>
);
} else if (this.state.viewDetails && !this.state.ViewActivityToken) {
return (
<div>
<button
style={{ float: "right" }}
onClick={e =>
this.setState({
ViewActivity: false,
viewDetails: false,
ViewActivityToken: false,
addNoteToken: false
})
}
className="btn btn-secondary"
>
Revert
</button>
<br />
<br />
{this.state.selectedSet &&
this.state.selectedSet.map((item, index) => {
return (
<div>
<li>
{" "}
<b>{item.QuestionWhenAnswered}</b>{" "}
</li>
<li>{item.QuestionResponse}</li>
<li>{item.Accepted}</li>
</div>
);
})}
</div>
);
}
} else if (this.state.ViewActivity && !this.state.addNoteToken) {
return (
<>
<button
style={{ float: "right" }}
onClick={e =>
this.setState({
ViewActivity: false,
viewDetails: false,
ViewActivityToken: false,
addNoteToken: false
})
}
className="btn btn-secondary"
>
Revert
</button>
<br />
<li>
<b>User Id: </b>
{this.props.RUId}
</li>
<li>
<b>Workstation: </b>
{this.props.workStation}
</li>
<li>
<b>Date: </b>
{moment(this.props.date).format("DD/MM/YYYY")}
</li>
<li>
<b>Complete Token: </b>
{this.props.completeToken}
</li>
{this.state.notesFromDB &&
this.state.notesFromDB.map((item, index) => {
return (
<div
style={{
backgroundColor: "white",
border: "inset",
borderWidth: "0.2px"
}}
>
<div style={{ float: "right" }}>
{moment(item.CreationTime).format("HH:MM DD/MM/YYYY ")}
</div>
<div>
<b>{`${item.UserStatus} `}</b>
</div>
<div style={{ textAlign: "left" }}>{item.Notes}</div>
</div>
);
})}
<br />
<button
onClick={this.AddNoteBtn}
className="btn btn-primary"
style={{ width: "100%" }}
>
Add Note
</button>
</>
);
}
}
}
How come when the first is clicked the modal appears blank but the rest of the modals are filled with the right data.
Essentially it seems as if though the first modal is not performing the data fetch which is within the modal component.
Any extra information needed let me know but these seem to be the most important for this use case.
Any help is much appreciated.
I completed this by using a condition within the use effect.
useEffect(() => {
setLoadingToken(true);
let data = {
WSAId: props.WSAId
};
fetch("/get-completed-questions", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(data)
})
.then(recordset => recordset.json())
.then(results => {
setAnsweredQuestions(results.recordset);
});
}, [PUT YOUR CONDTION HERE]);
I simply just passed my props through so it says every time there is a new props do this.

Categories

Resources