I have a table with multiple rows and when I select a row, I want to display detailed information about the selected item.
I make use of react-router-dom v6 and MUI's material table component.
Below a prototype:
I make use of routes to switch content on menu item click. The next thing is to open a detailed view, but I'm not sure what pattern to use. I was thinking of using routes, like a nested route, but I don't know how to pass the selected object to the detail view that shows the object in more details. Ofcourse there are multiple ways to do it, but don't know what's the best practice.
So what's the best practice to achieve this and how?
UPDATE
I can solve my problem like the snippet below. It works, but I don't really like this solution.
const App = () => {
const [activeRow, setActiveRow] = useState({});
return (
<StrictMode>
<Table setActiveRow={setActiveRow} />
<Userdetails activeRow={activeRow} />
</StrictMode>
);
};
The best way to do this is using useParams() with a dynamic path
first, create a dynamic route path
Example code
<Route path="invoices" element={<Invoices />}>
<Route path=":invoiceId" element={<Invoice />} />
</Route>
please note:
We just created a route that matches URLs like "/invoices/2005" and
"/invoices/1998". The :invoiceId part of the path is a "URL param",
meaning it can match any value as long as the pattern is the same
now you can invoiceID in your <Invoice> as the example
let params = useParams();
return <h2>Invoice: {params.invoiceId}</h2>;
for more details your can follow React Router V6 guide
Related
I am looking to conditionally render a component based on the route (using React Router), and the component should return null if it matches any path pre-defined in an array or some sort of similar data structure, where I do not have to be reliant on a <Switch>/<Route> setup. Currently here is what I have but it is clearly inefficient and not robust at all.
const Component = (props) => {
const path = props.location.pathname;
const paths_to_hide = ["/path/to/something", "/path/to/A", "/path/to/B"];
if (paths_to_hide.indexOf(path) != -1) return null;
return (
<div>test</div>
);
}
For example, if I want to match the following paths:
/path/to/something
/path/to/something/<any path that follows after this>
/path/<random string>/fixed
/newPath/<random string>
Note that this list is not just limited to 4 items, which is why I'm trying to stray away from having inline <Route> matching as I'm looking for a more scalable approach which I can save in a config file and have imported as an array or some similar data structure.
Currently my implementation will only be able to identify the first item, and there is no way to match the subsequent items, using the indexOf() function. What would be the best way to accomplish this? Any help is appreciated, thank you!
So upon reading the React Router docs further, I found this. This is definitely the most ideal solution and I overlooked this initially.
const Component = (props) => {
const path = props.location.pathname;
const paths_to_hide = ["/path/to/something", "/path/to/A", "/path/to/B"];
return (
<Switch>
<Route path={paths_to_hide}>
</Route>
<Route>
<div>test</div>
</Route>
</Switch>
);
}
So now I can create complex paths and don't have to loop through an array to match them, as it's taken care of by the Route component, and this is ideal because now I can import this array from a config file instead.
// the routes here
<Switch>
<Route exact path='/:page' component={Dashboard} />
<Route exact path='/:subCateg/:categ' component={Dashboard} />
</Switch>
I want to go from the first route to the second one, if I used (history.push) or (Link) just the URL change and not reloading.
Also, the same problem if I want to go from the second route to itself but with new params.
//here I'm in Dashbord and the History also pushing to Dashboard
onClick={
()=>{
history.push({pathname:`/${categ}/${subCateg}`, state: {id: subCateg.id}})
// window.location.reload(false);
}
}
as you can see I userd "window.location.reload(false);" and it solved the loading problem, but what if I want to Goback by browser Goback button, the same problem : change URL & not relaoding.
also I think using "window.location.reload(false);" is not a good practice.
``
package.json:
"react-router-dom": "^5.1.2",
As you are using react-router-dom you should not be using history.push(). Try to use Redirect from react-router-dom instead.
Just use a state named redirect and set it to the new route you want to go with your function:
onClick={() => this.setState({ redirect: 'newpath' }};
Then just put a return inside your render just like this:
if (this.state.redirect) {
return <Redirect to={this.state.redirect} />;
}
return (
...rest of your render
);
Caveat: I'm dangerously new to React as well as much around it, but have been pulling my hair out on this for the last day myself. So there are a number of reasons this may be very bad advice. But this was my situation and how I got it to work.
function newSongRedirect() {
history.push("/SongDetail/0");
history.go(0);
}
and hook it up to my button:
<button onClick={() => newSongRedirect() } >New Song</button>
It appears that history.push adds the value to the history, but doesn't cause it to be acted on. As now the first item, history.go(0) then causes it to act on it.
I've seen some posts referencing that history.push can't be used if you're nested in a <BrowserRouter>, but in my case, attempting to replace that in my index.js just led to other issues.
Also, I found this post from what looks to be part of the team working on react router. It seems the useHistory hook will be replaced when they get a chance.
Heads up: The useHistory hook is a quick stopgap for a future hook that we are working on: useNavigate. useNavigate will provide an API that is more closely aligned with and will fix a few long-standing problems with using the history API directly in the router (it'll probably look a lot like #reach/router's navigate API). We are providing useHistory for now to make the migration of existing code that uses the history API as painless as possible.
With react router I have done this up update the url:
this.props.history.push({
pathname: `/product/${this.props.product.id}`,
});
However this causes a re-render/navigation change. Is there a way to update the url without doing that?
My use case is I have a list of products and when I click on one I show a Modal and want to update the url to something like example.com/product/1 so that the link is sharable.
Wasn't able to find a solution using React Router, but was able to accomplish this using the browser's history interface by calling:
window.history.replaceState(null, "New Page Title", "/pathname/goes/here")
You can learn more about .replaceState here.
React Router's history.replace won't always work
React Router's history.replace method may or may not trigger a re-render depending on your app's route setup (e.g., you have a catch-all /* route). It does not prevent a render by definition.
replace will override the URL
this.props.history.replace({ pathname: `/product/${this.props.product.id}`})
I'm using a nice way of tricking the users that's the URL doesn't change.
To do this, you'll just need to setup your routes like this.
const Home = '/';
const About = '/ ';
const Contact = '/ ';
Notice the spaces. All characters will be counted inside the quote so it should work.
Routes.tsx
<Route exact path={Home} component={Home} />
<Route exact path={About} component={About} />
<Route exact path={Contact} component={Contact} />
Inside your About.tsx and Contact.tsx:
useEffect(() => {
window.history.replaceState(null, '', Home);
}, []);
After the user navigates to About or Contact pages, the hook will be called and run the code inside the callback function. It will remove the spaces after the component rendered.
That should be clean trick to disguise hidden URL 😂
Well, I try to understand legacy code with React routes onboard.
I have an url like
/home/reports/some_report_numbers
when user changes the ulr like that:
/home/reports/some_report_numb
I want to check if "some_report_numb" exists in props, in this.props.location.pathname. If exists - fine, go that way, otherwise go another way.
Maybe it is a wrong approach at all? Well, when the report is shown and user just deletes some letters in url I need to redirect to /home/ page. To do that I need somehow to check if that report with that numbers exists at all.
Maybe it could be done via
<Route name={SomeRouteName} path='reports/:reportId' component={HomePage} />
According to the documentation of react-router-dom
Link: https://reacttraining.com/react-router/web/example/url-params
match become an object that can be used in your component. Like explain in the documentation, you have access to match object with the following steps:
Route component as this.props.match
Route render as ({ match }) => ()
Route children as ({ match }) => ()
withRouter as this.props.match
matchPath as the return value
For example, if you have the following route:
<Route path="/reports/:reportId" component={HomePage} />
In your component, you can access to this with: this.props.match because it's an object and inside it, you will have what you want.
After that, you could check what params you have in your URL.
Now, if you want to redirect the user, you can use <Redirect to={'/your/path'} />
<Route path='/change-password/?resetToken=(:token)' component={()=><h1>testing</h1>} />
Above route don't render when I hit the url below?
http://localhost:3000/change-password/?resetToken=123
I also tried path='/change-password/?resetToken=:token'
So the main problem seems to be with the question mark in the path. But first you need to write :token instead of (:token), here is an example format of path with params on github docs of the react-router.
I followed this github post about no way to set reserved characters in the path name, but it didn't lead me to anything. One way you could solve the problem is to define your route like /change-password and then in the actual component do this.props.location.search.split("=")[1] to get the value of the token from the search query. Here is an example:
class ChangePassword extends React.Component {
componentDidMount() {
// get the token, check if it exists and do smth with it if it does
console.log(this.props.location.search.split("=")[1]);
}
render() {
return (<h1>Change password</h1>);
};
}
const App = () => (
<Router>
<div>
<Route path='/change-password' component={ChangePassword} />
<Route path='/home' component={ChangePassword} />
</div>
</Router>
);
It seems that react-router doesn't handle ? (or other reserved characters, haven't tested) in the path name, or I am seriously missing out something here and there is some magic option that makes it work :) I didn't find one but I made a working example with the code I provided on codesanbdbox with your path in the Route.