Conditional display of component based on Route matching - javascript

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.

Related

Creating a custom React Native component which automatically wraps inner text substrings with another component

I have the following component structure being rendered:
<Text>
Hello <UsernameWrapper>#gaberocksall</UsernameWrapper>! Nice to meet you.
</Text>
I would like create a custom component which automatically places #usernames into a UsernameWrapper, like this (and it should be rendered identically to the above snippet):
<AutoUsernameText>Hello #gaberocksall! Nice to meet you.</AutoUsernameText>
Or, even more generally, something along the lines of:
<MagicWrapper wrap={/#\w+/gi} wrapper={UsernameWrapper}>
Hello #gaberocksall! Nice to meet you.
</MagicWrapper>
How would I go about creating the MagicWrapper component in React Native?
We could just create a new component where we pass the username as a prop. In my opinion that is the way to go in react-native. Here is a quick implementation.
export const AutoUsernameText = (props) => {
return (
<Text>
Hello <UsernameWrapper>#{props.name}</UsernameWrapper>! Nice to meet you.
</Text>
)
}
Then, we can just use it as follows.
const SomeOtherScreen = () => {
return (
<AutoUsernameText name="gaberocksall" />
)
}
Notice that this solution depends on how UsernameWrapper is actually implemented. Since I do not know that, I assume plain strings here.
If you really want to pass it as a child, e.g.
<MagicWrapper>Hello #gaberocksal! Nice to meet you.</MagicWrapper>
then AutoUsernameText needs to parse its children and return the component you want instead. This is possible, although I do not see any good reason why one should do that. It opens many pitfalls that are not easy to avoid.
Nevertheless, we can achieve this as follows assuming that the child is a simple string using plain JS regular expressions.
export const MagicWrapper = (props) => {
// Notice that I do not guard here for potential errors ...
// props.children could be anything ...
const child = props.children
const name = child.match(/#\S+?(?=!)/g)
const content = child.split(name)
return (
<Text>
{content[0]}<UsernameWrapper>{name}</UsernameWrapper>{content[1]}
</Text>
)
}
In order to splitting specific Regex pattern like as username, url and etc, you are able to use react-native-parsed-text package. For example it's will help you changing color of the Regex pattern (also defining custom styles) or defining onPress function to doing some things. I hope it's be useful.
react-native-parsed-text github page.

How to open detail view on table row select in react.js?

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

How to create routes with dynamic params in gatsbyjs [duplicate]

I have setup gatsby project using this link. It is working correctly.
Now I know how to create route by defining the component inside the pages folder. But now I have a new challenge I need to create one dynamic route so that I can pass my id in it (Just like reactjs).
<Route path: "/path/:id"/>
How do I do that in gatsby?
You have to explicitly tell gatsby that a path should be dynamic. From the docs:
// gatsby-node.js
// Implement the Gatsby API “onCreatePage”. This is
// called after every page is created.
exports.onCreatePage = async ({ page, actions }) => {
const { createPage } = actions
// page.matchPath is a special key that's used for matching pages
// only on the client.
if (page.path.match(/^\/app/)) {
page.matchPath = "/app/*"
// Update the page.
createPage(page)
}
}
and then you can use dynamic routing in src/pages/app.js
import { Router } from "#reach/router"
const SomeSubPage = props => {
return <div>Hi from SubPage with id: {props.id}</div>
}
const App = () => (
<Layout>
<Link to="/app/1">First item</Link>{" "}
<Link to="/app/2">Second item</Link>{" "}
<Router>
// ...dynamic routes here
<SomeSubPage path="/app/:id" />
</Router>
</Layout>
)
export default App
Everything that goes to /app/* will be handled dynamically now. You should find your id as usual in the props.
Have a look at their authentication example https://github.com/gatsbyjs/gatsby/tree/master/examples/simple-auth
You can use square brackets ([ ]) in the file path to mark any dynamic segments of the URL. For example, in order to edit a user, you might want a route like /user/:id to fetch the data for whatever id is passed into the URL.
src/pages/users/[id].js will generate a route like /users/:id
src/pages/users/[id]/group/[groupId].js will generate a route like /users/:id/group/:groupId
Reference: https://www.gatsbyjs.com/docs/reference/routing/file-system-route-api#creating-client-only-routes
You can use gatsby-plugin-create-client-paths. It uses matchPath. For more info check
https://www.gatsbyjs.org/docs/gatsby-internals-terminology/#matchpath
https://www.gatsbyjs.org/packages/gatsby-plugin-create-client-paths/
This answer is Super late, but for anyone in the future who is faced with this problem, I have a simpler solution.
In Gatsby terms it's called a Splat Route.
For examples, If you want some page "domain.com/profile/[id]", where id can be any number, which will be used to display different data inside the website, you should name your page as [...id].
Now inside the page you can access this id as
const ProfilePage = (props) => <div>This page is for id number {props.params.id}</div>
Note: Don't miss the 3 dots, that is what signifies a splat route in gatsby.

Pathname conditional rendering in React

How can I conditionally add a component to the render on certain pathname?
Using const pathname = this.props.location.pathname I identify the current pathname
Then I get the slugs of the pages I want to identify using
const specialPages = get(this.props, 'data.allContentfulSpecialPages.edges')
I then organise the returned data by
const special = specialPages.map(({ node: page })=> (
`/parent/${page.slug}`
))
and this returns the page slugs as
["/parent/page1", "/parent/page2", "/parent/page3", "/parent/page4", "/parent/page5", "/parent/page6"]
All seems good to now but when I try to add add
let PageHeader;
if (pathname !== special) {
PageHeader =
<Header/>;
} else {
PageHeader = null
}
it doesn't do remove the <Header/> for pathname identified in special
Have I not correctly defined each in the array?
Edit - I have just noticed the issue but unsure of the fix.
const special is returning as /parent/page1/parent/page2/parent/page3/parent/page4/parent/page5/parent/page6
When adding to console.log(special) I receive
0:"/parent/page1"
1:"/parent/page2"
2:"/parent/page3"
3:"/parent/page4"
4:"/parent/page5"
5:"/parent/page6"
length:6
__proto__: Array(0)
So I believe I need to map these differently.
If I'm understanding correctly, you want to conditionally add a component to the render based on pathname. A common pattern I've seen for this is to use the and operator with the condition you're looking to check and the component to render if need be. Something like this.
{{ pathname !== special && <Header/> }}
This will not display anything if the condition is not true and will display Header component if condition is true.

Get path params in react-router v4

I'm trying to build a router link through my application,
In this scenario, I have three files.
App.js
Book.js
DetailedView.js
I have inside of Book built up a <Link> that only appears when hovered ( over a book cover )
{this.state.isHovered ? (
<Link to={`/details/${this.props.book.industryIdentifiers[1].identifier}`}>
<div className="hover-box"></div>
</Link>) : ( <div /> )}
This will take me to a /details/12345 (isbn10 number)
The thing I have a hard time to understand is how to for example
setState({iPressedThisBook}) when pressing <Link> or if i can use the part after /12345 to create like a filter
Because in App the Route will be hooked up as...
<Route path="/details/:id" render={() => (
<BookDetailedView
bookStateUpdated = {this.bookStateUpdated}
book = {this.state.books}
/>
)}/>
I, later on, want to grab the :id so that I make for example a this.props.book.find(:id) inside of my <BookDetailedView>
In order to receive the path param in you component, you need to first connect your component with withRouter HOC from react-router so that you can access the Router props and get the path params from the match props as this.props.match.params.id
Sample Code:
import {withRouter} from 'react-router';
class BookDetailedView extends React.Component {
render() {
var id = this.props.match.params.id
}
}
export default withRouter(BookDetailedView) ;
or simply passing it with render prop in route as
<Route path="/details/:id" render={({match}) => (
<BookDetailedView
bookStateUpdated = {this.bookStateUpdated}
book = {this.state.books}
id={match.params.id}
/>
)}/>
From the React Documentation of match
match
A match object contains information about how a <Route path> matched
the URL. match objects contain the following properties:
params - (object) Key/value pairs parsed from the URL corresponding to the dynamic segments of the path
isExact - (boolean) true if the entire URL was matched (no trailing characters)
path - (string) The path pattern used to match. Useful for building nested s
url - (string) The matched portion of the URL. Useful for building nested s
You’ll have access match objects in various places:
Route component as this.props.match
Route render as ({ match }) => ()
Route children as ({ match }) => ()
withRouter as this.props.match
matchPath as the return value
If a Route does not have a path, and therefore always matches, you’ll
get the closest parent match. Same goes for withRouter
You can access :id by doing
this.props.params.id
you can handle routing many ways, you don't have to do a
you can also handle it in a function .
function doSomethingWhenClicked(id)
{
doSomething();
this.props.history.push({
pathname: '/example/'+id
});
}
and bind this function on a onclick element

Categories