Search code examples
javascriptreactjsreact-routerpreact

How to use React Router with Preact


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;
};

Solution

  • (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.