How to use React Router with Preact
Asked Answered
J

4

15

I'm using Preact as my View framework (can NOT use React due to the Facebook IP problems). I needed to use React Router for the location routing because it has more flexibility than the Preact Router that the same team built.

I managed to get React Router to accept Preact in place of React, however, I can't get it to match locations. I'm not sure if it's a compatibility problem, or a configuration problem. I've tried using just one pair of routes ( App and Account ) and it still doesn't work with that simplified setup.

Q: Does anyone see if I'm doing something wrong here?

The error I get is: Location "/account/12345/" did not match any routes

main.js

import { h, render } from 'preact';
import { Router, browserHistory } from 'react-router';
import { Provider } from 'react-redux';

import createStore from './createStore';
import createRoutes from './createRoutes';

process.env.DEBUG && console.log('Hello, developer!');

const history = browserHistory;
const store = createStore( history );
const routes = createRoutes( store );

render((
    <Provider store={ store } key="redux-provider">
        <Router history={ history } createElement={ h } routes={ routes } />
    </Provider>
), document.body );

createRoutes.js

import { h } from 'preact';
import { IndexRoute, Route } from 'react-router';

// App Component
import App from './components/app';

// Sub Components
import Account from './components/account';
import Conversation from './components/conversation';
import Dashboard from './components/dashboard';

// Error Components
import BadAccount from './components/bad-account';
import NotFound from './components/not-found';

// Routes
export default ()=> (
    <Route path="/" component={App}>
        {/* Get URL parameter */}
        <Route path="account/:accountID" component={Account}>
            {/* Index Route */}
            <IndexRoute component={Dashboard} />
            {/* Sub Routes ( Alphabetical Please ) */}
            <Route path="chat" component={Conversation} />
            {/* Catch-All Route */}
            <Route path="*" component={NotFound} />
        </Route>
        {/* Handle Invalid URIs */}
        <Route path="*" component={BadAccount} />
    </Route>
);

createStore.js

import { applyMiddleware, combineReducers, compose, createStore } from 'redux';
import thunk from 'redux-thunk';
import { routerMiddleware } from 'react-router-redux';

import messages from './resources/messages/reducer';
import conversation from './resources/conversation/reducer';
import layout from './resources/layout/reducer';
import profile from './resources/profile/reducer';
import contract from './resources/contract/reducer';

/*const { devToolsExtension } = window;*/

export default history => {

    // Sync dispatched route actions to the history
    const reduxRouterMiddleware = routerMiddleware( history );

    // Create single reducer from all modules
    const rootReducer = combineReducers({
        messages,
        conversation,
        layout,
        profile,
        contract
    });

    // List redux middleware to inject
    const middleware = [
        thunk,
        reduxRouterMiddleware
    ];

    // Compose the createStore function
    const createComposedStore = compose(
        applyMiddleware( ...middleware )/*, // Figure this out...
        ( process.env.DEBUG && devToolsExtension ) ? devToolsExtension() : f => f*/
    )( createStore );

    // Create the store
    const store = createComposedStore( rootReducer );

    // Hook up Redux Routing middleware
    // reduxRouterMiddleware.listenForReplays(store);

    // Return store
    return store;
};
Jampan answered 18/5, 2016 at 17:14 Comment(1)
Note that as of react-router v4 Preact is supported out of the box. If you are having issues related to using react-router v4 with Preact the solution is probably not hereThanatopsis
A
17

(OP already solved his issue, but this ranks high in Google and is not very helpful for newcomers, so I thought I'd provide some background info)

Preact and preact-compat

preact is a minimal version of React that weighs just 3Kb. It implements a subset of React's API, with some small differences here and there. It also comes with a helper library, preact-compat, which provides compatibility with React by filling in missing parts and patching up API differences.

React-Router

react-router is a router library designed to work with React. But you can make it work with Preact as well, using preact-compat.

Setting up preact-compat

npm i --save preact-compat

Make sure you set up aliases for react and react-dom in your webpack / browserify configuration, or write some code to set up these aliases manually.

example webpack config

{
    // ...
    resolve: {
        alias: {
            'react': 'preact-compat',
            'react-dom': 'preact-compat'
        }
    }
    // ...
}

Then you can use React Components as-is. They won't know they are being rendered by Preact i.s.o. React. Have a look at this preact-compat-example.

Issues with compatibility

Keep in mind that when you are using Preact Compat, you are taking a risk. Jason is a very smart guy, but his library is only a fraction of the size of the one provided by Facebook so there's bound to be some differences. Components that use lesser known features of React might not work correctly. If you encounter such issues, report them to the preact-compat issue tracker (with a minimal reproduction in the form of a GitHub repo) to help him improve it.

There have been a few of such issues in the past that prevented React-Router from working correctly with Preact, but they have been fixed since and you should now be able to use the two together nicely.

Fiddle of Preact + React-Router

Have a look at this JS Fiddle for a working example.

Absolve answered 12/9, 2016 at 8:43 Comment(0)
T
5

Updated answer is there is a preact-router package now: https://www.npmjs.com/package/preact-router

import Router from 'preact-router';
import { h } from 'preact';

const Main = () => (
  <Router>
    <Home path="/" />
    <About path="/about" />
    <Search path="/search/:query" />
  </Router>
);

render(<Main />, document.body);
Too answered 30/8, 2016 at 5:46 Comment(2)
You can actually use the original React Router with Preact if you need it. Note that OP mentions he 'needed to use React Router for the location routing because it has more flexibility than the Preact Router [..]'Absolve
hey how can i use this to get multiple optional params from say the url--------------------------- : /about/user?id=5&name=abcBrahmana
J
-1

Found the issue, was a pair of problems with Preact's compatibility with React:

Contexts not handled correctly:

https://github.com/developit/preact/issues/156

props.children not handled correctly:

https://github.com/developit/preact-compat/issues/47#issuecomment-220128365

Jampan answered 19/5, 2016 at 15:22 Comment(0)
I
-1

Here is extend solution for preact-router with hash support. Works with reload and direct access.

https://www.webpackbin.com/bins/-KvgdLnM5ZoFXJ7d3hWi

import {Router, Link} from 'preact-router';
import {h, render} from 'preact';
import {createHashHistory} from 'history';

[cut]...[/cut]

const App = () => (
    <div>   
      <Header />
      <Router history={createHashHistory()}>            
        <Page1 default path="/" />
        <Page2 path="/page2" />
      </Router>
    </div>
);    
render(<App />, document.body);
Induna answered 5/10, 2017 at 13:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.