Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Allow injecting a custom component for NavLink to render to enhance support for HOCs #5496

Closed
Nimelrian opened this issue Sep 5, 2017 · 6 comments

Comments

@Nimelrian
Copy link

Background: I'm using Radium to style my React components.
Radium works by wrapping the target component's render function, searching for style props on DOM-node children and replacing them according to the current UI state (hovering/focusing/...). However, Radium does not do this for non-DOM-node children, since it does not know what the component is actually doing with the style prop. This is the case for React Router's Link and NavLink components

Usually, this is solved by wrapping the React component using Radium's HOC, as you can do with Link like in this snippet, which renders a Link with a red background, switching to green once you hover over it:

import React from "react";
import { render } from "react-dom";
import { BrowserRouter, Link} from "react-router-dom";
import Radium from "radium";

const RadiatingLink = Radium(Link);

const App = () =>
  <BrowserRouter>
    <RadiatingLink
      to="/test3"
      style={{ background: "red", ":hover": { background: "green" } }}
    >
      LinkTest
    </RadiatingLink>
  </BrowserRouter>

render(<App />, document.getElementById("root"));

But, if we were to replace Link with NavLink in this example it wouldn't work, because NavLink internally renders a Link not wrapped by the Radium HOC.

To solve this problem, I propose to add a new "renderComponent" prop to NavLink, which defaults to Link and gets rendered instead of a hardcoded Link component. This allows users to wrap the Link component rendered by NavLink (Or even render something completely different).

If this were implemented, it would allow injecting a custom render component like this:

import React from "react";
import { render } from "react-dom";
import { BrowserRouter, Link, NavLink} from "react-router-dom";
import Radium from "radium";

const RadiatingLink = Radium(Link);

const App = () =>
  <BrowserRouter>
    <NavLink
      to="/test3"
      renderComponent={RadiatingLink}
      style={{ background: "red", ":hover": { background: "green" } }}
    >
      LinkTest
    </NavLink>
  </BrowserRouter>

render(<App />, document.getElementById("root"));

This would also fix FormidableLabs/radium#907

@Nimelrian Nimelrian changed the title Feature Request: Allow injecting a custom component for NavLink to render to enhance support HOCs Feature Request: Allow injecting a custom component for NavLink to render to enhance support for HOCs Sep 5, 2017
@VdeVentura
Copy link

Hey @Nimelrian ! how did u work around this as they didn't merge the PR?

@timdorr
Copy link
Member

timdorr commented Oct 23, 2017

Closing per #5497

@timdorr timdorr closed this as completed Oct 23, 2017
@Nimelrian
Copy link
Author

@irvingv8 I basically rewrote NavLink to use a wrapped Link (If I had more than one use case, I would have kept the renderComponent prop, but there is no need in my project as of now), simple copy, paste, replace,

@VdeVentura
Copy link

@Nimelrian Ok, TY! It'd be nice if they consider an alternative where instead of passing "what to render" into NavLink they allow you to pass some HOC to enhance Link. This way the initial purpose of NavLink remains the same but allows us to enhance the Link when needed.

@jakubrpawlowski
Copy link

I also don't like that NavLink can't be wrapped in HOC.

For temporary solution I nested all NavLinks inside divs and styled background of those divs on hover with Radium. Maybe it helps somebody.

import React from 'react'
import Radium from 'radium'
import {NavLink} from 'react-router-dom'

const navigation = () => {
    const navStyle = {
        display: "grid",
        gridTemplateColumns: "repeat(4, 1fr)",
        lineHeight: "64px",
        textAlign: "center",
        borderBottom: "1px solid #ddd"
    }
    const makeHoverable = {
        display: "grid",
        ":hover": {
            backgroundColor: "#f7f7f7"
        }
    }
    const aStyle = {
        textDecoration: "none",
        fontWeight: "bold",
        color: "#666"
    }
    const aActiveStyle = {
        color: "#444",
        borderBottom: "3px solid orange"
    }
    return (
        <nav style={navStyle}>
            <div style={makeHoverable} key="homeHoverable"><NavLink style={aStyle} exact activeStyle={aActiveStyle} to="/">Home</NavLink></div>
            <div style={makeHoverable} key="clientHoverable"><NavLink style={aStyle} exact activeStyle={aActiveStyle} to="/client">Client</NavLink></div>
            <div style={makeHoverable} key="serverHoverable"><NavLink style={aStyle} exact activeStyle={aActiveStyle} to="/server">Server</NavLink></div>
            <div style={makeHoverable} key="contactHoverable"><NavLink style={aStyle} exact activeStyle={aActiveStyle} to="/contact">Contact</NavLink></div>
        </nav>
    )
}

export default Radium(navigation)

@ackvf
Copy link

ackvf commented Mar 27, 2018

You can walk around this with a Route. It's even documented in the React Router Training.

You can pass what to render as a render prop (below) or you can write it directly within the BetterNavLink as in the linked example. (above)

const BetterNavLink = ({ children, ...linkProps }) => (
  <Route
    path={linkProps.to}
    exact={linkProps.exact}
    children={ ({ match }) => children({active: !!match, linkProps}) } 
  />
)

Then in the render of your NavBar

<BetterNavLink exact to="/pdp">
  {
    ({active, linkProps}) => (
      <div className={`list-group-item list-group-item-dark ${active ? 'active' : ''}`}>
        <Link {...linkProps}>
          <span className="label">PDP</span>
        </Link>
      </div>
    )
  }
</BetterNavLink>

result is

<div class="list-group-item list-group-item-dark active">
  <a href="/pdp">
    <span class="label">PDP</span>
  </a>
</div>

@lock lock bot locked as resolved and limited conversation to collaborators Jun 4, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants