I have a React component that is clickable as a whole, but also contains buttons inside.
So something like
<Link to={'/path1'}>
...
<Link to={'path2'} />
...
</Link>
This is the behaviour I want, but I get this warning:
Warning: validateDOMNesting(...): <a> cannot appear as a descendant of <a>. See SearchResult > Link > a > ... > Link > a.
How seriously should I take this and what would be the workaround?
Nesting anchor tags (which is what <Link /> transpiles to) is forbidden in HTML. Even if you get the desired result, it is only because your browser is clever and has its own work-arounds. However, that behavior cannot be guaranteed across all browsers and platforms.
How seriously should I take this?
As per the above, I'd say quite seriously.
What would be the workaround?
The following isn't a workaround, but what I consider to be the "proper" implementation.
I'd programatically implement the navigation for the wrapping element and use the <Link /> for the buttons. So something like:
navigate = () => {
//push "path1" to history
}
render() {
return(
<div onClick={this.navigate}>
<Link to="path2">Some button</Link>
<Link to="path3">Some button</Link>
</div>
);
}
For more info about programatically navigating in React Router see one of the links below:
For React Router v1-v3: Link
For React Router v4: Link
I think the best approach is something similar to what Chris said but without the need to make the navigation programmatically. Keep the default behaviour of the links and use CSS to make them appear nested. As far as the browser is concerned, the links will be siblings and not parent/child.
So basically:
set the surrounding div's position to relative.
no explicit position to the 'parent' link, and let it use the whole width/height
set the 'child' link's position to absolute, which will remove it from the normal flow and will allow you to put it anywhere inside the div. Events shouldn't conflict because the links are not nested.
EDIT: I know this answer comes years later, but I expect many people to come across this problem since it is a common UI pattern.
Related
In my react app with react-router v6 I am retrieving links dynamically that can be either a regular external url (like https://stackoverflow.com/) or an internal path (like "/my-page") that corresponds to a Route with react-router.
So far, when using react-router-dom's <Link> component, I could only get it to work with an internal path, but NOT with an external url. As far as I understood the docs I couldn't find a way to achieve this, at least not with v6.
So the best approach I've come up with so far is to write an own MyLink component that either render an <a> or a <Link> depending on whether the href is external or not:
function MyLink(props) {
const isHrefExternal = props.href.match(/^http|^https|^www/);
if (isHrefExternal) {
return <a href={props.href}>{props.children}</a>;
}
return <Link to={props.href}>{props.children}</Link>;
}
This of course doesn't look like a very good solution:
The check for isHrefExternal is very naive
It's unclear what props MyLink should accept and how they should be managed since <a> and <Link> have different props.
For a full example see this codesandbox
Can you tell me how to do it better? Ideally there would be an option to pass an external url to react-router-dom's <Link> component but I couldn't find one.
Thanks a lot!
The Link component handles only internal links within your app, and likely won't ever handle external links natively.
I don't see any overt issues with your code, and I don't think it's a bad solution. The "isExternal" check may be naive, but it only needs to cover your specific use cases. As new use cases are required you can tweak the logic for this check. In my projects I've worked with we've just typically included an isExternal type property with fetched data so the app doesn't even need to think about what is or isn't an external link, it just checks the flag when rendering.
I've been reading the nextjs docs and it says in here that:
If the child of Link is a function component, in addition to using
passHref, you must wrap the component in React.forwardRef:
I'm new to using Semantic UI React so I'm not really sure if I need to do this and more importantly how to do this. I'm concerned about this because just before the quoted lines above, the docs says here that:
Without this (the passHref), the tag will not have the href attribute, which
might hurt your site’s SEO.
I can pass the passHref like this:
<Link href='/posts' passHref>
<Button>See Posts</Button>
</Link>
But the problem is that I don't think that we can wrap a component from Semantic UI with the React.forwardRef since it's just imported. So my questions are:
Is it just fine to let this be?
Or is there a workaround for this to "not hurt" my site's SEO?
Yes . The nextJs Documentation states if your child is not just an tag but a functional or class component then the passHref is needed. Make sure to pass it whenever you use a react component as a child
You need passHref only with some tags, here is an example. is not about SEO, and your quote is about "If your does not have passHref attribute and has child, your SEO might be broken".
Just create 2 pages with passHref and without, and check the pages, for example, with LIghthouse of chrome or any other tool. Also, check the DOM, you will see, no changes in SEO directly. (ofc if child components are html or even react components with no "required" href property.)
yes, in some cases you need to do the following according to the Nextjs documentation:
function NavLink({ href, name }) {
// Must add passHref to Link
return (
<Link href={href} passHref>
<RedLink>{name}</RedLink>
</Link>
)
}
I have a question about React, here's a simplified version of a React app.
In the app I want to render a fixed primary menu and a secondary menu that is optional and its content is controlled by inner components rendered in routing.
Also secondary menu is rendered somewhere else in mobile version of the app.
function App() {
return <Router>
<PrimaryMenu/>
<SecondaryMenu/>
<LayoutContent/>
{/* This block is rendered only on mobile devices */}
<Responsive {...Responsive.onlyMobile}>
<SecondaryMenu/>
</Responsive>
</Router>;
}
LayoutContent will render actual page content (using a Page component) according to routing rules and every page component may render its own secondary menu like this (e.g. page1 has its own submenu, page2 has another one, page3 has not.)
<Page title='Page 1 - With secondary menu'>
<SecondaryMenuItems>
{/* I want this content as children of secondary menu in both mobile and desktop menubars */}
<li>Page 1 item 1</li>
<li>Page 1 item 2</li>
</SecondaryMenuItems>
</Page>
I tried to implement it by using React Contexts but if I store children nodes in context an infinite render is triggered. I changed it to use a id property in <SecondaryMenuItems/> component but the approach is very fragile and also has some drawbacks.
Here's my working example it's working but as I said is pretty fragile:
What if I use a duplicate id for secondary menus?
What if I forget a secondary menu key?
Also if you switch to a page with a menu and then go to page3 (that has no menu) previous page menu remain on screeen.
How to accomplish this with react? Is there a suggested way to do that?
A simpler way to express my question is "how to pass a set of react nodes between unrelated components (e.g. siblings components)"
Update
I've completed my working example with received hints, now by combining useRef with ReactDOM.createPortal I achieved final result which is now in the example.
This is a use case for React Portals. Portal will let you render secondary menu items from a page into secondary menu container that exists somewhere else
All you need to do is to call React.createPortal in render of thepage, pass rendered element and target node to render into, regardless of position in DOM tree
I've edited your example using portals here https://codesandbox.io/s/secondary-menu-example-vbm3x. This of course is a basic example, you might want to abstract portals logic in a separate component for convenience, and/or pass dom reference from parent, instead of calling getElementById on mount
Rendering same children in multiple sibling nodes
The question asks "how to pass a set of react nodes". Ideally, don't. If you are rendering nodes somewhere in your hierarchy with the intention of using them elsewhere, you may be using the wrong strategy.
If you need to render the same components in different places, make a function that renders the components, and call it from both places. In other words, always pass the information, not the rendered elements.
Render inside the router
In a typical Single Page Application, the router will render all of the (non-static) components. This is how the example should have done it. The routing component (LayoutContent) should have been responsible for rendering the "passed nodes" (SecondaryMenu) directly.
<Route path="/page1">
<Page title="Page 1 - With secondary menu">
<SecondaryMenu id="menu1"> {/* <- use SecondaryMenu instead of SecondaryMenuItems */}
<li>Page 1 item 1</li>
<li>Page 1 item 2</li>
</SecondaryMenu> {/* <- use SecondaryMenu instead of SecondaryMenuItems */}
</Page>
</Route>
When rendering inside the router is impossible
If for some reason the routing component cannot render the content directly, then a Single Page Application (or routing) solution is probably the wrong solution here. The question doesn't include any information as to why the components can't be rendered inside the router, feel free to edit the question and comment with more info.
Another way of achieving the example would be for there to be no routing component (i.e. no LayoutContent) and for SecondaryMenu to check the path of the page and conditionally render the appropriate content based on that.
It may seem silly to manually render conditionally based on a path when there is a router component which does this for you, and I would agree. The solution is then to not use a router at all. Trying to render children in the router and passing them has a strong code smell.
In the React hierarchical layout, if the same information is needed make decisions about rendering in multiple places (the path in this case), move that information up to the nearest parent of all components and pass it down as props or as context.
Avoiding ID clashes
"What if I use a duplicate id for secondary menus?"
If you call a function to render the secondary menu instead of rendering it and passing it, then you can pass a menu prefix in the props, and use this menu prefix in the function.
function SecondaryMenuItems({ children, idPrefix, path }) {
if (path == '/path1') {
return (
<ul id={`${idPrefix}-newlist`}>
On keys
"What if I forget a secondary menu key?"
React keys need only be unique within a rendered list. In fact, keys are simply an optimisation to prevent React having to re-render a generated list on every pass. If you forget to include a key (or make a bad choice of key), React has to re-render the list every time, but it's not more serious than that. In simple cases, you won't notice the drop in performance.
I have read many articles to find out the real time use case of this.props.children but i didn't find the answer that i am looking for.I know that this.props.children is used to access the data b/w the opening and closing tag of a component. But my question is why can't we add a prop to the component instead of writing data b/w opening and closing tag.
for Ex:
<Example>This is data<Example> //can be accessed as this.props.children
can be written as
<Example data="This is data"/> //can be accessed as this.props.data
Can somebody please explain me with a real-time example of where we can achieve a certain task by using only this.props.children?
For example if you have complicated children of a component:
<Card>
<div class='title'>Title</div>
<div class='content'>Content</div>
</Card>
It would be easier than if you write like:
<Card content={[<div class='title'>Title</div>, <....>]} />
Samething you can find here, for example in Drawer component of Material-UI here. Drawer is a component that slides from the left, it can contain anything, so using props.childrens.
While making an app, you want a parent component which will render anything in your component. The use cases which I can think of are:
When you want to open a different component depending upon the route change.
const App = ({ children }) => (
<div className="full-height">
{children}
</div>
);
When you want to have same styles throughout your app for generic elements such as body, head etc. You'll just have to apply on this component, e.g., in above example, the full-height will get applied everywhere in the app on top component. (Obviously there are other work arounds but this is always more clear)
For use cases where you want to expose your component (when component doesn't know children ahead of time) as libraries and props can vary a lot and complicates the rendering. Read this
Obviously you don't have to use it but it makes code more elegant and understandable.
I am using contentful to get markdown to a react component that uses react-markdown to parse the markdown
import ReactMarkdown from 'react-markdown';
<Markdown source={text} />
Would I like to do is to override the Renderer so instead of it rendering ## as an h2 render i can pass a custom component to override the default h2 type to my own h2 component. How can i do that and is there and examples?
One of the options to <ReactMarkdown> is renderers.
One of the common renderers handles headings. If you look at the default rendering you'll see this:
heading: function Heading(props) {
return createElement('h' + props.level, getCoreProps(props), props.children);
},
So pass in your own heading handler. Check the level inside, roughly:
function CustomHeading(props) {
if (props.level !== 2) {
return createElement(`h${props.level}`, getCoreProps(props), props.children);
}
return <MyCustomElement {...props} />
}
If you don't have access to the code that commonmark-react-renderer gives you in the context of your function (which you probably won't) then you'd also need to duplicate what createElement gives you (but it's simple).
Unrelated: I've never used <ReactMarkdown> (but will), but this took me about five minutes of research. I'm including my path to encourage others to dig into their own questions and hopefully give some insight into how such things can be researched.
The react-markdown home page
Scanned through the "Options" section to see if custom rendering was trivially supported
Found the renderers option, which sounded promising
Clicked the link provided in that option's docs
Saw that heading was one of those (which made sense; I'd expect a renderer for every major formatting that Markdown supports)
Opened up the src directory to see if the implementation was easy to find
There was only one file, so I opened it
Searched the page for "heading" and found it
Cut and pasted that code here
The ability to read docs and follow trails is really important.