Next.JS component breaking after reload & state change - javascript

I am working on a Next.JS project however came across an issue I have not been able to resolve.
Whenever I reload the page, one of the components renders, more or less, as child of itself. Here is an video of what happens:
Here follows the code relevant:
<Col md={9} className={styles.Main}>
{roomsError && <p>{t('common:DATA_LOADING_ERROR_TEXT')}</p>}
{roomsData.length > 0 && <RoomCards user={user} roomsData={roomsData} />}
{roomsData.length === 0 && !loading && <RoomCardEmpty/>}
{loading && <p>{t('ROOMS_LOADING_ROOMS')}</p>}
</Col>
const [roomsData, setRoomsData] = useSessionStorage('dorms', []);
const [hasMore, setHasMore] = useState(true);
const [loading, setLoading] = useState(false);
const [roomsError, setRoomsError] = useState(false);
.Main {
overflow-x: hidden;
padding-bottom: 20px;
}
export default function RoomCards({
roomsData = [],
user = null,
uploadsPage = false,
className = '',
style,
...restProps
}) {
const {t} = useTranslation('rooms');
const rowClassName = uploadsPage ? 'row-cols-1 row-cols-md-2' : '';
const colMd = uploadsPage ? null : 10;
if (roomsData.length === 0) {
return (
<>
{uploadsPage && <p>{t('ROOMS_CARDS_NO_UPLOADED_ROOMS_TEXT')}</p>}
{!uploadsPage && <p>{t('ROOMS_CARDS_NO_ROOMS_TEXT')}</p>}
</>
);
} else {
return (
<Row className={`justify-content-center g-5 ${rowClassName}`} style={uploadsPage ? {paddingBottom: '80px'} : {}}>
{roomsData.map((roomData, index) => (
<Col
key={index}
md={colMd}
className={className}
style={style}
{...restProps}
>
{roomData.external ?
<ExternalRoomCard
user={user}
roomData={roomData}
uploadsPage={uploadsPage}
/> :
<RoomCard
user={user}
roomData={roomData}
uploadsPage={uploadsPage}
/>}
</Col>
))}
</Row>
);
}
}
Here is the rendered HTML:
Before Reload:
After Reload:
I hope anyone can tell me what goes wrong here and can help me to resolve this.
EDIT:
Another attempt... Weird behavior...

Related

Custom alert component cannot be displayed after first initialiation

In a React app, I am trying to create a custom alert in a separate component as shown below:
employee.ts:*
const [open, setOpen] = useState(false);
const [severity, setSeverity] = useState("");
const [message, setMessage] = useState("");
<CustomAlert open={open} severity={severity} message={message} />
custom-alert.js:
export default function CustomAlert(props) {
const{open, message, severity} = props;
return (
<Snackbar open={open} autoHideDuration={6000} >
{severity !== null && severity !== undefined && severity !== "" ? (
<Alert
variant="filled"
onClick={() => {
setOpen(false);
}}
severity={severity}
sx={{ width: "100%" }}
>
{message}
</Alert>
) : (
<div></div>
)}
</Snackbar>
)
}
Although it works on the first call, it cannot be displayed for the next call from employee component. So, should I define some listener etc? Or can I fix the problem easily by using a smart approach?

How to correctly fill the grid antd with data from the server

I made such a grid using grid antd . code here.
const { Row, Col } = antd;
const App = () => (
<Row>
<Col className={'first'} span={16}>
<img src="https://picsum.photos/800/400?random=1"/>
</Col>
<Col span={8}>
<Row>
<Col className={'second'} span={24}>
<img src="https://picsum.photos/800/400?random=2"/>
</Col>
<Col className={'third'} span={24}>
<img src="https://picsum.photos/800/400?random=3"/>
</Col>
</Row>
</Col>
</Row>
)
const ComponentDemo = App;
ReactDOM.render(<ComponentDemo />, mountNode);
I'm getting data from the server. There may be more than 3 of them there. I should output the first 3 like this. The rest will be displayed after pressing the button. How can this effect be achieved in myData.map(). To output these elements without using indexes?
I will try do somthing like this
dataSale.slice(0,maxCount).map(({...item},index)=>(
(index===0)?(
<Col key = {index} span={16}>
<SaleCard {...item}/>
</Col>
):(
<Col key={index} span={8}>
<SaleCard {...item}/>
</Col>
)
))
I hope this helps. The following code should work with any number of image links sent by the server. I have mocked with 9 images.
I followed components approach, like what you are supposed to when working with React. I created some components and pieced them back-together to create the design that you wanted to with map().
const { Row, Col, Button } = antd;
const {useState} = React;
const data = [
"https://picsum.photos/800/400?random=1",
"https://picsum.photos/800/400?random=2",
"https://picsum.photos/800/400?random=3",
"https://picsum.photos/800/400?random=4",
"https://picsum.photos/800/400?random=5",
"https://picsum.photos/800/400?random=6",
"https://picsum.photos/800/400?random=7",
"https://picsum.photos/800/400?random=8",
"https://picsum.photos/800/400?random=9",
]
const ColWithImage = (props) => (
<Col span={props.n % 3 == 1 ? 16 : 24}>
<img src={data[props.n-1]}/>
</Col>
)
const MainRow = (props) => {
const k = props.n*3 + 1;
return (
<Row>
<ColWithImage n={k}/>
<Col span={8}>
<Row>
<ColWithImage n={k+1}/>
<ColWithImage n={k+2}/>
</Row>
</Col>
</Row>
)}
const Container = (props) => {
// Create an iterable array depending upon the number of image links
const arr = Array.from(Array(Math.floor(data.length/3)))
// Show only one row if the button is not clicked
// But show all the rows if the button is clicked
return !props.buttonClicked
? <><MainRow n={0}/></>
: (<>
{
arr.map((item, index) => <MainRow key={index} n={index}/>)
}
</>)
}
const App = () => {
const [buttonClicked, setButtonClicked] = useState(false);
const [buttonText, setButtonText ] = useState("Show More");
const handleClick = () => {
setButtonClicked(!buttonClicked);
setButtonText(buttonClicked ? "Show More" : "Show Less");
}
return (<>
<Container buttonClicked={buttonClicked}/>
<Button onClick={handleClick}>{buttonText}</Button>
</>)
}
const ComponentDemo = App;
ReactDOM.render(<ComponentDemo />, mountNode);
You can view the result here.
I created a SalesView component and it will render the layout as per you requirement. It receives an items array of length less than or equal to 3 (Suppose you have total 5 records, only two records will be displayed in second row).
Hope this solution solves your problem.
import { useState } from "react";
import { Row, Col, Button } from "antd";
import "antd/dist/antd.min.css";
const list = Array.from({ length: 20 }).map((_, i) => ({
id: i,
url: `https://picsum.photos/800/400?random=${i + 1}`,
}));
const SalesCard = ({ id, url }) => {
return <img src={url} />;
};
const SalesView = ({ items }) => {
return (
<Row>
{items?.[0] && (
<Col span={16}>
<SalesCard {...items[0]} />
</Col>
)}
{items.length > 2 && (
<Col span={8}>
<Row>
{items?.[1] && (
<Col span={24}>
<SalesCard {...items[1]} />
</Col>
)}
{items?.[2] && (
<Col span={24}>
<SalesCard {...items[2]} />
</Col>
)}
</Row>
</Col>
)}
</Row>
);
};
function App() {
const [showAll, setShowAll] = useState(false);
const totalChunks = Math.ceil(list.length / 3);
const data = Array.from({ length: showAll ? totalChunks : 1 }).map((_, index) => {
const startIndex = index * 3;
const endIndex = startIndex + 3;
return <SalesView key={index} items={list.slice(startIndex, endIndex)} />;
});
const onClick = () => setShowAll(true);
return (
<>
{data}
{!showAll && <Button onClick={onClick}>Show More</Button>}
</>
);
}
export default App;
you can achieve it via CSS or use ternary
dataSale.map((item, index) =>
<Col key={index} span={index < 3 ? 16 : 8}>
<SaleCard {...item}/>
</Col>
)

Why isn't this React functional component failing to display an array (via .map) stored in state?

I'm having a devil of a time getting this component to properly take an array and map out components based on it. Although I can see this array log to the console, for some reason, my component (in the dev environment) is just displaying blank. So, I know that ternary statement is going to true, but React isn't then mapping (and thus, creating JSX) based on the array.
Note: I've checked out other questions here on SO, and I haven't seen one that's quite like this. If I'm wrong, please point me in that direction!
Expected Behavior
I am successfully setting listings to an array with 11 objects in it, so the DOM should display 11 cards using this data.
Observed Behavior
The DOM will display <Skeleton />s at first, indicating that the ternary statement is flowing to the falsey code block, and then the DOM will just not display anything. White space.
Code Sample
Browse (Parent)
export default function Browse({ user }) {
// prevents useEffect from infinite looping
let [loaded, setLoaded] = useState(false)
let [listings, setListings] = useState(false) // Raw listings array pulled from DB
let [radius, setRadius] = useState(10000)
useEffect(() => {
// Waits for user to come in from Auth Context
if (typeof user.userDoc !== 'undefined' || listings.length < 1) {
if (loaded === false) {
let grabListings = async () => await getLocalListings().then(result => setListings(result))
grabListings()
}
if (listings.length > 1) {
setLoaded(true)
}
}
}, [user, listings])
console.log(listings) // successfully shows array in listings state
return (
<>
<Navbar />
{
listings
? listings.map(listing => {
return (
<BrowseItem
... // props successfully passed here
/>
)
})
: <>
<Container style={{ marginTop: '18px' }}>
<Skeleton animation="wave" />
<Skeleton animation="wave" />
<Skeleton animation="wave" />
<Skeleton animation="wave" />
<Skeleton animation="wave" />
</Container>
</>
}
<Footer />
</>
)
}
BrowseItem (Child)
export default function BrowseItem({
creatorProfilePic,
creatorUsername,
docId,
mainImgUrl,
productStory,
productTitle,
seoTitle,
user
}) {
...
let [favorited, setFavorited] = useState(null) // whether user has favorited this item
let [loaded, setLoaded] = useState(false)
/** Handles click of the favorite button.
* Routes to FirebaseCalls function based on state of favorited
*/
const handleFavorite = async (e) => {
e.preventDefault()
let result
if (favorited) {
result = await removeFavorite(docId, user.currentUid)
if (result === 'success') {
setFavorited(!favorited)
setSnackMessage('Removed from Favorites')
setOpen(true)
} else {
setSnackMessage('⚠️ There was an error removing this from Favorites')
setOpen(true)
}
} else {
result = await addFavorite(docId, user.currentUid)
if (result === 'success') {
setFavorited(!favorited)
setSnackMessage('✓ Added to Favorites')
setOpen(true)
} else {
setSnackMessage('⚠️ There was an error adding this to Favorites')
setOpen(true)
}
}
}
useEffect(() => {
if (!loaded) {
// Determines whether item should default to favorited
try {
let userFavorites = user.userDoc.favorites
if (userFavorites) {
if (userFavorites.some(id => id === docId)) {
setFavorited(true)
}
}
setLoaded(true)
} catch (error) {
console.log(error)
}
}
}, [user])
////////////////////////////////////////////////////////////////////////
///////////////////////////// SNACKS ///////////////////////////////////
////////////////////////////////////////////////////////////////////////
let [snackMessage, setSnackMessage] = useState('')
let [open, setOpen] = useState(null)
const handleSnackClose = (event, reason) => {
if (reason === 'clickaway') {
return
}
setOpen(false)
}
return (
<>
{
loaded
? <>
<Card>
<CardHeader
avatar={
<Avatar alt={`${creatorUsername} Profile Picture`} src={creatorProfilePic} />
}
action={
<IconButton>
<MoreHoriz />
</IconButton>
}
title={productTitle}
subheader={`${creatorUsername}`}
/>
<CardMedia
className={classes.media}
image={mainImgUrl}
title={productTitle}
onClick={handleImgTouch}
/>
<CardContent>
<Typography variant="body2" color="textSecondary" component="p">
{productStory}
</Typography>
</CardContent>
<CardActions disableSpacing>
<IconButton onClick={handleFavorite}>
{
favorited
? <Favorite color='primary' />
: <Favorite />
}
</IconButton>
<IconButton>
<Share />
</IconButton>
</CardActions>
</Card>
<Box mb={4}></Box>
</>
: <>
<Skeleton animation="wave" />
<Skeleton animation="wave" />
<Skeleton animation="wave" />
<Skeleton animation="wave" />
</>
}
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
open={open}
autoHideDuration={3000}
onClose={handleSnackClose}
message={snackMessage}
action={
<>
<Button onClick={handleFavorite} color='primary'>UNDO</Button>
<IconButton size="small" color="inherit" onClick={handleSnackClose}>
<Close fontSize="small" />
</IconButton>
</>
}
/>
</>
)
}
Stack
"next": "^9.4.4"
"react": "^16.8.6"
Testing in a Next.js Development Environment

ReactJS: Rendering the state separately and not as an array

I ended up pulling off what I wanted. However, it's giving me an array of the state instead of rendering each one separately. This is probably very simple and I'm more than likely over-complicating it but hey, any help would be nice.
Here's what I currently am dealing with
And here's a better example: https://i.imgur.com/WLDkbOb.gif
And lastly here's probably the best overview: https://imgur.com/a/zintqTA
constructor(props) {
super(props);
this.state = {
data: [],
loading: false,
}
}
ws = new WebSocket(URL)
componentDidMount() {
this.ws.onopen = () => {
console.log('connected')
}
this.ws.onmessage = e => {
const tbox = JSON.parse(e.data);
if(tbox.data && tbox.data.length > 0){
this.setState({
data : this.state.data.concat(tbox.data[0]),
})
}
}
this.ws.onclose = () => {
console.log('disconnected')
this.setState({
ws: new WebSocket(URL),
})
}
}
render() {
let { data } = this.state;
const chatBox = data.map(item => {
return (
<List
key={item.id}
dataSource={this.state.data}
renderItem={item => (
<List.Item >
<List.Item.Meta
avatar={<Avatar size="large" icon="user" />}
title={<div>{item.user} {item.date}</div>}
description={item.message}
/>
</List.Item>
)}
>
</List>
)
})
return (
<div>
<div>
{chatBox}
</div>
I'm trying to loop through the state and render each message separately
I think you don't need to loop through this.state.data[] because you are already setting data source to antd <List> component. antd List component handles collection of objects for us.
This would be the code for rendring your this.state.data:
const chatBox = <List dataSource={this.state.data}
renderItem={item => (
<List.Item >
<List.Item.Meta
avatar={<Avatar size="large" icon="user" />}
title={<div>{item.user}
{item.date}</div>}
description={item.message}
/>
</List.Item>
)}
>
</List>;
you can have a look at these links :
https://stackblitz.com/run
https://ant.design/components/list/

Update list when redux store changes

I am trying to update the list when my redux store changes but for some odd reason it isn't. I have to manually refresh the page to see my changes. Here's the snippet of my List component and rowRenderer.
<InfiniteLoader
isRowLoaded={this._isRowLoaded}
loadMoreRows={this._loadMoreRows}
rowCount={visibleRequest.length}
>
{({ onRowsRendered, registerChild }) => (
<AutoSizer>
{({ height, width }) => (
<List
ref={registerChild}
className="List"
height={height}
rowHeight={listRowHeight}
onRowsRendered={onRowsRendered}
rowCount={rowCount}
rowRenderer={this._rowRenderer}
width={width}
/>
)}
</AutoSizer>
)}
</InfiniteLoader>
_rowRenderer = ({ index, key, style }) => {
const { loadedRowsMap, selected } = this.state;
const row = this.getDatum(index);
let content;
if (loadedRowsMap[index] === STATUS_LOADED) {
content = row;
} else {
content = (
<div className="placeholder" style={{ width: _.random(100, 200) }} />
);
}
return (
<PendingChat
key={key}
content={content}
style={style}
row={row}
{...this.props}
/>
);
};
Yeah, I ran into the same problem. Its because the references to your objects don't change when you do
const row = this.getDatum(index);
let content;
if (loadedRowsMap[index] === STATUS_LOADED) {
content = row;
}
Take a look at immutability.

Categories