i'm totally beginner in react.
I tried to improve my skill day after day.
Today im stuck on problem, i want to create dynamic route with JSON characters (here dragon ball z)
My routes are correct but i want to show biography on only clicked characters like "i click on goku show goku bio"
I want to make it without REACT HOOKS (dont useLocation, useParams ect..).
At moment i'm totally stuck
Can you help me ? how can i do?
Thanks for help :)
here is the blitzstack of my project:
REACT ROUTER DBZ EXERCICE - WITHOUT HOOKS
I don't know why you are using react-router-dom and then not really use it for what it was designed for. You are working with function components, so as far as I can tell, any solution will require a React hook. Whether you just use the useParams hook to get the id to filter by, or if you declare an id state in the parent with useState, or create a React context and use both useState and useContext, or use Redux and useDispatch and useSelector. Do you see where this is headed?
I suggest just using the useParams hook as it's the most trivial to implement.
Fix the character bio route so the id route match param is easier to read and consume.
<Route path="/CharBio/:id" element={<CharBio />} />
With path="/CharBio:id" the link would inject a leading : character into the id with to={`/CharBio${element.id}`}, i.e. instead of "goku" the id param would be ":goku", and this doesn't work easily for filtering.
Fix the link in Perso so it's linking to a "/CharBio/:id" path.
<Link to={`/CharBio/${element.id}`}>
<h1>{element.id}</h1>
</Link>
Use the useParams hook in the CharBio component and filter the API data by id.
export default function CharBio() {
const { id } = useParams();
const element = API.find(el => el.id === id);
return element ? <p>{element.bio}</p> : null;
}
Related
I am trying to implement a routing for my react project. No problem for the react router dom, but I am wondering how manage deep routing page's id, in order to be able to go back to previous pages.
Until now I have implemented useLocation and also useParams but I could not figure it out the proper way.
In my case I have 3 level pages "Main Category --> Category --> Sub category ".
Each category has multiple sub categories, so ID has to be dynamic.
The problem is, when I am in "Sub category" and I go back, react does not know which "Category" i came from.
Should I pass the "category ID" until the sub category page?
It is better use useLocation, useParams or Redux ? Or maybe you have others better solutions...
`const location = useLocation();
const catName = location.state?.name;
const catID = location.state?.id;`
<BackArrow to={`/catPage/${catID}`} title={catName}/>
If you want to go back to the previous page in react-router-dom v6 then you can use useNavigate.
import { useNavigate } from 'react-router-dom'
const Component = () => {
const navigate = useNavigatge()
return (
<button onClick={() => navigate(-1)}>Go back</button>
)
}
You can also change the number inside navigate to -2 if you want to go back two pages and so on.
Assuming your routing structure is made out of nested routes, you could use a relative path using react-router-dom's Link (Source) like so:
<Link to="../">Parent category</Link>
// OR in some cases
<Link to=".." relative="path">Parent category</Link>
Maybe take a look also at the useNavigate hook (React-dom doc)
Full example in codesandbox
I am using a library called Material-UI, to build up my app layout. I am using its <ListItemButton> component inside the sidenav of my app. Now. this component <ListItemButton> is not a link, its a button. but it has some styles, you can see how it looks here, click me. This component accepts a component prop, what this component prop does is that it basically allows you to render something inside of it, for instance, I can give it <div/>, I can give it <a/> I can give it<b/> etc etc, but I can also give it a React component. and that's what I am doing here. I am giving it the React router <NavLink> component.
Now. this is so easy, just give it the <NavLink> component and its going to work. But, unfortunately, it doesn't allow you to write <NavLink> as <NavLink>, you must write it as NavLink without the brackets, therefore, I can not pass any arguments to it.
Luckily, the way this Material-UI works, is that when it sees a prop inside one of its components such as the <ListItemButton> component that it doesn't understand, it automatically passes that prop to the child component. so in our case, if we wrote a prop that is not inside the <ListItemButton> documentation, it's going to pass it to the NavLink component.
hence, we can pass the to prop. as follows:
<ListItemButton to="/users" component={NavLink}>
So far so good. The last thing we have to make is to make sure that the ListItemButton component is going to be highlighted whenever the link in the search bar matches the NavLink specified path.
The good news is that the <ListItemButton> has a predefined prop that makes it looks on the screen as selected, so it highlights it a bit darker. this prop is called the selected prop, it accepts a Boolean, (true or false). SO:
How can we do this? we can do it by comparing if the URL in the searchbar matches the path we specified in the to prop.
Now this was the introduction of why I want to find out the currently used path.
So here's the question:
as I know, the only way to do this is to compare if the currently visited link is the same path as the NavLink or Link component by doing something like so:
<ListItemButton to="/users" component={NavLink} selected={pathname === "/users"}>
I know that this question was answered before, I saw the thread where people were mentioning that the way to do so is by using the useLocation() hook, they were saying you can do it like so: then do the comparison:
const { pathname } = useLocation()
However, this doesn't always work, for instance, if the user visited the following links, the ListItemButton component is Not going to be highlighted (selected):
http://example.com/users ✅ works
http://example.com/users/ ❌ doesn't work
http://example.com/users// ❌ doesn't work
http://example.com/users/// ❌ doesn't work
You might say, okay, simply compare using .includes() as follows:
<ListItemButton to="/users" component={NavLink} selected={pathname.includes("/users")}>
But remember, this shouldn't be if you have other routes in the sidenav such as:
Manage Users (/users)
Create User (/users/create)
because if you did the comparison using the .includes() method, you'll end up highlighting (selecting) both NavLinks (ListItemButton).
So, now, Is there a built-in React Router Hook, or method that can get the route name correctly, the exact name that is defined by me in the <BrowserRouter> in my index.js file?
& Thanks Yous 🌹
I see, you are trying to style the button and not really the NavLink component if I'm understanding your post correctly. I think you may be looking for the useMatch hook.
Returns true if the URL for the given "to" value matches the current
URL. This is useful for components that need to know "active" state, e.g. <NavLink>.
import { useMatch } from 'react-router-dom';
...
const isUserRoute = useMatch("/user");
...
<ListItemButton
to="/users"
component={NavLink}
selected={isUserRoute}
>
...
</ListItemButton>
Update
To use the useMatch hook for each ListItemButton I suggest factoring ListItemButton out into its own component.
Example:
const LinkButton = ({ children, to }) => {
const selected = useMatch(to);
return (
<ListItemButton
component={NavLink}
to={to}
selected={selected}
>
{children}
</ListItemButton>
);
};
Use the LinkButton instead of ListItemButton in the UI.
<div>
{listItems.map((item) => (
<LinkButton key={item.label} to={item.path}>
{item.label}
</LinkButton>
))}
</div>
If you prefer keeping the path matching localized to the parent component, import and use the useLocation hook and matchPath utility from react-router-dom.
Example:
import { useLocation, matchPath } from 'react-router-dom';
...
const { pathname } = useLocation();
...
<div>
{listItems.map((item) => (
<ListItemButton
component={NavLink}
to={item.path}
selected={matchPath(item.path, pathname)}
>
{item.label}
</ListItemButton>
))}
</div>
How can I always get the same part of the URL in react?
example:
http://localhost:3000/supplier/924511e8-9056-4c1e-9976-625bf042924e
I only want "supplier", but this can be anything else. So it's possible for it to be:
http://localhost:3000/product/924511e8-9056-4c1e-9976-625bf042924e
Then I want "product"
But it can also be just http://localhost:3000/supplier/ also in this case I only want the supplier. And this can be anything.
How do I do this? If I've already tried it with pathname.slice(0, pathname.indexOf("/") but this doesn't seem to work.
So I only want the string after the http://localhost:3000/want this/ no matter if there is anything after it or not.
You can use the split method as below:
const url = 'http://localhost:3000/supplier/'
const want_this = url.split('/')[3]
Just use useParams from react router dom
import {useParams} from "react-router-dom";
function Child() {
// We can use the `useParams` hook here to access
// the dynamic pieces of the URL.
let { id } = useParams();
return (
<div>
<h3>ID: {id}</h3>
</div>
);
}
I could not implement the Link component in server-side rendering.
<Link to={`/edit/${id}`}>
<h3>{description}</h3>
</Link>
In /edit page, I have this line of code to test the props that passed:
<h1>{props.match.params.id}</h1>
this throws an error because match prop is not passed.
If I used <a></a> instead of <Link/> wrapped /edit page with withRouter I get those props however this time I am disconnected from the store.
Since <Link/> navigates inside react-router looks like props that passed to components are cleared up when I click on <Link/>. I could not figure out how to solve the issue.
I added historyApiFallback:true to webpack.config devServer object but it did not solve the issue.
here is the repo
Your exact mistake is using href prop for the react-router Link component. you should use to, like below:
<Link to={`/edit/${id}`}>
<h3>{description}</h3>
</Link>
After fixing this issue, directly you will fall into another issue. you will get it inside EditExpensePage page:
const mapStateToProps = (state, props) => {
return {
expense: state.expenses.find(
expense => expense.id === props.match.params.id // here, you will get error the params of undefined
)
};
};
You will get params of undefined error because you wrap the withRouter by using react-redux connect, in fact, you should wrap react-redux connect by a withRouter:
export default withRouter(connect(mapStateToProps)(EditExpensePage));
After moving HOC wrapping the match key of props won't be undefined.
I developing front-end with reactjs recently.
but i have a problem in my SPA project.
my webapp have tab menu like a text-editor.
I want each tab to always keep their input value. However, these values are lost because tabs are re-rendering when they disappear or are new.
Is there a way to keep these values without using storage like local-storage?
You will need to take advantage of the useState hook to store an object that contains the data from your tabs... so you can get the data from the object whenever the state changes... assuming you don't update it wrongly.
Find illustratio below:
import React, { useState } from 'react'
function MyTabComponent (props){
const [tabData, updateTabData] = useState({})
return(
// your component jsx here
// you can then fetch your tab data from the tabData object
)
}
Hoping you find this helpful.