React, Electron, And Router - javascript

I am currently to adapt a simple web app i made (using React and Redux) into a native desktop app.
I am using GitHub's Electron and Webpack to do this. Everything is fine if I use hashHistory from React-Router.. but I want to use browserHistory so my app will still look nice (URL-wise) when running as a webapp. If i do this though, I get the following error:
No route matches path ".../index.html"
Which makes sense to me. I am loading index.html as the main file for Electron:
mainWindow.loadURL('file://' + __dirname + '/index.html');
I'm just wondering if it is at all possible to use browserHistory with React-Router and Electron. If anyone knows it would be greatly appreciated!

Not exactly. But there is even better solution .
You should Separate from your react app the bootstrap file.
Bootstrap file which load your app and pass to it some additional params from outside.
In your situation you will create two bootstrap files, one for electron - with memoryHistory (I think it is better for electron) and second one for browsers with browser history.
Example of bootstrap file for electron index-electron.jsx:
import React from "react";
import ReactDOM from "react-dom";
import { createMemoryHistory } from "react-router";
import App from "./App.jsx";
const initialState = window.__INITIAL_STATE__;
const config = window.__CONFIG__;
const history = createMemoryHistory("begin-path");
ReactDOM.render(
<App
config={config}
history={history}
initialState={initialState}
/>, document.getElementById("root"));
Example of bootstrap file for browsers index-browser.jsx:
import React from "react";
import ReactDOM from "react-dom";
import { browserHistory } from 'react-router';
import App from "./App.jsx";
const initialState = window.__INITIAL_STATE__;
const config = window.__CONFIG__;
ReactDOM.render(
<App
config={config}
history={history}
initialState={initialState}
/>, document.getElementById("root"));
In my examples difference is small (only history) but you can make more changes. As you see I also provide additional begin params from outside (initialState, config);
And how your App should :
import React from 'react';
import { Router, Route } from 'react-router';
class App extends React.Component {
static propsTypes = {
history: React.PropTypes.object,
config: React.PropTypes.object,
initialState: React.PropTypes.object
};
render() {
return (
<Router history={this.props.history}>
<Route ...>
...
</Route>
</Router>
);
}
}
export default App;
Above code is only conception. It is from my project, where I removed obsolete things. Therefore without some modification it may not work
Now for electron you use index-electron.jsx, and for browsers index-browser.jsx. Most of your code is reusable cross both envs. And it is very flexible.

Related

Creating browser site history in React

I'm fairly new to React and I'd like to build a panel to show Recently Viewed URLs within my web site.
I'm trying to use the createBrowserHistory library to do that from a navigation page. Here is the layout:
└── navigation
├── component1
└── component2
└── component3
I'd like to implement something where if I click on component3, then component1, then component2, it would display something like this:
Recently Visited:
component2
component1
component3
I have a History.js file that consists of only this:
import { createBrowserHistory} from 'history';
export default createBrowserHistory({
})
and from the Navigation.js page, I have something like this:
import history from './path/to/History';
import { useLocation } from 'react-router-dom';
const location = useLocation();
history.push(location)
Now if I do a console.log(history), I just see the current path that I'm on, but not the previous URLs. I was under the impression that history.push() pushes the URL to a stack, but how do I extract the last several URLs that were visited?
You need to add the history as a property into the Router(router provider)
import React from "react";
import ReactDOM from "react-dom";
import { Router } from "react-router";
import { createBrowserHistory } from "history";
const history = createBrowserHistory();
ReactDOM.render(
<Router history={history}>
<App />
</Router>,
node
);
And then your hooks would be work well as your expected

CSS Path in Reactjs is wrong

I have observed that suddenly CSS path in my project is changed, I am not sure about the reason whether this is a configuration issue or any other, but because of the path CSS is taking too much time to load.
I have already tried to check imports and everything in the code but it seems proper
Please check the below image for path comparison between the new and the backup folder.
New Path:
Old Path
Note: I need the path to be like old where react convert the css to inline, this runs faster.
Below is how I have imported my custom CSS in index.js:
import * as serviceWorker from './serviceWorker';
import React, { Component } from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import { render } from 'react-dom';
import App from './App';
import './index.css';
import './utilities/animate.css';
//react and redux related library
import { Provider } from 'react-redux';
import store from './redux/store/index';
class Main extends Component {
render() {
return (
<Route path='/' component={App} />
);
}
}
let rootElement = document.getElementById('app');
render(<Provider store={store}>
<BrowserRouter >
<Main />
</BrowserRouter>
</Provider>, rootElement);
serviceWorker.unregister();
I have run npm update and updated few packages, may be some of the packages were corrupted or outdated. This fixed my issue of performance and CSS loading slow.

How to test React router without errors?

I've implemented an App component which contains a Route using React and React router:
import {
BrowserRouter as Router,
Route
} from 'react-router-dom';
import createHeader from './components/header/header';
import createLandingPage from './components/landing-page/landing-page';
import createFooter from './components/footer/footer';
export default React => () => {
const Header = createHeader(React);
const LandingPage = createLandingPage(React);
const Footer = createFooter(React);
return (
<section id="sectionId">
<Header />
<Router>
<Route exact path="/" component={LandingPage} />
</Router>
<Footer />
</section>
);
};
The app itself renders and works properly, as I can go to my browser and see everything as expected.
The problem arises when I try to implement proper testing, which runs in Node.js without a browser:
import test from 'tape';
import dom from 'cheerio';
import React from 'react';
import reactDom from 'react-dom/server';
import { MemoryRouter } from 'react-router';
import createApp from './app';
const render = reactDom.renderToStaticMarkup;
test('App init & render', assert => {
const message = 'It sohuld initialize and render the app.';
const App = createApp(React);
const $ = dom.load(render(
<MemoryRouter>
<App />
</MemoryRouter>
));
const output = $('#jptv').length;
const actual = output > 0;
const expected = true;
assert.equal(actual, expected, message);
assert.end();
});
I used MemoryRouter to wrap my app as indicated in React docs to avoid errors, but with or without it, I have the same error when running the test in the console:
Invariant Violation: Browser history needs a DOM
My guess is that I cannot run a component using BrowserRouter out of the browser, yet I don't understand what would be the proper way to test it, as I don't want to force myself to manipulate the code as to fit the test environment shortcomings.
I've searched the web and look into similar issues, but I cannot find an answer, and the docs offer little if no useful information to deal with this.
Any help will be most appreciated.
Thanks!

How to reduce build time of 'browserify-rails' gem?

I am working on project that will use Reactjs on top off rails application.
So i set up my project following this link
Set up Rails with React and Es6
Everything is working fine but i change my bundle.js to this
require('./main');
Then inside main.js i have this
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route } from 'react-router';
import { browserHistory } from 'react-router';
/*Import Component*/
import NotFound from './components/NotFound';
/*
*
* Routes
*
* */
var routes = (
<Router history={browserHistory}>
<Route path="/" component={NotFound}/>
</Router>
);
ReactDOM.render(routes , document.querySelector('#main'));
So inside my component file (/components/NotFound.js)
I have this
import React from 'react';
var NotFound = React.createClass({
render : function () {
return (
<div>
<h1>Not found</h1>
</div>
)
}
});
export default NotFound;
It can be displayed smoothly but the problem is
when i change some code inside my component file (in this case /components/NotFound.js) then refresh the page.
It took around 3 Seconds for just 1 line of code that i added to my component file.
So this is so painful because it took a huge amount of time just for edit a few line of code.
So anyone know how can i reduce the build time to be less than this?
Thanks a lot!

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;
};
(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.
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);
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
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);

Categories

Resources