Basic question, but we all start somewhere.
I have a parent Nav.js
import React, { Component } from 'react'
import navLink from './navLink'
class Nav extends Component {
render () {
return (
<div>
<nav className="bb bt b--black-20 tc center bg-white">
<navLink />
</nav>
</div>
)
}
}
Nav.displayName = 'Nav'
export default Nav
Here's the file that won't get displayed:
navLink.js:
import React from 'react'
import Link from 'next/link'
const navigationLinks = [
{ name: '1', link: '/', router: true },
{ name: '2', link: '#' }
]
const renderLink = (link, i) => {
if (link.router === true) {
return (
<Link href={link.link}>
<a className="yellow" key={i}>{link.name}</a>
</Link>
)
} else {
return (
<a className="red" key={i} target="_blank" rel="noopener noreferrer" href={link.link}>{link.name}</a>
)
}
}
const navLink = () => (
<nav className="container">
{navigationLinks.map(link => renderLink(link))}
</nav>
)
navLink.displayName = 'navLink'
export default navLink
I'm my problem is super basic, but I have no idea how to resolve this. Thanks for your time!
The main problem is navLink with a small letter, it should be NavLink as RenderLink if you want to use it.
In JSX lower case are considered to be a HTML tags, HTML is not have no a navLink tag so it's just didn't displayed.
User-Defined Components Must Be Capitalized
The first part of a JSX tag determines the type of the React element.
Capitalized types indicate that the JSX tag is referring to a React component. These tags get compiled into a direct reference to the named variable, so if you use the JSX expression, Foo must be in scope.
see: docs
Render links
const SimpleLink = ({link}) => (
<Link href={link.link}>
<a className="yellow">{link.name}</a>
</Link>
);
const RenderRouter = ({link}) => (
<a className="red" target="_blank" href={link.link}>{link.name}</a>
);
const renderLink = link => link.router ?
( <SimpleLink key={i} link={link}/> ) :
( <RenderRouter key={i} link={link}/> );
...
const NavLink = () => (
<div className="container">
{ navigationLinks.map((link, i) => renderLink(link, i))}
</div>
);
...
export default NavLink
Import
import NavLink from './navLink.js'
Nav Component
class Nav extends Component {
render () {
return (
<nav className="bb bt b--black-20 tc center bg-white">
<NavLink />
</nav>
)
}
}
See: working example
Fix proptypes
react-router may provide propType definitions for it already, but whether they do or not, you'd still need to explicitly connect them.
Rend.propTypes = {
link: React.PropTypes.shape({
link: React.PropTypes.string.isRequired
}),
};
See: eslint-plugin-react/issues/1109
Related
App.jsx
import React from "react";
import Navbar from "./Navbar";
function App() {
return (
<div>
<Navbar
navLink1="about-us"
navLink2="projects"
navLink3="services"
navLink4="blog"
/>
</div>
);
}
export default App;
Navbar.jsx
import React from "react";
export default function Navbar(props) {
const navHeader = "Logo";
const navItems = ["About Us", "Projects", "Services", "Blog"];
const navLinks = navItems.map(addNavLink);
function addNavLink(value, index) {
return "props.navLink" + String(index + 1);
}
return (
<nav>
<h3 className="nav-header">{navHeader}</h3>
{navItems.map((itemValue, itemIndex) =>
navLinks.map(
(linkValue, linkIndex) =>
itemIndex === linkIndex && (
<a
href={linkValue.replaceAll('"', '')}
className="nav-links"
key={itemIndex}
>
<div className="nav-items">{itemValue}</div>
</a>
)
)
)}
</nav>
);
}
I want to use props along with loops so that its name can be changed slightly to match each custom attributes of Navbar component. For instance:
props.navLink1
props.navLink2
props.navLink3
props.navLink4
I tried to use props by first storing them as a string and then removed the double inverted commas in order to use the functionality of props. However, this didn't work. I'm relatively new to React and I'm not sure if it's even possible but if you have any idea to accomplish this then help me out.
This is the output I'm looking for by using loops and props.
In my App.js (or main component) I am rendering my Navbar component
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import Navbar from './components/layout/navbar/Navbar';
import './App.css';
const App = () => {
return (
<Router>
<Navbar />
</Router>
);
};
export default App;
In my Navbar I am rendering my NavLinks component and passing in as props the menu
import React from 'react';
import NavLinks from './NavLinks';
const menu = [
{ id: 1, label: 'Home', url: 'https://www.google.com/?client=safari' },
{ id: 2, label: 'Contact us', url: 'https://stackoverflow.com' },
];
const Navbar = () => {
return (
<nav>
<NavLinks items={menu} />
</nav>
);
};
export default Navbar;
In my NavLinks I bring in as props the items which is the menu we saw before and map through it and pass in as props url and label.
import React from 'react';
import NavLink from './NavLink';
const NavLinks = ({ items }) => {
const links = items.map((item) => (
<NavLink key={item.id} url={item.url} label={item.label} />
));
return <ul>{links}</ul>;
};
export default NavLinks;
In my NavLink component I am creating a Link to the url
import React from 'react';
import { Link } from 'react-router-dom';
const NavLink = ({ url, label }) => {
return (
<li className='nav-item'>
<Link to={url}>{label}</Link>
</li>
);
};
export default NavLink;
For some reason my Link has a path of multiple google urls. The url to the google homepage is duplicated many times. I do not know why this is happening.
Link component is to Provides declarative, accessible navigation around your application
If you use the Link component for the external URL, this will keep appending your URL to the previous ones.
For navigating to an external URL, I would suggest you to use native HTML tag instead:
const NavLink = ({ url, label }) => {
return (
<li className="nav-item">
<a href={url}>{label}</a>
</li>
);
};
Working example:
with RTL how do i check the to='/login' in Link to be true since RTL library mainly grabs by testid or text.
current page testing
import React from 'react';
import Proptypes from 'prop-types';
import { Link } from 'react-router-dom';
function Navigation(props) {
return (
<nav className="header__nav">
<Link to="/login">
<button type="button" className="header__login">LOGIN</button>
</Link>
</nav>
);
}
test
describe('Navigation Content', () => {
test('clicking login button', () => {
const props = jest.fn();
const { getByTestId, getByText } = render (
<Navigation popUpHandler={props}/>, { wrapper: MemoryRouter }
);
expect((<Link>).toHaveAttribute('to', '/login')
})
})
page i am trying to render to when clicked
function LoginForm() {
return (
<div className="login">
<h1 className="entryheader__header">Login</h1>
</div>
)
export default LoginForm;
Could you try rendering your component like this:
import { MemoryRouter } from 'react-router-dom'
...
...
const { getByTestId, getByText } = render(<YourComponent {...props}/>, { wrapper: MemoryRouter });
fireEvent.click(getByText('LOGIN'));
expect(getByText('some-text-on-login-page')).toBeInTheDocument();
This will wrap your component with the memory router rather than you doing it manually.
You can also try using BrowserRouter instead of MemoryRouter in your test file..
I wish I had an answer to this question but I do feel like I can say the following:
I wouldn't do
<Link to="/login">
<button type="button" className="header__login">LOGIN</button>
</Link>
because this will render non-valid HTML markup. It breaks accessibility rules to start
I'm trying to figure out the best ways of creating functional components that generate simple html - take this nav-links.js file for example:
export const navLinks = [
{
name: "home",
href: "/"
},
{
name: "subs",
href: "/subs"
}
];
The html my component attempts to generate just loops through each link to generate an unordered-list with list tags and a tags inside.
The problem occurs in this nav-menu.js file, where the output is just an two <ul></ul> tags with nothing inside:
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
import {navLinks} from "../util/nav-links";
export const NavMenu = () => {
const lis = () => (
{navLinks}.map(link => {
return (
<li>
<Link to={link.href} key={link.href}>{link.name}</Link>
</li>
)
})
)
return (
<div id="navbar">
<ul>
{lis}
</ul>
</div>
)
}
Why is nothing rendering in the above component? I wrote some more code below that solves it, although I wonder whether it could be refactored better:
export const NavMenu = () => {
return (
<div id="navbar">
<ul>
{navLinks.map(link => {
return (
<li>
<Link to={link.href} key={link.href}>{link.name}</Link>
</li>
)
})}
</ul>
</div>
)
}
Why does the first attempt not work, and how could it be better refactored? Thanks for any help here
In your the first example didn't work because you passed the const list = () => {..} as a anonymous function without calling it, inside the curly braces in the JSX portion of you code.
If you have called it like {list()}:
return (
<div id="navbar">
<ul>
{lis()}
</ul>
</div>
)
This way it would have probably worked.
Or you could have stored the list inside a variable like this:
const lis = navLinks.map(link => {
return (
<li>
<Link to={link.href} key={link.href}>{link.name}</Link>
</li>
)
}
)
or also like this:
const lis = navLinks.map(link => (
<li>
<Link to={link.href} key={link.href}>{link.name}</Link>
</li>
)
)
)
I'm trying to use react-bootstrap breadcrumb as below.
<Breadcrumb>
<Breadcrumb.Item href="#">Home</Breadcrumb.Item>
<Breadcrumb.Item><Link to={"/products"}>Products</Link></Breadcrumb.Item>
<Breadcrumb.Item active>{productName}</Breadcrumb.Item>
</Breadcrumb>
As you can expect, products Link will render anchor tag inside another anchor tag, which is invalid markup. But Home creates a simple anchor tag instead of react's Link making the page to reload, making it unusable.
What's the solution for this? Unfortunately, there's no mention of this in react-bootstrap doc. (link)
I would probably use react-router-bootstrap, but if you don't want to include it as a dependency, you can apply the link by hand using the now available linkAs and linkProps Breadcrumb params. For instance:
<Breadcrumb.Item linkAs={Link} linkProps={{ to: "/path" }}>
My item
</Breadcrumb.Item>
This approach is interesting especially if you are using just the "as" attribute with other components like Button or NavLink.
This nicely works and it does not refresh the whole page, only what's needed to change
import { Breadcrumb } from "react-bootstrap";
import { Link } from "react-router-dom";
export const SiteMap = ({ hrefIn }) => {
const items = [
{ href: "/dictionaries", name: "Dictionaries" },
{ href: "/antonyms", name: "Antonyms" },
];
return (
<Breadcrumb>
{items.map((item) =>
item.href === hrefIn ? (
<Breadcrumb.Item active>{item.name}</Breadcrumb.Item>
) : (
<Breadcrumb.Item linkProps={{ to: item.href }} linkAs={Link}>
{item.name}
</Breadcrumb.Item>
)
)}
</Breadcrumb>
);
};
I ended up dropping react-boostrap and doing it 'by hand':
const Breadcrumbs = ({ breadcrumbs }) => (
<ol className="breadcrumb">
{breadcrumbs.map((breadcrumb, index) => (
<li key={breadcrumb.key}>
<NavLink to={breadcrumb.props.match.url}>
{breadcrumb}
</NavLink>
</li>
))}
</ol>
);
It works for me if I wrap <Breadcrumb.Item> into the <LinkContainer>.
Now outer Breadcrumb "My applications" which points to /applications URL redirects my app with react-router to the applications page.
I tested this with react-bootstrap v0.32.4 https://5c507d49471426000887a6a7--react-bootstrap.netlify.com/components/breadcrumb/
I got <LinkContainer> from react-router-bootstrap package: https://github.com/react-bootstrap/react-router-bootstrap
I saw "the wrapping" here before, though I don't generate breadcrumb in a loop: https://github.com/react-bootstrap/react-router-bootstrap/issues/141#issue-122688732
import { LinkContainer } from 'react-router-bootstrap';
// and then in the render function
<Breadcrumb>
<LinkContainer to="/applications" exact>
<Breadcrumb.Item>My applications</Breadcrumb.Item>
</LinkContainer>
<Breadcrumb.Item active>My First Applicaton</Breadcrumb.Item>
</Breadcrumb>
Here I have passed onClick to Breadcrumb.Item to handle navigation with the help of
import { withRouter } from "react-router-dom";
Here is the sample:
const RenderBreadcrumb = ({ breadcrumbInfo, history }) => {
const handleRedirect = (url) => {
history.push(url);
}
return (
<Breadcrumb>
{ map(breadcrumbInfo, (item) => {
if(item.isActive) {
return (<Breadcrumb.Item active>{item.text}</Breadcrumb.Item>);
}
return (<Breadcrumb.Item onClick={() => { handleRedirect(item.link); }}>{item.text}</Breadcrumb.Item>);
})}
</Breadcrumb>
)
}
export default withRouter(RenderBreadcrumb);
Bootstrap.Item internally uses SafeAnchor which allows you to not use a link if you don't want to.
Using the as prop you can modify what tag is used (a by default). For example you can pass:
<Bootstrap.Item as="div" />
And it will use a div tag for presenting the item.