Uncaught Error: There was an error while hydrating this Suspense boundary. Switched to client render
Answered
dank() posted this in #help-forum
dank()OP
I'm trying to generate cards by mapping a card component with an array of data but it's causing a hydration failure. However rendering the card component on it's own doesn't seem to cause any problems.
const content = useMemo(() => {
return vehicleList.map((vehicle) => (
<VehicleCard
key={vehicle.id}
url={`/content/vehicles/${vehicle.id}`}
vehicleImage={`/vehicleImages/${vehicle.file}`}
loading={"lazy"}
vehicleData={{
description: vehicle.description,
make: vehicle.make,
model: vehicle.model,
year: vehicle.year,
price: vehicle.price,
status: vehicle.status,
}}
dictionary={props.dictionary}
/>
));
}, [vehicleList, props.dictionary]);
return <>{content}</>;
}Unhandled Runtime Error
Error: Hydration failed because the initial UI does not match what was rendered on the server.
Warning: Expected server HTML to contain a matching <a> in <div>.Answered by DirtyCajunRice | AppDir
export const useMounted = () => {
const [mounted, setMounted] = useState<boolean>(false);
useEffect(() => {
setMounted(true);
}, []);
return mounted;
}200 Replies
can you post the updated code and the contents of VehicleCard
dank()OP
works
doesn't work
return
<>
<VehicleCard ...props/>
</>;doesn't work
const content = vehicleList.map((vehicle) => (
<VehicleCard
key={vehicle.id}
url={`/content/vehicles/${vehicle.id}`}
vehicleImage={`/vehicleImages/${vehicle.file}`}
loading={"lazy"}
vehicleData={{
description: vehicle.description,
make: vehicle.make,
model: vehicle.model,
year: vehicle.year,
price: vehicle.price,
status: vehicle.status,
}}
dictionary={props.dictionary}
/>
));
return <>{content}</>;
}place the mapping logic directly inside the fragment, you don't need the extra variable
dank()OP
okay i'll try that first
the vehicle card content is too large I'll show the relevant parts
@not-milo.tsx place the mapping logic directly inside the fragment, you don't need the extra variable
dank()OP
that doesn't solve it, I'll send the vehicleCard contents
return (
<React.Fragment>
<Link href={props.url} style={{ textDecoration: "none" }}>
<Card
sx={{
maxWidth: { xs: "70%", md: "90%" },
maxHeight: { md: 200 },
margin: 2,
display: { md: "flex", xs: "block" },
}}
variant="outlined"
>
<CardMedia
sx={{
width: { xs: "100%", sm: "100%", md: "20rem" },
height: { xs: "100%", sm: "100%", md: "10rem" },
}}
>
<Suspense fallback={<Skeleton variant="rectangular" />}>
<StyledImage
src={props.vehicleImage}
width={200}
height={200}
alt="Vehicle picture"
loading={props.loading}
/>
</Suspense>
</CardMedia>
{/* <Box sx={{ display: "flex", flexDirection: "column" }}> */}
<CardActionArea
sx={{
display: "flex",
flexDirection: "column",
justifyContent: "flex-start",
alignItems: "flex-start",
}}
>
<CardContent>
<Typography gutterBottom variant="h5" component="div">
{`${props.vehicleData.make} ${props.vehicleData.model} ${props.vehicleData.year}`}
</Typography>
<Typography variant="body2" color="text.secondary">
{props.vehicleData.description}
</Typography>
<Typography variant="h6">${props.vehicleData.price}</Typography>
</CardContent>
</CardActionArea>
<div
style={{
display: "grid",
// alignItems: "center",
}}
>
<VehicleStatusLabel status={props.vehicleData.status} />
</div>
{/* </Box> */}
</Card>
</Link>
</React.Fragment>
);Ik what you're thinking but hear me out
you keep using fragments everywhere even though you don't need them
dank()OP
Ik I'm wrapping the whole thing inside a link
oh that too
fragments should be used only when you need to return multiple elements at the same level and you can't wrap them in a parent element
dank()OP
I tried only passing <div><div> as the return of the vehicleCard, the error persists on anything I pass except React.Fragment
does the error show up even in an incognito browser window?
Dunker
<Suspense fallback={<Skeleton variant="rectangular" />}>
<StyledImage
src={props.vehicleImage}
width={200}
height={200}
alt="Vehicle picture"
loading={props.loading}
/>
</Suspense>error says problem is here, unless you have another suspense s
dank()OP
lemme check
@Dunker tsx
<Suspense fallback={<Skeleton variant="rectangular" />}>
<StyledImage
src={props.vehicleImage}
width={200}
height={200}
alt="Vehicle picture"
loading={props.loading}
/>
</Suspense>
error says problem is here, unless you have another suspense s
dank()OP
I get the error no matter what JSX I return from the vehicleCard, one exception is if I return React.Fragment
dank()OP
I started seeing the error when I added the loading.tsx, it seems like that file picked up on something
when I remove the file the error disappears
Dunker
next has started to have weird behaviours and bugs, and i am not sure if this one of those or not
without seeing the whole picture it's hard to tell what could be wrong with your code
do you have a minimum reproduction repo that you can share? or even the repository for your actual code
Dunker
also try without that suspense wrapper
@not-milo.tsx do you have a minimum reproduction repo that you can share? or even the repository for your actual code
dank()OP
I'll try to make a minimum reproduction repo
@dank() it really doesn't matter what the vehicle card component is
Dunker
so is it not working {array.map(i => <VehicleCard/>)}
dank()OP
@Dunker so is it not working {array.map(i => <VehicleCard/>)}
dank()OP
yeah it's just
return (
<>
{vehicleList.map((vehicle) => (
<VehicleCard key={vehicle.id} />
))}
</>
);
}@dank() yeah it's just
tsx
return (
<>
{vehicleList.map((vehicle) => (
<VehicleCard key={vehicle.id} />
))}
</>
);
}
Dunker
then problem is not there
when you make it like
#Unknown Channel
// {vehicleList.map((vehicle) => (
// <VehicleCard key={vehicle.id} />
// ))}
</>
#Unknown Channel
// {vehicleList.map((vehicle) => (
// <VehicleCard key={vehicle.id} />
// ))}
</>
if it still persists
@not-milo.tsx does the error show up even in an incognito browser window?
have you checked this? there might be an extension messing with the contents of the page
@Dunker when you make it like
<>
// {vehicleList.map((vehicle) => (
// <VehicleCard key={vehicle.id} />
// ))}
</>
that's not how you comment out jsx...
@not-milo.tsx have you checked this? there might be an extension messing with the contents of the page
dank()OP
yes I tried on incognito mode
Dunker
i know, just for demo
dank()OP
oh you mean commented out
everything
dank()OP
the issue persists
the problem doesn't happen when I try to render the vehicleCard component
it happens when I try to map it
@dank() works
tsx
return
<>
<VehicleCard ...props/>
</>;
**doesn't work**
tsx
const content = vehicleList.map((vehicle) => (
<VehicleCard
key={vehicle.id}
url={`/content/vehicles/${vehicle.id}`}
vehicleImage={`/vehicleImages/${vehicle.file}`}
loading={"lazy"}
vehicleData={{
description: vehicle.description,
make: vehicle.make,
model: vehicle.model,
year: vehicle.year,
price: vehicle.price,
status: vehicle.status,
}}
dictionary={props.dictionary}
/>
));
return <>{content}</>;
}
dank()OP
@Dunker
Dunker
can you map something different like <div>test<div>
Dunker
means
dank()OP
ayo wait a min
dank()OP
it's the array that's causing the issue
Dunker
💥
dank()OP
the array is selected from the redux store
Dunker
that complex, and
dank()OP
idk why that's raising a prob tho, not sure what are the restraints of using RTK in a Next.js app
Dunker
probably you are trying to use redux at server component
dank()OP
nope that would be a straight no no from Next.js
Dunker
also variable.map .. is not a proper way to render items
dank()OP
what is the proper way?
Dunker
const.map
dank()OP
it is a const actually
const vehicleList = useSelector(selectSearchedVehicles);Dunker
not const
const array = ..variable
dank()OP
export const selectSearchedVehicles = (state: stateProps) => {
return state.searchedVehicles.vehiclesPool;
};Dunker
yep it returns something like
const array = ["car1"]
const array = ["car2"]
...
const array = ["car1"]
const array = ["car2"]
...
so its variable
dank()OP
not quite
Dunker
your vehicleList shouldnt change by selections
it should be always same
like ["a", "b"]
Dunker
changing when you select something
dank()OP
how do you suggest I get the data from the store?
Dunker
need to see full component logic
at least selectors and rendering parts
Dunker
also redux slice
dank()OP
I mean you seen it all except for the slice
Dunker
let me bring them together to see
dank()OP
that's the slice
type stateProps = {
searchedVehicles: { vehiclesPool: IVehicle[] };
};
const initialState = {
vehiclesPool: [],
};
const SearchedVehiclesSlice = createSlice({
name: "searchedVehicles",
initialState,
reducers: {
setSearchedVehicles: (state, action) => {
state.vehiclesPool = action.payload;
},
},
});
export const selectSearchedVehicles = (state: stateProps) => {
return state.searchedVehicles.vehiclesPool;
};Dunker
const vehicleList = useSelector(selectSearchedVehicles);
selectSearchedVehicles = (state: stateProps) => {
return state.searchedVehicles.vehiclesPool;
};
vehicleList.map(vehicle -> ... }
selectSearchedVehicles = (state: stateProps) => {
return state.searchedVehicles.vehiclesPool;
};
vehicleList.map(vehicle -> ... }
dank()OP
yeah
Dunker
are you seeing something wrong
dank()OP
👀
I don't
Dunker
state.searchedVehicles dont have vehiclesPool
dank()OP
it's in the intital state actually
Dunker
oh yes mb
dank()OP
I don't think there's a prob cuz TS is pretty strict
it wouldn't let that slip yk
it's inferring the right data
Dunker
okay so what you are seeing as vehicleList at console
and is it changing when you select something from UI
dank()OP
lemme see
nah
not sure what you meant but it doesn't change in the console
when doing things that invoke rerendering the component
Dunker
i meant sth like
select X from UI -> console : ["X"]
" Y from UI -> console: ["Y"]
select X from UI -> console : ["X"]
" Y from UI -> console: ["Y"]
maybe problem is your inputs and select logic, (to send data to redux store)
and if
[1,2,3].map(i => <VehicleCard/> not gives same error, problem is totally your vehicleList arrayDunker
import React from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'
const selectNumCompletedTodos = createSelector(
(state) => state.todos,
(todos) => todos.filter((todo) => todo.completed).length
)
export const CompletedTodosCounter = () => {
const numCompletedTodos = useSelector(selectNumCompletedTodos)
return <div>{numCompletedTodos}</div>
}
export const App = () => {
return (
<>
<span>Number of completed todos:</span>
<CompletedTodosCounter />
</>
)
}When the selector does only depend on the state, simply ensure that it is declared outside of the component so that the same selector instance is used for each render:
redux has a lot edge cases tbh
@Dunker When the selector does only depend on the state, simply ensure that it is declared outside of the component so that the same selector instance is used for each render:
dank()OP
the selectors are all declared in the slice
as I showed earlier
Dunker
yea i meant declare it with createSelector
dank()OP
okay I'll try that
Dunker
When using useSelector with an inline selector as shown above, a new instance of the selector is created whenever the component is rendered. This works as long as the selector does not maintain any state. However, memoizing selectors (e.g. created via createSelector from reselect) do have internal state, and therefore care must be taken when using them. Below you can find typical usage scenarios for memoizing selectors.
dank()OP
actually I found what's causing it
but I will address this when I fix the issue first
Dunker
const counter = useSelector((state) => state.counter) yours looks okay alsodank()OP
so the problem is in a different component, the searchbar, my search feature logic is like this
// filter vehicles based on the search query
useEffect(() => {
if (!searchQuery) {
// if there's no search query, just display all vehicles
dispatch(setSearchedVehicles(allVehicles));
} else {
const queryWords = searchQuery?.toLowerCase().split(" ");etc..
Dunker
i said this you also 😄
maybe problem is your inputs and select logic, (to send data to redux store)
dank()OP
yes that's why I checked the other file
it's because I'm storing all vehicles in the searchedVehicles when there's no query
what's wrong with that tho 🙂
Dunker
you have a thing like all parts are moving 😄
if there is no search, why dispatch tho
dank()OP
because the searchedVehicles is initially empty
also when user clears the search it displays all vehicles
Dunker
yea but method of that is kinda wrong
maybe you should use .filter when search
dank()OP
where?
Dunker
vehicleList.filter(q => q.brand == query) ?
dank()OP
instead of what?
Dunker
dispatch
dank()OP
that's not supposed to do any filtering
it just fills the searched vehicles pool with all the available vehicles from the vehicle's slice(another slice)
so I basically have two slices related to vehicles, one is for vehicles and one is for searched vehicles
Dunker
hmm, and
dank()OP
the searched vehicles is initially empty, but when a user enters the page it is filled with allVehicles.
all vehicles comes from this selector
selected by
all vehicles comes from this selector
export const selectAllVehicles = (state: RootState) => {
return state.vehicle.entities;
};selected by
const allVehicles = useSelector(selectAllVehicles);Dunker
okay go on
dank()OP
it is filled whenever the search query is empty
when there's no query in the url params
Dunker
yep
then
dank()OP
that's all
idk how to go about fixing it
Dunker
i think problem is you are using dispatch in useEffect instead a action function
like handleSearch, onSubmit ..
thats because your search logic kinda working wrong
dank()OP
yeah actually I could listen to the search input change and do the thing
Dunker
okay at first all vehicles rendering at page, (this dont need a dispatch tho) then you are searching them
dank()OP
lemme try that
Dunker
when someone writes something as query, your list should rerender as updated
filtered
@Dunker okay at first all vehicles rendering at page, (this dont need a dispatch tho) then you are searching them
dank()OP
there's more stuff to account for, I can't directly use the allVehicles
Dunker
so your selector probably will like (state => state.allVehicles.filter(v => v.brand == query))
dank()OP
there's also these selectors that the search filter gonna be affecting based on current page
export const selectSavedVehicles = (state: stateProps) => {
return state.searchedVehicles.vehiclesPool.filter(
(vehicle: IVehicle) => vehicle.status === VehicleProgressStatus.SAVED
);
};
export const selectDeletedVehicles = (state: stateProps) => {
return state.searchedVehicles.vehiclesPool.filter(
(vehicle: IVehicle) => vehicle.status === VehicleProgressStatus.DELETED
);
};
export const selectRequestedVehicles = (state: stateProps) => {
return state.searchedVehicles.vehiclesPool.filter(
(vehicle: IVehicle) => vehicle.status === VehicleProgressStatus.REQUESTED
);
};
export const selectBidlostVehicles = (state: stateProps) => {
return state.searchedVehicles.vehiclesPool.filter(
(vehicle: IVehicle) => vehicle.status === VehicleProgressStatus.BID_LOST
);
};
export const selectBidwonVehicles = (state: stateProps) => {
return state.searchedVehicles.vehiclesPool.filter(
(vehicle: IVehicle) => vehicle.status === VehicleProgressStatus.BID_SUCCESS
);
};
export const selectImportingVehicles = (state: stateProps) => {
return state.searchedVehicles.vehiclesPool.filter(
(vehicle: IVehicle) => vehicle.status === VehicleProgressStatus.IMPORTING
);
};
export const selectReceivedVehicles = (state: stateProps) => {
return state.searchedVehicles.vehiclesPool.filter(
(vehicle: IVehicle) => vehicle.status === VehicleProgressStatus.RECEIVED
);
};
export const selectForsaleVehicles = (state: stateProps) => {
return state.searchedVehicles.vehiclesPool.filter(
(vehicle: IVehicle) => vehicle.status === VehicleProgressStatus.FOR_SALE
);
};
export const selectSoldVehicles = (state: stateProps) => {
return state.searchedVehicles.vehiclesPool.filter(
(vehicle: IVehicle) => vehicle.status === VehicleProgressStatus.SOLD
);
};
export default SearchedVehiclesSlice.reducer;if the problem is with the usage of useEffect then I could probably fix it with an onChange handler for the search input and move my logic there
Dunker
selectSearchedVehicles = () => state.searchedVehicles.vehiclesPool.filter((v => v.name == query) kinda
@dank() if the problem is with the usage of useEffect then I could probably fix it with an onChange handler for the search input and move my logic there
Dunker
onSubmit handler is better, when you submit query it ll send to store and render it back, if you want it dynamic use onChange
dank()OP
but how do I fill the searched vehicles pool on first render
without useEffect
Dunker
at first there is no searchedVehicles
input is empty
query is null
dank()OP
yes but when there is no searchedVehicles I gotta show all vehicles
Dunker
yea just fetch all vehicles from state and render it directly
Dunker
no you ll filter your data according to your query
as expected
first render all data, then filter
not even related with state
dank()OP
the way I'm doing it now is, I render the searchedVehicles in the page and then it's filled
and the filters apply to it
that works for all the different selectors
Dunker
yea but you are trying to write something like [].map(i -> <VehicleCard/>
cuz vehicleList is [] at first load
dank()OP
I see
Dunker
you are connecting your search logic to data
i meant you need to connect your data to search logic
dank()OP
I don't wanna apply any filters to the actual vehicles slice, just the searchedVehicles
that's the thing
Dunker
like
if (query) {return state.vehicles.filter(v => v.name = query)
else {return state.vehicles.all ..
if (query) {return state.vehicles.filter(v => v.name = query)
else {return state.vehicles.all ..
also look at RTK-Query
this is all i have so far, hope you can fix it somehow if not there s a channel in reactiflux dc server named redux
dank()OP
yeah I'll try to figure out a way without changing my whole process
Dunker
only you need that actual vehicles slice, not more
dank()OP
yeah maybe no need for searchedVehicles slice, it's just I'm trying to keep all the selectors in sync
Dunker
with rtk-query, not even need selectors
dank()OP
interesting
Dunker
good luck, if nothing helps tomorrow i can look at live if you want
dank()OP
alr deal thanks a lot I appreciate it
I might just keep it as is and call it a feature, the user has to search before all the vehicles appear
doesn't seem too bad
export const useMounted = () => {
const [mounted, setMounted] = useState<boolean>(false);
useEffect(() => {
setMounted(true);
}, []);
return mounted;
}Answer

