Create Unique Route for Each Blog Post React - reactjs

I'm working on a blog site using React which incorporates the use of a CMS, in this case Contentful.
All blog posts are written/saved on the CMS. I am currently able to populate all blog posts onto my site but it appears as a continuous page where this no separation between the posts. To avoid this, I was planning on creating a unique link for each blog post and having the list of all articles on the main blog page (example below), and having the ability to click on the desired post which takes you to a page dedicated to only that one post.
Is there a way to dynamically create a unique link for each post that I am retrieving from the CMS.
Main Blog Page
Blog Post #1
Blog Post #2
Blog Post #3
Have a unique link created for each one of these articles such that the url is such a manner: "testsite.com/blog/blogpost1"
Wasn't able to find a similar question on here, though it might be that I wasn't searching for the correct terms. Appreciate the help!
Update
const BlogPostList = ({ article }) => {
const { titleheadings, body } = article.fields
const title = titleheadings.replace(/\s+/g, '')
const postBody = marked(body)
return (
<Router>
<li>
<Link to={"/" + title}>{titleheadings}</Link>
</li>
<Route path={"/:id"}>
<Child title={titleheadings} body={postBody} />
</Route>
</Router >
)}
function Child(props) {
let id = useParams()
console.log(id)
return (
<div>
<Row className="justify-content-center">
<Col className="py-3" md="11">
<Card>
<Card.Body>
<Card.Title>{props.title}</Card.Title>
<Card.Text align="justify" dangerouslySetInnerHTML={{ __html: props.body }} />
</Card.Body>
</Card>
</Col>
</Row>
</div>
);
}
export default BlogPostList;

Contentful DevRel here. 👋
What you're looking for is a Router for React. A router allows you two to define URLs and routes inside of your application.
What a lot of people do is to have a dynamic route that takes a slug field as parameters. For react-router that could be like the following:
<Route path="/:slug">
<Page />
</Route>
Then in your Page component you would read out the slug URL param and fetch the Contentful entry you're interested in.

Related

React - How to show another page on clicking a React Material Datagrid

I am a complete newbie in React. Any help will be greatly appreacited.
I have a React MAterial Datagrid which list of users. When I click a row in the datagrid, it should show the in depth details of the user in another window. I should be able to edit the data, save the data and come back to the data grid again.
Edit: Here the application has a header and a sidebar which should also be displayed when routing to other page happens. The new page should open in the same window with the header and sidebar. In the component I have added in the element={User} I have implemented the logic to display the header and sidebar. The routing does not happen with this code. I find it difficult to identify the mistake I did.
I did implement the code like this and did debugging. The code flows through my function onRowClick, but no component is displayed. Please help me with the mistake I do.
Posting only the part of the code I work:
const Users = () =>{
const handleUserClicked: GridEventListener<"rowClick"> = async ()
..... // Here all the logic to retrieve the data for the Grid is implemented.
}
const openUserDetails = () => {
return (
<div>
<Routes>
<Route path="/" element={<User />} />
</Routes>
</div>
);
};
return (
<React.Fragment>
{selectedCustomer && (
<p> Ausgewählter Nutzer: {selectedCustomer.Username}</p>
)}
<DataGrid
rows={users}
columns={columns}
pageSize={16}
rowsPerPageOptions={[5]}
getRowId={(row: any) => row.name}
autoPageSize={true}
rowHeight={45}
onRowClick={handleUserClicked} {...openUserDetails} // I did try even adding on the onRowClick={openUserDetails}
hideFooterSelectedRowCount={true}
/>
</React.Fragment>
);
}
enter code here
if the result of getRowId is id and this id is equal to your record primary key id you can do like this:
for example, your back-end server for getting post detail is: https://exampl.com/posts/{id}
if you want to see detail in a new tab, you can put the MAU datagird given id into this URL and then open this in a new tab by:
window.open('/yourUrl with post id','_blank')
if this description is not your answer, you should ask your questions better
You can navigate to another path by using useNavigate
import { useNavigate } from "react-router-dom";
const navigate = useNavigate();
const openUserDetails = () => {
navigate("/<your path here>");
};
<DataGrid
rows={users}
columns={columns}
pageSize={16}
rowsPerPageOptions={[5]}
getRowId={(row: any) => row.name}
autoPageSize={true}
rowHeight={45}
onRowClick={openUserDetails}
hideFooterSelectedRowCount={true}
/>
useNavigate documentation

React-js undefined props dynamic page when accessing URL directly

I'm trying to create a webshop with React as front-end framework and have gotten stuck on the routing of the products. I currently have all the products in a json table and import these into my webshop which works fine when the product page is accessed through the link but whenever I go to the product page directly I get an error stating that my props are undefined. I have
<Router>
<Switch>
<Route exact path="/products/:productId" component={Product} />
</Switch>
</Router>
as my router to the product page and link to the page using:
<Link to ={{pathname:`/products/` + product.url, ProductdetailProps:{title: product.title, description: product.text, image: product.image}}}>
The Product page looks like this:
export const Product = ({location}) => {
return (
<div>
<img src={location.state.image} alt={backupImage}/>
<p>{location.state.title}</p>
<p>{location.state.description}</p>
</div>
);
};
Should I keep on "creating" the product pages this way and if so can someone help me with this or should make each product page seperately and link those?

How to Use Router with 2 different components?

I'am having issues trying to figure it out how to use Router with more then one Wrapper Component.
So my app has a Wrapper component that makes a get call validates the token and has a url parameter. This url is the link to fetch from my api.
I have a users.jsx file that lists the users and a userdetails.jsx file that lists the details of each user. The key is the CN.
My users.jsx already has a dynamic link that uses the username(cn) for each user in the list.
Now my app renders the user list in my users.js file using the Wapper component and passing the url to fetch for the user list.
What i need is to have the userDetails integrated here.
users.js
const IndexPage = () => (
<Layout>
<MainComponentWrapper url="http://localhost:5000/user">
<User />
</MainComponentWrapper>
</Layout>
)
My other url is: http://localhost:5000/user/<cn>to fetch for the details.
I was trying to come with a code like this, but it doesn' work:
const IndexPage = () => (
<Layout>
<Router>
<div>
<MainComponentWrapper url="http://localhost:5000/user">
<User path="/users" />
</MainComponentWrapper>
</div>
<div>
<MainComponentWrapper url="http://localhost:5000/user/<cn>">
<UserDetails path="/users/:cn" />
</MainComponentWrapper>
</div>
</Router>
</Layout>
)
I want to navigate to the user details if i click on a user in the list
Also i am using gatsby plugin :
{
resolve: `gatsby-plugin-create-client-paths`,
options: { prefixes: [`/user/*`] },
},
```
refer this react-router-exact , read the first answer , then read the second answer , you need to undestand the concept of exact (its described in answer 1) in react router then you have to combine it with the switch (in the answer two) statement .

Gatsby page in page

I'm using gatsby js and trying to figure out how to have a page level side bar with Gatsby links that render a new component inside a div in the same page I can do this using react-router-dom but in Gatsby all I can find is how to create blog posts which is driving me nuts as every tutorial I find is the same blog post.
Here is my layout page /layouts/index.js
export default ({ children }) => (
<div id="layout">
<header>
<h3>Header</h3>
<MainNav />
</header>
{children()}
</div>
)
About Page
/pages/about.js
export default ({ location, match }) => {
console.log('location = ', location, 'match = ', match );
return (
<div id="about">
<SideBar />
<div id="content">
// . add child template or component for link clicked in sidebar
</div>
</div>
);
};
What I'm trying to do is when a user clicks on a link in the side bar stay on about but render a new component or template based on the gatsby-link clicked in the about sidebar.
The About SideBar component
/components/about/side-bar.js
const SideBar = () => {
return (
<div id="side-bar">
{/* <li><Link to='/about?sort=name'>work</Link></li> */}
<li><Link to="/about/work">work</Link></li>
<li><Link to='/about/hobbies'>hobbies</Link></li>
<li><Link to='/about/buildings'>buildings</Link></li>
</div>
)
}
Problem with the links above, they are trying to go to a new page called.
/about/work
This is not what I'm trying to do. Again I'm trying to make it stay on about but render a new component inside the content div.
Please help gatsby is so all over the place as far as docs goes. ok maybe its just me and not getting the docs clearly.
Thanks
UPDATE:
I tried adding a page suing createPage which works for me kind of but it doesn't pass the match.params id
gatsby-node.js
exports.createPages = ({ boundActionCreators }) => {
const { createPage } = boundActionCreators;
const myComponent = path.resolve('src/pages/about/index.js');
createPage({
path: '/about/:id',
component: myComponent
})
}
After a long time of trying to understand Gatsby and I can say I still don't as its docs are vast and not very clear. But once I started to look at the node-apis and onCreatePage it gave me some ideas. This is what the docs literally say.
onCreatePage
Called when a new page is created. This extension API is
useful for programmatically manipulating pages created by other
plugins e.g. if you want paths without trailing slashes.
So the only part in here that gives me a hint of this might be the key to helping me is this line. useful for programmatically manipulating pages created by other
plugins
Anyway this got me writing some code at least. Then about 3 hours later I found a plugin that was doing exactly what I was trying to do with this method. The plugin is called gatsby-plugin-create-client-paths key here is client-paths!!!!!
This makes life worth living! So in my case above I just wanted to be able to use Gatsby's router ( which is just react-router-dom behind the scenes), to pass me and id or value to routers match.params object. It still doesn't but what it does do is checks for any path after a prefix like /folder/ in my case '/about/work and recreate the page with a template component (in my case keep using pages/about/index.js), which is my template. Now that we have about/index.js rendering for ever link after /about/ then we can use some internal switch statement to handle the location that is been passed to /about/index.js. Still don't get match.params update but I do get props.location.pathname; which allows me to extract everything after the prefix to use in a switch statement to render my specific components based on the routes pathname. Enough rabbiting on here is a rough solution to show as an example.
So add the plugin as an npm install.
open up gatsby.config.js and add the below code to the exports.
module.exports = {
plugins: [
{
resolve: `gatsby-plugin-create-client-paths`,
options: { prefixes: [`/about/*`] },
},
]
}
Then in my main about page pages/about/index
import React from "react";
import SideBar from '../../components/about/side-nav';
export default (props) => {
const { pathname } = props.location;
var n = pathname.lastIndexOf('/');
var pageId = pathname.substring(n + 1);
const page = () => {
switch(pageId){
case '':
return (
<div>Work Page</div>
);
case 'work':
return (
<div>Work Page</div>
);
case 'hobbies':
return (
<div>Hobbies Page</div>
);
case 'buildings':
return (
<div>buildings Page</div>
);
}
}
return (
<div id="about">
<SideBar />
<div id="content">
{page()}
</div>
</div>
);
};
Then in my sidebar I call it like this.
<li><Link to="/about/work">work</Link></li>
<li><Link to='/about/hobbies'>hobbies</Link></li>
<li><Link to='/about/buildings'>buildings</Link></li>
Hopefully this will help someone else out. After all this I'm starting to really question the bulk of gatsby especially with docs not been very clear. Based on the response to my question I guess not many people in stackoverflow's community are using Gatsby which is worrying when you need help. It does look like Gatsby's github community is very helpful but that should be for bug issues and not for questions like mine, but encouraging to see.
Hope this helps someone.

switching between certain component on a page using nested route in react router

I am trying to create a job site. Following pages shows list of all the jobs which is shown once user hits search button from home page. So basically this is the second page.
In this page i am catching all the search parameter from url and fetching data from api and result is shown as below:
Once the user clicks individual joblist, detail page should load on the same page without changing header and fixed component with unique URL for the detail page. Expected result shown below:
My Problem:
I manage to create a nested Route, which renders detail page on the same page and also has a unique url. But it renders on top of existing job list. I mean if user clicks on joblist1, detail page renders on top of subsiquent list(above list: 2, 3, 4). But expected result is to only render detail page but not list of jobs when individual job list is clicked.
My code: I have only shown part of the code for brevity and simplicity.
1) jobs.js: Passes state data to child component to show list.
return(
<div>
<div>
fixed component
</div>
<div>
<RouteHandler />
<JobLists joblists={this.state.joblists} />
</div>
</div>
)
2) jobList.js: uses .map function to go through all data and handleclick function generate url and opens that url once user clicks individual link. Router catches nested route and loads value inside jobs.js in " ".
handleClick: function(i){
var base_path = window.location.protocol + '//' + window.location.host;
base_path += '/#/jobs-detail';
window.location= base_path;
},
render: function(){
var jobListRow = this.props.joblists.map(function(jobrowobj, i){
return(
<div key={jobrowobj.id} onClick={this.handleClick.bind(this, i)}>
<img src={jobrowobj.logo} alt="" />
<h3>{jobrowobj.title}</h3>
</div>
)
}.bind(this));
return(
<ul id="joblists">
{jobListRow}
</ul>
)
}
3) Route file:
var routes = (
<Route handler={App}>
<DefaultRoute handler={Home} />
<Route name="jobs" path="jobs" handler={Jobs}>
<Route name="jobs-detail" handler={JobDetail} />
</Route>
<NotFoundRoute handler={NotFoundPage} />
</Route>
);
I am not sure what is the best way to switch certain section (component) on a page as in my case switching between joblist component and jobdetail component. As you can see i am only able to load other component on top of existing component which is not the expected result.
Also would appreciate if any hint is given to maintain scroll position on the job list on user hitting back button.
I suggest you to upgrade your react-router to 1.0.0-rc1, and the API is more clear. Your problem is similar to the official introduction. The nested component will be passed as this.props.children, and you can insert it into the jobListRow.
About the scroll position, there's a github issue discussing how to restore it :)

Resources