No-unused-expressions issue React - reactjs

I am making an eCommerce website with React. There is a ProductList.js which is listing the code of Product.js. I am using the context API which is created from context.js.
The code of context.js is-
state = {
products: [],
detailProduct,
};
getItem = (id) => {
let product = this.state.products.find((item) => item.id === id);
return product;
};
handleDetail = (id) => {
let product = this.getItem(id);
this.setState(() => {
return { detailProduct: product };
});
};
addToCart = (id) => {
console.log(`Hello from Add to Cart. Id is: ${id}`);
};
render() {
return (
<ProductContext.Provider
value={{
...this.state,
handleDetail: this.handleDetail,
addToCart: this.addToCart
}}
>
{this.props.children}
</ProductContext.Provider>
);
}
This is not the full code for sure. If you need full code, I can give that too.
Code of Product.js is-
<ProductConsumer>
{(value) => {
<div className="p-5 img-container" onClick={() => value.handleDetail(id)}>
<Link to="/details">
<img src={img} className="card-img-top" alt="Product Image" />
</Link>
<button className={inCart ? "in-cart cart-btn" : "cart-btn"} disabled={inCart ? true : false} onClick={() => value.addToCart(id)}>
{inCart ? (
<p className="text-capitalize mb-0" disabled>
Already in cart
</p>
) : (
<i className="fas fa-cart-plus"></i>
)}
</button>
</div>
}}
</ProductConsumer>
The error I am getting--
./src/components/Product.jsx
Line 13:15: Expected an assignment or function call and instead saw an expression no-unused-expressions
Search for the keywords to learn more about each error.
Please help me in fixing this issue...

You are using curly braces { in the consumer block. Change these to parentheses like so:
<ProductConsumer>
{(value) => (
<div className="p-5 img-container" onClick={() => value.handleDetail(id)}>
<Link to="/details">
<img src={img} className="card-img-top" alt="Product Image" />
</Link>
<button className={inCart ? "in-cart cart-btn" : "cart-btn"} disabled={inCart ? true : false} onClick={() => value.addToCart(id)}>
{inCart ? (
<p className="text-capitalize mb-0" disabled>
Already in cart
</p>
) : (
<i className="fas fa-cart-plus"></i>
)}
</button>
</div>
)}
</ProductConsumer>
Read this answer for an explanation: https://stackoverflow.com/a/35440330/1790728

Related

is there a way to display each individual comment data without it being set to the recently clicked comment?

im currently building a website similar to reddit but im having troubles with the comments, i can toggle the comment on and off on click but whenever more than one comment is clicked it just displays the comment data of the recent comment that was clicked.
this is my code:
const handleComments = (subText, id) => {
setCommentIds(prev => (prev.includes(id) ? prev.filter(cid => cid !== id) : [...prev, id]));
dispatch(getComments({ subText, id }));
};
const popularRendering = () => {
if (popular.isLoading) {
return Array(5)
.fill()
.map((_, index) => <FeedSkeleton key={index} />);
} else if (popular.data && popular.data.data) {
return popular.data.data.children.map((data, index) => (
<div className="box-container" key={index}>
<div className="data-container">
<div className="votes-container">
<TiArrowUpOutline
size={27}
className={`${upvoted[data.data.id] ? "up-voted" : "up-vote"}`}
onClick={() => handleUpVote(data.data.id)}
/>
<p className={upvoted[data.data.id] ? "up-voted" : downvoted[data.data.id] ? "down-voted" : ""}>{formatNumber(data.data.score)}</p>
<TiArrowDownOutline
size={27}
className={`${downvoted[data.data.id] ? "down-voted" : "down-vote"}`}
onClick={() => handleDownVote(data.data.id)}
/>
</div>
<div className="feed-header">
{subredditIconUrl[data.data.subreddit_name_prefixed] ? (
<img className="feed-icon-img" src={subredditIconUrl[data.data.subreddit_name_prefixed]} alt="subreddit-icon" />
) : null}
<p>{data.data.subreddit_name_prefixed}</p>
<span>Posted by u/{data.data.author} </span>
<span>{formatDate(data.data.created_utc)}</span>
</div>
<div className="feed-body">
<p>{data.data.title}</p>
{isImage(data.data.url) ? (
<img src={data.data.url} alt="subreddit" />
) : data.data.is_video ? (
<video height="auto" width="100%" controls>
<source src={data.data.media.reddit_video.fallback_url} />
<p className="error-media">Sorry, this content cannot be displayed.</p>
</video>
) : null}
</div>
<div className="footer">
<div className="comments" onClick={() => handleComments(data.data.subreddit_name_prefixed, data.data.id)}>
<GoComment size={25} className="comment-icon" />
<p>{formatNumber(data.data.num_comments)} Comments</p>
</div>
</div>
{subreddit.commentsLoading
? Array(5)
.fill()
.map((_, index) => <CommentsSkeleton key={index} />)
: commentIds.includes(data.data.id) && <Comments postId={data.data.id} />}
</div>
</div>
));
}
};
i tried using an array to store the ids and only display the ids of those comments but it just always resulted in issues and an infinite loop of headaches and errors.

All my sidebar menu opens at once in ReactJS

I am trying to create a sidebar menu with dropdwon
For I single menu it works fine but if I have multiple dropdown
All opens at one when I click on the menu to open the submenu.
I am getting the data for the menu from a json array
const SidebarItems = ({ items }) => {
const [open, setOpen] = useState(false)
return (
<div>
{items && items.map((item, index) => {
return (
<div key={index}>
<div>
<li >
<div className="nav-items" onClick={() => setOpen(!open)}>
<span>
<i className="bi-badge-vr" />
{item.title}
</span>
<i className="bi-chevron-down" />
</div>
</li>
</div>
<div className={open ? 'sub-menu sub-menu-open' : "sub-menu"} >
{item.chidren && item.chidren.map((sub_menu, sub_index) => {
return (
<div key={sub_menu}>
<Link to="manager/staff/create">
<span>
<i className="bi-badge-vr" />
{sub_menu.title}
</span>
</Link>
</div>
)
})}
</div>
</div>
)
})}
</div>
)
}
This is because they all use the same state.
Move the item to its own component so I can keep its own state:
const Item = ({ item }) => {
const [open, setOpen] = useState(false);
return <Your item code here />;
};
And in SideBarItems:
items.map((item, index) => {
return <Item item={item} key={index} />;
});

React can't perform state update on unmounted component - checking isMounted not fixing

I am using axios to return data from an API and trying to present this in to various nested components in my React App.
The code looks something like this:
const Building = () => {
const { bid } = useParams();
const { userAccessToken } = useAuth();
const [buildingData, setBuildingData] = useState([]);
const bearerToken = `Bearer ${userAccessToken}`;
React.useEffect(() => {
let isMounted = true;
const axiosConfig = {
headers: { Authorization: bearerToken },
};
axios
.get(
"http://localhost:3001/building?requestedlid=2&requestedbid=" + bid,
axiosConfig
)
.then(function (response) {
if (isMounted) {
setBuildingData(response.data[0]);
}
})
.catch(function (error) {
// handle error
console.log(error);
});
return () => {
isMounted = false;
};
}, [bearerToken, bid]);
return (
<React.Fragment>
<Helmet title="Building Profile" />
<Container fluid className="p-0">
<Breadcrumb className="float-end mt-2">
<Breadcrumb.Item href="/dashboard/default">Home</Breadcrumb.Item>
<Breadcrumb.Item href="/buildings/portfolio">
Portfolio
</Breadcrumb.Item>
<Breadcrumb.Item active>Building Profile</Breadcrumb.Item>
</Breadcrumb>
<h1 className="h3 mb-3">
Building Profile
<OffcanvasHelp
id="buildingprofile"
name="Building Profile"
scroll
backdrop
/>
</h1>
<div className="clearfix"></div>
<Row>
<Col xl="8">
<BuildingProfile
name={buildingData.building_name}
status={buildingData.status}
description={buildingData.description}
keycontacts={buildingData.key_contacts}
created={buildingData.stats.created_date}
golive={buildingData.stats.golive_date}
/>
<Rooms />
</Col>
<Col xl="4">
<AccountManager />
<Map location={buildingData.location} />
<GetSupport type="commercial" />
</Col>
</Row>
</Container>
</React.Fragment>
);
};
My problem is I am receiving the common error:
Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
Using the methods described in many StackOverflow answers, to check an isMounted boolean as in my code above, I am unable to resolve the issue.
Weirdly, the issue specifically occurs when I am passing these two props to the BuildingProfile component:
created={buildingData.stats.created_date}
golive={buildingData.stats.golive_date}
If I don't pass these two props, everything else works fine.
I'm sure I'm missing something silly but after several hours of trying to figure it out I'm still stuck. Anybody who can provide a pointer or any tips, I would be really grateful.
Many thanks
--- Update - including the BuildingProfile component:
const BuildingProfile = ({
name,
status,
description,
created,
golive,
keycontacts,
}) => {
// Modal config for "Deactivate Building"
const initOpenModals = () => {
let modals = {};
colors.forEach((color, index) => {
modals = Object.assign({}, modals, { [index]: false });
});
console.log(modals);
return modals;
};
const [openModals, setOpenModals] = useState(() => initOpenModals());
const toggle = (index) => {
// Toggle selected element
setOpenModals((openModals) =>
Object.assign({}, openModals, { [index]: !openModals[index] })
);
};
const notyf = useContext(NotyfContext);
const [type] = useState("success");
const [duration] = useState("5000");
const [ripple] = useState(true);
const [dismissible] = useState(false);
const [positionX] = useState("right");
const [positionY] = useState("top");
const navigate = useNavigate();
return (
<Card>
<Card.Header className="mb-0 pb-0">
<Card.Title className="mb-0">
<IsAllowed to="edit:buildings">
<div className="card-actions float-end">
<Dropdown align="end">
<Dropdown.Toggle as="a" bsPrefix="-">
<MoreHorizontal />
</Dropdown.Toggle>
<Dropdown.Menu>
<Dropdown.Item onClick={() => navigate("/buildings/edit")}>
Edit Building
</Dropdown.Item>
<React.Fragment key="deactivateBuilding">
<Dropdown.Item onClick={() => toggle("deactivateBuilding")}>
Deactivate Building
</Dropdown.Item>
<Modal
show={openModals["deactivateBuilding"]}
onHide={() => toggle("deactivateBuilding")}
centered
>
<Modal.Header closeButton>
<b>Admin Function:</b> Deactivate Building
</Modal.Header>
<Modal.Body className="m-3">
<p className="text-left mb-0">
Are you sure you want to deactivate the
<b>Bus Works</b> building? This will prevent the
building from showing up in the platform completely.
</p>
</Modal.Body>
<Modal.Footer>
<Button
variant="secondary"
onClick={() => toggle("deactivateBuilding")}
>
Close
</Button>{" "}
<Button
variant="danger"
onClick={() => {
toggle("deactivateBuilding");
notyf.open({
type,
message: "The building has been deactivated.",
duration,
ripple,
dismissible,
position: {
x: positionX,
y: positionY,
},
});
}}
>
Deactivate Building
</Button>
</Modal.Footer>
</Modal>
</React.Fragment>
</Dropdown.Menu>
</Dropdown>
</div>
</IsAllowed>
<h1 className="mb-0 pb-0">{name}</h1>
<Badge
className={
status === "Live Building"
? "my-2 btn-gradient inline"
: "my-2 inline"
}
bg="success"
>
{status}
</Badge>
</Card.Title>
</Card.Header>
<Card.Body>
<h5>Building Overview:</h5>
<p className="mb-4">{description}</p>
<div className="row">
<div className="col-md-4">
<div className="mb-4">
<h5>Created Date</h5>
<p>{created}</p>
</div>
</div>
<div className="col-md-4">
<div className="mb-4">
<h5>Go-Live Date</h5>
<p>{golive}</p>
</div>
</div>
<div className="col-md-4">
<div className="mb-4">
<h5>Key Contacts</h5>
<div>
<span className="me-1">
<OverlayTrigger
placement="right"
overlay={
<Tooltip id="tooltip-right">Joe Bloggs</Tooltip>
}
>
<img
src={avatar3}
width="28"
height="28"
className="rounded-circle me-2"
alt="Joe Bloggs"
/>
</OverlayTrigger>
</span>
<span className="me-1">
<OverlayTrigger
placement="right"
overlay={
<Tooltip id="tooltip-right">Joe Bloggs</Tooltip>
}
>
<img
src={avatar2}
width="28"
height="28"
className="rounded-circle me-2"
alt="Joe Bloggs"
/>
</OverlayTrigger>
</span>
<span className="me-1">
<OverlayTrigger
placement="right"
overlay={
<Tooltip id="tooltip-right">Joe Bloggs</Tooltip>
}
>
<img
src={avatar1}
width="28"
height="28"
className="rounded-circle me-2"
alt="Joe Bloggs"
/>
</OverlayTrigger>
</span>
<Link to="#" className="d-inline-block text-muted fw-bold ms-2">
+2 more
</Link>
</div>
</div>
</div>
</div>
</Card.Body>
</Card>
);
};
I figured this out and wanted to share my answer in-case it helps anyone else.
It turns out that because I am trying to access the nested object property in the rendered component before the API call has finished, the property ("stats") doesn't exist. When the state eventually updates once the API call has finished, it cannot update the component resulting in the error I was seeing.
The way to fix this is to do something like this:
.... // useState should be set to an object, not an array
const [buildingData, setBuildingData] = useState({});
.... // skipping past intermediary code for brevity
.then(function (response) {
if (isMounted) {
setBuildingData({
name: response.data[0].building_name,
status: response.data[0].status,
description: response.data[0].description,
keycontacts: response.data[0].key_contacts,
created: response.data[0].stats.created_date,
golive: response.data[0].stats.golive_date
});
}
})
.... // then to access it in the component use:
<BuildingProfile
name={buildingData.name}
status={buildingData.status}
description={buildingData.description}
keycontacts={buildingData.keycontacts}
created={buildingData.created}
golive={buildingData.golive}
/>
Doing it this way means that the nested object is updated with API data inside of the useEffect hook, not in the component itself.
Hope this helps somebody.

Cannot redirect one page to another page in ReactJs

I'm making a function that when i click on the image container, it will open the page with the product detail with the exact detail for each specific product. However, when i click on the image, nothing happen! Please help me to find out what wrong with my codes, thank you so much!
Product.js:
class Product extends React.Component {
render() {
const { id, title, img, price, inCart } = this.props.product;
return (
<ProductWrapper clasName="col-9 mx-auto col-md-6 col-lg-3 my-3">
<div className="card">
<ProductContext.Consumer>
{(value) => (
<div className="img-container p-5">
<Router>
<Link to="/details">
<img
src={img}
alt="product"
className="card-img-top"
onClick={() => {
value.handleDetail(id);
}}
/>
</Link>
</Router>
<button
className="cart-btn"
onClick={() => value.addToCart(id)}
disabled={inCart ? true : false}
>
{inCart ? (
<p className="text-capitalize mb-0">In Cart</p>
) : (
<i class="fas fa-cart-plus"></i>
)}
</button>
</div>
)}
</ProductContext.Consumer>
<div className="card-footer d-flex justify-content-between">
<p className="align-self-center mb-0">{title}</p>
<h5 className="text-blue mb-0">
<span className="mr-1">$</span>
{price}
</h5>
</div>
</div>
</ProductWrapper>
);
}
}
context.js:
class ProductProvider extends React.Component {
state = {
products: storeProducts,
detailProduct: detailProduct
};
getItem = (id) => {
const product = this.state.products.find((item) => item.id === id);
return product;
};
handleDetail = (id) => {
const product = this.getItem(id);
this.setState(() => {
return { detailProduct: product };
});
};
addToCart = (id) => {
console.log(`hello details. id is ${id}`);
};
render() {
return (
<ProductContext.Provider
value={{
...this.state,
handleDetail: this.handleDetail,
addToCart: this.addToCart
}}
>
{this.props.children}
</ProductContext.Provider>
);
}
}
Sandbox link for better observation: https://codesandbox.io/s/why-cant-i-fetch-data-from-a-passed-value-forked-30bgi?file=/src/App.js
I recommend a different approach where the product ID goes into the URL rather than being selected in context. This has a major advantage in that refreshing the details page means the product ID will be retained.
Here is a link to a working CodeSandbox.
And here are the changes I made:
In the context provider, you can remove handleDetail since the selection will instead live in the URL:
class ProductProvider extends React.Component {
state = {
products: storeProducts,
detailProduct: detailProduct
};
getItem = (id) => {
const product = this.state.products.find((item) => item.id === id);
return product;
};
addToCart = (id) => {
console.log(`hello details. id is ${id}`);
};
render() {
return (
<ProductContext.Provider
value={{
...this.state,
addToCart: this.addToCart
}}
>
{this.props.children}
</ProductContext.Provider>
);
}
}
In the App component, change your details route to take an itemId parameter:
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<ProductProvider>
<Router>
<Switch>
<Route exact path="/productlist" component={ProductList} />
<Route path="/details/:itemId" component={Details} />
<Route path="*" component={() => "404 Not Found"} />
</Switch>
</Router>
</ProductProvider>
</div>
);
}
In your product component, Make the Link point to the details/itemId URL and remove any need to set that ID in context:
class Product extends React.Component {
render() {
const { id, title, img, price, inCart } = this.props.product;
return (
<ProductWrapper clasName="col-9 mx-auto col-md-6 col-lg-3 my-3">
<div className="card">
<ProductContext.Consumer>
{(value) => (
<div className="img-container p-5">
<Link to={`/details/${id}`}>
<img src={img} alt="product" className="card-img-top" />
</Link>
<button
className="cart-btn"
onClick={() => value.addToCart(id)}
disabled={inCart ? true : false}
>
{inCart ? (
<p className="text-capitalize mb-0">In Cart</p>
) : (
<i class="fas fa-cart-plus"></i>
)}
</button>
</div>
)}
</ProductContext.Consumer>
<div className="card-footer d-flex justify-content-between">
<p className="align-self-center mb-0">{title}</p>
<h5 className="text-blue mb-0">
<span className="mr-1">$</span>
{price}
</h5>
</div>
</div>
</ProductWrapper>
);
}
}
Finally, in the Details component, pluck the itemId off of the params and find the right item from your product list in context:
class Details extends React.Component {
render() {
const { itemId } = this.props.match.params;
return (
<ProductContext.Consumer>
{(value) => {
const selected = value.products.find(
(p) => p.id === parseInt(itemId)
);
if (!selected) {
return "Bad product ID: " + itemId;
}
const { id, company, img, info, price, title, inCart } = selected;
return (
<div className="container py-5">
{/* title */}
<div className="row">
<div className="col-10 mx-auto text-center text-slanted text-blue my-5">
<h1>{title}</h1>
</div>
</div>
{/* end of title */}
<div className="row">
<div className="col-10 mx-auto col-md-6 my-3">
<img src={img} className="img-fluid" alt="" />
</div>
{/* prdoduct info */}
<div className="col-10 mx-auto col-md-6 my-3 text-capitalize">
<h1>model : {title}</h1>
<h4 className="text-title text-uppercase text-muted mt-3 mb-2">
made by : <span className="text-uppercase">{company}</span>
</h4>
<h4 className="text-blue">
<strong>
price : <span>$</span>
{price}
</strong>
</h4>
<p className="text-capitalize font-weight-bold mt-3 mb-0">
some info about product :
</p>
<p className="text-muted lead">{info}</p>
{/* buttons */}
<div>
<Link to="/productlist">
<ButtonContainer>back to products</ButtonContainer>
</Link>
<ButtonContainer
cart
disabled={inCart ? true : false}
onClick={() => {
value.addToCart(id);
}}
>
{inCart ? "in cart" : "add to cart"}
</ButtonContainer>
</div>
</div>
</div>
</div>
);
}}
</ProductContext.Consumer>
);
}
}

Passing data between components into React (props)

Into DisplayVisit component I have displayed data:
<div>
{(this.state.allVisit.length > 0) ? this.state.allVisit.map(data => {
return (
<div key={data.id} className="card-header mb-2" style={{ background: "#F0F3F7" }}>
<div className="form-group">
<label><b>VisitName:</b> {data.dataContext.VisitName} </label>
</div>
<div className="form-group">
<Link to={`/visit/${data.id}`} className="btn btn-secondary btn-sm" >
<i className="fas fa-arrow-circle-right" /> Details
</Link>
</div>
</div>
)
}) : (
<div className="card-header p-3 mb-2 text-black">
<label><b>Empty</b></label>
</div>
)
}
</div>
When user click Details is moved to next component VisitDetails - this is working.
App.js
<Route exact path="/visit/:id" component={VisitDetails}/>
Then I want have access to id and data.dataContext:
class VisitDetails extends Component {
render() {
const { id } = this.props;
const { data.dataContext } = this.props;
//or const {data} = this.prop
return (
<div>
{id}, {data.dataContext.VisitName}
</div>
)
}
}
but I'm doing something wrong. Im just learning and try to dev first app.
In your DetailVisit component :
<Link to={{ pathname: `/visit/${data.id}`, state: { data: data.dataContext.VisitName} }} className="btn btn-secondary btn-sm">
<i className="fas fa-arrow-circle-right" /> Details
</Link>
In your VisitDetails.js :
class VisitDetails extends Component {
render() {
const { id } = this.props.match.params;
const { data } = this.props.location.state;
return (
<div>
{id}, {data}
</div>
)
}
}

Resources