-
I'm holding the data that the I have to hold back the data that the loader returns for the infinity scroll needs. Is there a way around this, filtering should use links instead of forms, as I think this is a common pattern. Thank you in advance. Codesanbox: https://codesandbox.io/p/sandbox/thirsty-mountain-fdqyf5 import type { LoaderArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
import { Link, useLoaderData } from "@remix-run/react";
import { useState } from "react";
type OrderBy = "newest" | "votes";
type Post = {
id: number;
title: string;
votes: number;
createdAt: string;
};
const posts: Post[] = [
{
id: 1,
title: "Post 1",
votes: 3,
createdAt: "2023-06-26T10:53:05.941Z",
},
{
id: 2,
title: "Post 2",
votes: 5,
createdAt: "2000-06-26T10:53:05.941Z",
},
];
export const loader = async ({ request }: LoaderArgs) => {
const searchParams = new URL(request.url).searchParams;
const orderBy = (searchParams.get("orderBy") ?? "newest") as OrderBy;
let postsTmp = posts;
if (orderBy !== "newest") {
postsTmp = posts.sort((a, b) => b.votes - a.votes);
}
return json({ posts: postsTmp, orderBy });
};
export default function Index() {
const loaderData = useLoaderData<typeof loader>();
const activeOrderBy = loaderData.orderBy;
// 🚨 :: not updated when loaderData returns new data based on orderBy searchParams
const [postsState, setPostsState] = useState(loaderData.posts);
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
{(["newest", "votes"] as OrderBy[]).map((orderBy) => (
<Link
key={orderBy}
to={`?orderBy=${orderBy}`}
style={{
margin: "0 8px",
...(activeOrderBy === orderBy ? { color: "green" } : {}),
}}
>
{orderBy}
</Link>
))}
<div>
{postsState.map((post) => (
<div key={post.id} style={{ borderBottom: "1px solid black" }}>
<h1 key={post.id}>{post.title}</h1>
<div style={{ display: "flex", gap: "8px" }}>
<span>
<strong>votes</strong> {post.votes}
</span>
<span>
<strong>createdAt</strong> {post.createdAt}
</span>
</div>
</div>
))}
</div>
</div>
);
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
This is how state works in React, once a component is mounted and a state is initialized if the value used as the default for the state changes React will not sync it again. You have a few options:
For 2, you can do it with: let loaderDataPosts = loaderData.posts
useEffect(() => {
setPostsState(loaderDataPosts)
}, [loaderDataPosts]) For 3, you can do it with: export default function Index() {
let loaderData = useLoaderData<typeof loader>();
let location = useLocation()
return <UI key={location.key} {...loaderData} />
}
function UI({ posts, orderBy: activeOrderBy }: SerializeFrom<typeof loader>) {
let [postsState, setPostsState] = useState(posts);
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
{(["newest", "votes"] as OrderBy[]).map((orderBy) => (
<Link
key={orderBy}
to={`?orderBy=${orderBy}`}
style={{
margin: "0 8px",
...(activeOrderBy === orderBy ? { color: "green" } : {}),
}}
>
{orderBy}
</Link>
))}
<div>
{postsState.map((post) => (
<div key={post.id} style={{ borderBottom: "1px solid black" }}>
<h1 key={post.id}>{post.title}</h1>
<div style={{ display: "flex", gap: "8px" }}>
<span>
<strong>votes</strong> {post.votes}
</span>
<span>
<strong>createdAt</strong> {post.createdAt}
</span>
</div>
</div>
))}
</div>
</div>
);
} I think 1 is the ideal, only use states if you plan to let users change them client-side, second option is better than the third since you can have more states in the future without resetting every time the order of posts change. |
Beta Was this translation helpful? Give feedback.
This is how state works in React, once a component is mounted and a state is initialized if the value used as the default for the state changes React will not sync it again.
You have a few options:
key={location.key}
props (you get thelocation
from Remix's useLocation hook) so every time there's a location change (e.g. when search params change) it will re-mount the component initializing the state again (…