Unable to use const within a class in React - reactjs

I am following this tutorial
https://nickymeuleman.netlify.com/blog/gatsby-pagination#navigate-to-previousnext-page
Everything is working fine, but I am unable to add const within the class. I am using VSC to code up the site, it just doesn't seem to like it.
This is the class I am trying to place the consts within. As I am new to React I am bit lost on finding a solution without using a plugin.
export default class PostList extends React.Component {
render() {
const posts = this.props.data.allMarkdownRemark.edges
return (
<Layout>
<Head title="Posts" />
<div className={layoutStyles.pageHeader}>
<h2>Posts</h2>
<span>Just my ramberlings</span>
</div>
{posts.map(({ node }) => {
const title = node.frontmatter.title || node.fields.slug
return (
<div className={postPageStyles.postItem}>
<div className={postPageStyles.postItemTitle}>
<h2>{title}</h2>
<span>Posted on {node.frontmatter.date}</span>
</div>
<div>
<p>{node.excerpt}</p>
<Link to={`${node.fields.slug}`}>
<span>Continue Reading</span>
<span role="img"> 👉🏼</span>
</Link>
</div>
</div>
)
})}
</Layout>
)
}
}

You cannot indeed use const in a class "just like that":
class App extends React.Component {
const a = 2 // will throw a syntax error
render(){
return <div>Hello World</div>
}
What you can do is either not use a const to declare the variable as a class field:
class App extends React.Component {
a = "john";
render(){
//now you can access a via `this`
return <div>{`Hello ${this.a}`}</div>
}
Or if you don't need it to be to be somehow "bound" to your component, you can declare it outside of the class.
const a = "john"
class App extends React.Component {
render(){
//you can simply access `a`
return <div>{`Hello ${a}`}</div>
}

I have managed to resolve this. After much googling.
const PostList = props => {
const { currentPage, numPages } = props.pageContext
const isFirst = currentPage === 1
const isLast = currentPage === numPages
const prevPage = currentPage - 1 === 1 ? '/' : (currentPage - 1).toString()
const nextPage = (currentPage + 1).toString()
const posts = props.data.allMarkdownRemark.edges
return (
<Layout>
<Head title="Posts" />
<div className={layoutStyles.pageHeader}>
<h2>Posts</h2>
<span>Just my ramberlings</span>
</div>
{posts.map(({ node }) => {
const title = node.frontmatter.title || node.fields.slug
return (
<div className={postPageStyles.postItem}>
<div className={postPageStyles.postItemTitle}>
<h2>{title}</h2>
<span>Posted on {node.frontmatter.data}</span>
</div>
<div>
<p>{node.excerpt}</p>
<Link to={`${node.fields.slug}`}>
<span>Continue Reading</span>
<span role="img"> 👉🏼</span>
</Link>
</div>
</div>
)
})}
{!isFirst && (
<Link to={prevPage} rel="prev">
← Previous Page
</Link>
)}
{!isLast && (
<Link to={nextPage} rel="next">
Next Page →
</Link>
)}
{Array.from({ length: numPages }, (_, i) => (
<Link
key={`pagination-number${i + 1}`}
to={`posts/${i === 0 ? '' : i + 1}`}
>
{i + 1}
</Link>
))}
</Layout>
)
}
export default PostList
To use the consts I had to change
const PostList = ({ data }) => {
const posts = data.allMarkdownRemark.edges
...
to
const PostList = props => {
const posts = props.data.allMarkdownRemark.edges
which then allowed me to use const { currentPage, numPages } = props.pageContext

Does this work as a functional component? Note that you are no longer accessing this.props.data but can instead just directly access the destructured data variable.
const PostList = ({data}) => {
const posts = data.allMarkdownRemark.edges
return (
<Layout>
<Head title="Posts" />
<div className={layoutStyles.pageHeader}>
<h2>Posts</h2>
<span>Just my ramberlings</span>
</div>
{posts.map(({ node }) => {
const title = node.frontmatter.title || node.fields.slug
return (
<div className={postPageStyles.postItem} key={node.fields.slug}>
<div className={postPageStyles.postItemTitle}>
<h2>{title}</h2>
<span>Posted on {node.frontmatter.date}</span>
</div>
<div>
<p>{node.excerpt}</p>
<Link to={`${node.fields.slug}`}>
<span>Continue Reading</span>
<span role="img"> 👉🏼</span>
</Link>
</div>
</div>
)
})}
</Layout>
)
}
export default PostList
export const query = graphql`{...}`

Related

UseState value doesn't change when re-render in NextJs

I am creating a blog application on NextJs and on the page which displays posts by category i faced with a problem
i have a useState which contains all posts that i get from backend by category that i pass
The problem is when i click on the link which changes category of displayed posts i still got same posts on page, and to get actual useState value i have to reload page, how to fix it?
Here is full component
const News: FC<NewsCat> = ({ news, category }) => {
const [selectedCateg, setSelectedCateg] = useState(category);
//UseState with posts
const [postsList, setPostsList] = useState(news);
const [page, setPage] = useState(1);
const handleClick = (categ: string) => {
setSelectedCateg(categ);
};
return (
<div className={styles.wrap}>
<nav className={styles.categories}>
//Cetegory list
{list.map((i: string, index) => (
<Link href={"/category/" + listEng[index]} key={index}>
<button
className={styles.active}
onClick={() => handleClick(listEng[index])}
>
<h2>{i}</h2>
</button>
</Link>
))}
</nav>
<div className={styles.catTitle}>
<h1>{newsTranslate(selectedCateg)}</h1>
</div>
<div className={styles.mainBlock}>
{postsList.map((i: News) => (
<div key={i._id} className={styles.normal_card}>
<div className={styles.normal_card_img}>
<Link href={"/news/" + i._id}>
<img src={i?.image} alt="" />
</Link>
<div className={styles.desc}>
<div className={styles.up_desc}>
<Link href={"/category/" + category}>
<h6>{newsTranslate(i.category)}</h6>
</Link>
<h6>| {moment(i.createdAt).format("LLL")}</h6>
</div>
<Link href={"/news/" + i._id}>
<h2 className={styles.titleDesc}>
{i.title?.length > 150
? `${i.title?.substring(0, 90)}...`
: i.title}
</h2>
</Link>
</div>
</div>
<div className={styles.normal_card_desc}>
<h4>{moment(i.createdAt).format("LLL")}</h4>
</div>
</div>
))}
</div>
<div className={styles.loadMoreButton}>
<button
onClick={async () => {
setPage(page + 1);
console.log(page);
const getNextPosts = await axios.get(
"http://localhost:3000/api/category/" + category,
{
params: {
page: page,
},
}
);
setPostsList([...postsList, ...getNextPosts.data]);
}}
>
LoadMore
</button>
</div>
</div>
);
};
export default News;
export const getServerSideProps: GetServerSideProps = async ({
params,
}: any) => {
const res = await axios.get(
`http://localhost:3000/api/category/${params.category}`,
{
params: {
page: 1
}
}
);
return {
props: {
news: res?.data,
category: params?.category,
},
};
};
I know that i can fix it like this:
useEffect(() => {
setPostsList(news)
}, [news])
but in my opinion its not the best way
postsList will only change value when you call setPostsList (except for the initial value which you're passing in on first load).
So after the category is changed, you'll need to fetch the posts from the API and call setPostsList (similar to how you are doing on page change)

How can I insert row div for every 3rd entry in props?

I've got a set of dashboards that display in bootstrap cards on a front page and I would like to wrap them in a div with the class row for every 3rd entry. I was thinking about marking my dashboard component with the DB id from props and use a modulus function, but that will cause problems if an ID is deleted
Dashboard component:
export type DashboardProps = {
id: number
title: string
description: string
}
const Dashboard: React.FC<{ dashboard: DashboardProps }> = ({ dashboard }) => {
return (
<>
<div className="col-sm-12 col-lg-4">
<div className="card bg-light h-100">
<div className="card-header">
{dashboard.title}
</div>
<div className="card-body d-flex flex-column">
<p className="card-text">
{dashboard.description}
</p>
<a className="btn btn-info text-center mt-auto"
onClick={() =>
Router.push("/dashboard/[id]", `/dashboard/${dashboard.id}`)
}
>Go to dashboard</a>
</div>
</div>
</div>
</>
)
}
export default Dashboard
Index page:
type Props = {
dashboards: DashboardProps[]
}
export const getServerSideProps: GetServerSideProps = async () => {
const dashboards = await prisma.dashboard.findMany({
orderBy: { id: "asc", },
})
return {
props: JSON.parse(JSON.stringify({ dashboards })),
}
}
const Index: React.FC<Props> = (props) => {
const { data: session, status } = useSession()
if (status === "loading") {
return (
<Spinner />
)
}
if (!session) {
return (
<Layout>
<AccessDenied />
</Layout>
)
}
return (
<Layout>
<h1>Dashboards</h1>
{props.dashboards.map((dashboard) => (
<Dashboard dashboard={dashboard} />
))}
</Layout>
)
}
export default Index
I could also potentially wrap them in a single div with class row, but I would need to enforce a top/bottom margin so the cards don't stack right on top of each other. Any tips to get me rolling on this would be greatly appreciated!
.map provides index, you this to find every 3rd element.
//...
{
props.dashboards.map((dashboard, index) =>
(index + 1) % 3 === 0 ? (
<div>
<Dashboard key={dashboard.id} dashboard={dashboard} />
</div>
) : (
<Dashboard key={dashboard.id} dashboard={dashboard} />
)
)
}

How can I create Single Page

How can I pass map items (title, category and images) in my id.jsx file.
Basically, I just want to create a single page for my projects. But I can only access post ID. I don't know how to pass other data items.
'Projects folder'
[id].js
import { useRouter } from "next/router";
const Details = () => {
const router = useRouter();
return <div>Post #{router.query.id}
// Single Project Title = {project.title} (like this)
</div>;
};
export default Details;
index.js
import { MyProjects } from "./MyProjects";
const Projects = () => {
const [projects, setProjects] = useState(MyProjects);
{projects.map((project) => (
<Link
href={"/projects/" + project.id}
key={project.id}
passHref={true}
>
<div className="project__item">
<div className="project__image">
<Image src={project.image} alt="project" />
</div>
<div className="project_info">
<h5>{project.category}</h5>
<h3>{project.title}</h3>
</div>
</div>
</Link>
))}
If I understand your question correctly, you want to send some "state" along with the route transition. This can be accomplished using an href object with the "state" on the query property, and the as prop to hide the query string.
Example:
{projects.map((project) => (
<Link
key={project.id}
href={{
pathname: "/projects/" + project.id,
query: {
id: project.id,
category: project.category,
title: project.title
}
}}
passHref={true}
as={"/projects/" + project.id}
>
<div className="project__item">
<div className="project__image">
<Image src={project.image} alt="project" />
</div>
<div className="project_info">
<h5>{project.category}</h5>
<h3>{project.title}</h3>
</div>
</div>
</Link>
))}
...
const Details = () => {
const router = useRouter();
return (
<div>
<div>Post #{router.query.id}</div>
<div>Title {router.query.title}</div>
<div>Category {router.query.category}</div>
</div>
);
};

Nuka Carousel not displaying in react slideHeight props 0

I am using nuka carousel in react TypeScript a saleor pwa react ts app
Nuka carousel not showing items cause nuka is passing slideHeight 0 to slider -frame
Code Example:
render() {
const { title } = this.props;
const { products } = this.state;
const productsList = products.map((product: any) => (
<Link to={'/product/' + product.id} key={product.id}>
<ProductListItem product={product} />
</Link>
))
return (
<div className="products">
<div className="container">
<h3>{title}</h3>
<Carousel>
{productsList}
</Carousel>
</div>
</div >
)
}
I solve it by just add if (products.length)
Solution:
render() {
const { title } = this.props;
const { products } = this.state;
if (products.length) {
const productsList = products.map((product: any) => (
<Link
to={'/product/' + product.id} key={product.id}
>
<ProductListItem product={product} />
</Link>
))
return (
<div className="products">
<div className="container">
<h3>{title}</h3>
<Carousel>
{productsList}
</Carousel>
</div>
</div >
)
}
return null;
}
There is no need to override css this is proper way
Here is solution Via Override css. this is for those who is interested in css override

React Sort By Like

I am trying to figure out how to add an onClick feature that will then sort the likes in descending order. AKA each project has a 'like' button. I want to add another button to the page to allow the user to sort the project likes by descending order.
import React from 'react';
import ProjectsListItem from './ProjectsListItem'
const Project = ({ projects }) => {
const renderProjects = projects.projects.map(project =>
<ProjectsListItem project={project} key={project.id}/>
);
return (
<div className="container">
<div className="row">
{renderProjects}
</div>
</div>
);
};
export default Project;
Page 2
class ProjectsListItem extends Component {
handleOnClick = () => {
this.props.likeProject(this.props.project)
}
onClick = () => {
this.props.sortBy(this.props.project.like)
}
render() {
return(
<div>
<div className="col-sm-4">
<div className="container-fluid text-left">
<h4> <Link key={this.props.project.id} to=
{`/projects/${this.props.project.id}`}>{this.props.project.title}
</Link> </h4>
<h5> {this.props.project.studio}</h5>
<CounterButton project={this.props.project} likeProject=
{this.handleOnClick}/>
</div>
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
projects: state.projects
}
}
export default connect(mapStateToProps, {likeProject})
(ProjectsListItem);
You would have to make an event handler such as
https://reactjs.org/docs/handling-events.html
In this case you would probably want to do
onSortClick(e) {
e.preventDefault();
this.props.sorted = true
}
bind that to your click handler like this:
<CounterButton project={this.props.project} likeProject=
{this.onSortClick.bind(this)}/>
Hope this helps.

Resources