12

I'm rendering components from my external (node_modules) pattern library. In my main App, I'm passing my Link instance from react-router-dom into my external libraries' component like so:

import { Link } from 'react-router-dom';
import { Heading } from 'my-external-library';

const articleWithLinkProps = {
    url: `/article/${article.slug}`,
    routerLink: Link,
  };

<Heading withLinkProps={articleWithLinkProps} />

In my library, it's rendering the Link as so:

const RouterLink = withLinkProps.routerLink;

<RouterLink
  to={withLinkProps.url}
>
  {props.children}
</RouterLink>

The RouterLink seems to render correctly, and even navigates to the URL when clicked.


My issue is that the RouterLink seems to have detached from my App's react-router-dom instance. When I click Heading, it "hard" navigates, posting-back the page rather than routing there seamlessly as Link normally would.

I'm not sure what to try at this point to allow it to navigate seamlessly. Any help or advice would be appreciated, thank you in advance.

Edit: Showing how my Router is set up.

import React from 'react';
import { hydrate, unmountComponentAtNode } from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import { Provider } from 'react-redux';
import { createBrowserHistory } from 'history';
import { ConnectedRouter } from 'react-router-redux';
import RedBox from 'redbox-react';
import { Route } from 'react-router-dom';
import { Frontload } from 'react-frontload';
import App from './containers/App';

import configureStore from './redux/store';
import withTracker from './withTracker';

// Get initial state from server-side rendering
const initialState = window.__INITIAL_STATE__;
const history = createBrowserHistory();
const store = configureStore(history, initialState);
const mountNode = document.getElementById('react-view');
const noServerRender = window.__noServerRender__;

if (process.env.NODE_ENV !== 'production') {
  console.log(`[react-frontload] server rendering configured ${noServerRender ? 'off' : 'on'}`);
}

const renderApp = () =>
  hydrate(
    <AppContainer errorReporter={({ error }) => <RedBox error={error} />}>
      <Provider store={store}>
        <Frontload noServerRender={window.__noServerRender__}>
          <ConnectedRouter onUpdate={() => window.scrollTo(0, 0)} history={history}>
            <Route
              component={withTracker(() => (
                <App noServerRender={noServerRender} />
              ))}
            />
          </ConnectedRouter>
        </Frontload>
      </Provider>
    </AppContainer>,
    mountNode,
  );

// Enable hot reload by react-hot-loader
if (module.hot) {
  const reRenderApp = () => {
    try {
      renderApp();
    } catch (error) {
      hydrate(<RedBox error={error} />, mountNode);
    }
  };

  module.hot.accept('./containers/App', () => {
    setImmediate(() => {
      // Preventing the hot reloading error from react-router
      unmountComponentAtNode(mountNode);
      reRenderApp();
    });
  });
}

renderApp();
djmcr
  • 1,490
  • 3
  • 24
  • 54
  • 1
    Why not having your `` component passed on to your external lib as an argument or props? that would allow more flexibility and its clean way of handling navigation. – ROOT Jan 10 '20 at 15:02
  • Can you show how you set up your router? Is it a Browser, Memory, or Static Router? – zero298 Jan 10 '20 at 16:25
  • @mamounothman - That's how it's currently being done, but seems to affect the way it navigates to a post-back. – djmcr Jan 14 '20 at 10:37
  • You are using the deprecated library `react-router-redux` (https://github.com/reactjs/react-router-redux), unless you have an special constraint to enforce using outdated versions of the `react-redux` and `react-router` libraries, you should consider moving to newer versions, as it may fix your problem). Either you do the upgrade, or you should provide the exact versions of `react`, `react-router-redux`, `react-redux` and `react-router-dom` your project is currently using so we have a chance to find a patching solution. – GonArrivi Jan 16 '20 at 09:40
  • Can you confirm that everything works if Link is used directly with that same URL (not by proxying it to library first)? – zhuber Jan 17 '20 at 09:28
  • @zhuber - This seems to work fine when passing to a component within my site, only once it's gone through node_modules does this unwanted behaviour happen. – djmcr Jan 17 '20 at 10:12

1 Answers1

4

I've reconstructed your use case in codesandbox.io and the "transition" works fine. So maybe checking out my implementation might help you. However, I replaced the library import by a file import, so I don't know if that's the decisive factor of why it doesn't work without a whole page reload.

By the way, what do you mean exactly by "seamlessly"? Are there elements that stay on every page and should not be reloaded again when clicking on the link? This is like I implemented it in the sandbox where a static picture stays at the top on every page.


Check out the sandbox.

This is the example.js file

// This sandbox is realted to this post https://stackoverflow.com/q/59630138/965548

import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import { Heading } from "./my-external-library.js";

export default function App() {
  return (
    <div>
      <img
        alt="flower from shutterstock"
        src="https://image.shutterstock.com/image-photo/pink-flowers-blossom-on-blue-600w-1439541782.jpg"
      />
      <Router>
        <Route exact={true} path="/" render={Welcome} />
        <Route path="/article/coolArticle" component={CoolArticleComponent} />
      </Router>
    </div>
  );
}

const Welcome = () => {
  const articleWithLinkProps = {
    url: `/article/coolArticle`,
    routerLink: Link
  };

  return (
    <div>
      <h1>This is a super fancy homepage ;)</h1>
      <Heading withLinkProps={articleWithLinkProps} />
    </div>
  );
};

const CoolArticleComponent = () => (
  <div>
    <p>This is a handcrafted article component.</p>
    <Link to="/">Back</Link>
  </div>
);

And this is the my-external-library.js file:

import React from "react";

export const Heading = ({ withLinkProps }) => {
  const RouterLink = withLinkProps.routerLink;
  return <RouterLink to={withLinkProps.url}>Superlink</RouterLink>;
};
New3D
  • 193
  • 1
  • 8
  • Hi there, many thanks for the answer! It seems as though only when passing via node_modules, does the issue occur. – djmcr Jan 17 '20 at 10:13