Editing a useParams ID to use as a proper title in React - reactjs

Here's my Routing
<Route path="/marvel" element={<Marvel />} />
<Route path="/marvel/:id" element={<MarvelDetail />} />
Here's my Marvel page
<a href="/marvel/captain-america-civil-war">
Captain America: Civil War
</a>
Here's my Detail page
const { id } = useParams();
<div className="marvel__main-topbar">
<span className="marvel__main-title">{id}</span>
</div>
I have a main page that redirects to any number of topics relating to a specific Marvel movie. If you take a look at the Detail page code you'll notice I have a span with { id } for the title which displays at the very top of each page. The routing works fine and I'm redirected to the right page, however instead of having the title be Captain America: Civil War it makes it captain-america-civil-war. I know it does this because it's just copying the route. But how can I make each page have their own proper title instead of one that uses dashes?
What I've Tried
I changed the id to include the replace method but that didn't fully work because it doesn't know when to include a colon or any other special characters.

What you could do is create a separate file that contains an array of marvel objects with the id AND the proper title like so :
marvelDetails.js
const marvelDetails = [
{
id: 'captain-america-civil-war',
title: 'Captain America: Civil War'
},
];
In your Details page:
const {id} = useParams();
// Don't forget to import the marvelDetails.js file
const title = marvelDetails.find((detail) => detail.id === id).title
<div className="marvel__main-topbar">
<span className="marvel__main-title">{title}</span>
</div>

Related

How to provide dynamic content pages in react?

After submitting a form I need to display a success page. It's content is determined by the form values and it consists of headline, static text and a form value. In the end 5 different success pages are possible. How can I best provide the data in a separate success page? Do I need 5 different success pages or is there dynamic solution possible?
Routes
<Route path="/form" element={<MyForm/>} />
<Route path="/success" element={<SuccessPage/>} />
success page templates
<h1> Headline A </h1>
<p> Text A <p>
<span> {form value name} </span>
<h1> Headline B </h1>
<p> Text B <p>
<span> {form value name} </span>
There are multiple solutions to this problem.
You can put the form values on the query string. Then use that to display content.
import { useLocation } from 'react-router-dom';
// http://localhost:4000/success?valueA=101
const SuccessGenericComponent = () => {
const search = useLocation().search;
const valueA = new URLSearchParams(search).get("valueA");
console.log(valueA); //101
return (
<h1> Headline A </h1>
<p> Text A <p>
<span> {valueA} </span>
)
}
You can use a state management library like Redux, React.Context, or even just your localstorage to handle the state of your form and access it in a different component. This will involve an initial setup though.
You can use generic component for this usecase, as your template data is same, No need for different pages.
You can add conditional rendering to differenciate the page or define a type of page you need from service and display based on that.

problem rendering items with props.match.params.id

I am trying to implement a simple query string matches to show the appropriate item.
here is the component which will render further info on a listed item:
const Product = (props) => {
console.log(props.match.params.id)
console.log(data.products)
const product = data.products.find((x) => x.id === props.match.params.id);
if (!product) {
return <div>product not found!</div>;
}
while both of thoese consule logs give me what i want the product does not seem to be found. here is my product list:
const data={
products:[
{
id:1,
brand:'Nappies',
price:1200
},
{
id:2,
brand:'Knitted Goods',
price:1400
},
{
id:3,
brand:'Baked Treats',
price:1100
},
{
id:4,
brand:'Clothes',
price:1100
}
]
}
and i am matching the querys with this ...
<Link to={`/product/${product.id}`}><button className="card bg-gray-700 hover:bg-red-600 transition text-white w-full h-1/6 absolute bottom-0">See More</button></Link>
the above button is the button a user clicks to see more on that item (price, name, stock, etc)
when clicking that link it takes me to the appropriate page url. (http://localhost:3000/product/2 )but its giving me the "no item found visually on the page"
this is how my app.js is set up... fairly simply:
<Nav />
<Switch>
<Route exact path='/'>
<Homepage />
</Route>
<Route path='/product/:id' component={Product}/>
</Switch>
</Router>
it seems to be like the find method isn't working? such a bizarre problem. am i missing something really obvious? been coding about a year and still find myself stumped on simple react stuff lately :/ so discouraging.
its not rendering any of my info after that in the individial product component
shown down below:
<li>
<div className="row">
<div>{product.name}</div>
<div className="price">{product.price}</div>
</div>
</li>
essentially i want the above code rendered after clicking one of the listed items. its saying "cannot get id of undefined".... seems to be something with me find method but even then i still can't figure out what could be wrong?!?
propps.match.params.id seems to be a string
but, when your looks for a particular product, you use strict match
const product = data.products.find((x) => x.id === props.match.params.id);
your id is number, '2' === 2 is false
So, try
const product = data.products.find((x) => x.id === Number(props.match.params.id));
Do use useParams hook to retrieve id as
const Product = (props) => {
let { id } = useParams();
return (
<div>
<h3>ID: {id}</h3>
</div>
);
}
For further details check here https://reactrouter.com/web/example/url-params

Fetching Url type id with useParams (React Router)

I'm fetching news articles from The Guardian API. Article id's are like this: "world/live/2020/dec/31/coronavirus-covid-live-news-updates-vaccine-lockdown"
I need to go from article summary to article details and I'm using React Router for this. If I use const { id } = useParams(); and console log it, it gets only gets "world" form the id. How can I get the full id?
<Route path="/:id" component={ArticlePage} />
{topStories.map((story) => (
<Link to={`/${story.id}`}>
<ImageCard key={story.id} story={story} />
</Link>
))}
Issue
Path "/:id" will only match the first path "segment", i.e. "world" from "world/live/2020/dec/31/coronavirus-covid-live-news-updates-vaccine-lockdown".
Solution
If the path is "world/live/2020/dec/31/coronavirus-covid-live-news-updates-vaccine-lockdown" and you are trying to get coronavirus-covid-live-news-updates-vaccine-lockdown as the id then you need to define a route path that matches what you want to extract.
Something like
<Route path="/world/live/:year/:month/:day/:id" component={ArticlePage} />
Now using the params you can extract any of these, including the id.
const { day, id, month, year } = useParams();

Create Unique Route for Each Blog Post React

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.

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.

Resources