Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/hardchor/electron-redux

Use redux in the main and browser processes in electron
https://github.com/hardchor/electron-redux

electron klarna-featured redux

Last synced: about 2 months ago
JSON representation

Use redux in the main and browser processes in electron

Awesome Lists containing this project

README

        

# electron-redux

[![CircleCI](https://circleci.com/gh/klarna/electron-redux/tree/master.svg?style=svg)](https://circleci.com/gh/klarna/electron-redux/tree/master)

- [electron-redux](#electron-redux)
- [Motivation](#motivation)
- [The solution](#the-solution)
- [Install](#install)
- [Actions](#actions)
- [Local actions (renderer process)](#local-actions-renderer-process)
- [Aliased actions (main process)](#aliased-actions-main-process)
- [Blacklisted actions](#blacklisted-actions)
- [F.A.Q.](#faq)
- [Contributions](#contributions)
- [Contributors](#contributors)

## Motivation

Using redux with electron poses a couple of problems. Processes ([main](https://github.com/electron/electron/blob/master/docs/tutorial/quick-start.md#main-process) and [renderer](https://github.com/electron/electron/blob/master/docs/tutorial/quick-start.md#renderer-process)) are completely isolated, and the only mode of communication is [IPC](https://github.com/electron/electron/blob/master/docs/api/ipc-main.md).

- Where do you keep the state?
- How do you keep the state in sync across processes?

### The solution

`electron-redux` offers an easy to use solution. The redux store on the main process becomes the single source of truth, and stores in the renderer processes become mere proxies. See [under the hood](#under-the-hood).

![electron-redux basic](https://cloud.githubusercontent.com/assets/307162/20675737/385ce59e-b585-11e6-947e-3867e77c783d.png)

## Install

```
npm install --save electron-redux
```

`electron-redux` comes as redux middleware that is really easy to apply:

```javascript
// in the main store
import { forwardToRenderer, triggerAlias, replayActionMain } from 'electron-redux';

const todoApp = combineReducers(reducers);

const store = createStore(
todoApp,
initialState, // optional
applyMiddleware(
triggerAlias, // optional, see below
...otherMiddleware,
forwardToRenderer, // IMPORTANT! This goes last
),
);

replayActionMain(store);
```

```javascript
// in the renderer store
import { forwardToMain, replayActionRenderer, getInitialStateRenderer } from 'electron-redux';

const todoApp = combineReducers(reducers);
const initialState = getInitialStateRenderer();

const store = createStore(
todoApp,
initialState,
applyMiddleware(
forwardToMain, // IMPORTANT! This goes first
...otherMiddleware,
),
);

replayActionRenderer(store);
```

Check out [timesheets](https://github.com/hardchor/timesheets/blob/4991fd472dbb12b0c6e6806c6a01ea3385ab5979/app/shared/store/configureStore.js) for a more advanced example.

And that's it! You are now ready to fire actions without having to worry about synchronising your state between processes.

## Actions

Actions fired **MUST** be [FSA](https://github.com/acdlite/flux-standard-action#example)-compliant, i.e. have a `type` and `payload` property. Any actions not passing this test will be ignored and simply passed through to the next middleware.

> NB: `redux-thunk` is not FSA-compliant out of the box, but can still produce compatible actions once the async action fires.

Furthermore, actions (and that includes `payload`s) **MUST** be (de-)serialisable, i.e. either POJOs (simple `object`s - that excludes native JavaScript or DOM objects like `FileList`, `Map`, etc.), `array`s, or primitives. For workarounds, check out [aliased actions](#aliased-actions-main-process)

### Local actions (renderer process)

By default, all actions are being broadcast from the main store to the renderer processes. However, some state should only live in the renderer (e.g. `isPanelOpen`). `electron-redux` introduces the concept of action scopes.

To stop an action from propagating from renderer to main store, simply set the scope to `local`:

```javascript
function myLocalActionCreator() {
return {
type: 'MY_ACTION',
payload: 123,
meta: {
scope: 'local',
},
};
}
```

### Aliased actions (main process)

Most actions will originate from the renderer side, but not all should be executed there as well. A great example is fetching of data from an external source, e.g. using [promise middleware](https://github.com/acdlite/redux-promise), which should only ever be executed once (i.e. in the main process). This can be achieved using the `triggerAlias` middleware mentioned [above](#install).

Using the `createAliasedAction` helper, you can quite easily create actions that are are only being executed in the main process, and the result of which is being broadcast to the renderer processes.

```javascript
import { createAliasedAction } from 'electron-redux';

export const importGithubProjects = createAliasedAction(
'IMPORT_GITHUB_PROJECTS', // unique identifier
(accessToken, repoFullName) => ({
type: 'IMPORT_GITHUB_PROJECTS',
payload: importProjects(accessToken, repoFullName),
}),
);
```

Check out [timesheets](https://github.com/hardchor/timesheets/blob/4ccaf08dee4e1a02850b5bf36e37c537fef7d710/app/shared/actions/github.js) for more examples.

### Blacklisted actions

By default actions of certain type (e.g. starting with '@@') are not propagated to the main thread. You can change this behaviour by using `forwardToMainWithParams` function.

```javascript
// in the renderer store
import {
forwardToMainWithParams,
replayActionRenderer,
getInitialStateRenderer,
} from 'electron-redux';

const todoApp = combineReducers(reducers);
const initialState = getInitialStateRenderer();

const store = createStore(
todoApp,
initialState,
applyMiddleware(
forwardToMainWithParams(), // IMPORTANT! This goes first
...otherMiddleware,
),
);

replayActionRenderer(store);
```

You can specify patterns for actions that should not be propagated to the main thread.

```javascript
forwardToMainWithParams({
blacklist: [/^@@/, /^redux-form/],
});
```

## F.A.Q

### `electron-redux` crashes with electron 10.x

As of Electron 10, the `remote` module is removed by default.

We can get it back by adding `enableRemoteModule=true` to the `webPreferences`:

```js
const w = new BrowserWindow({
webPreferences: {
enableRemoteModule: true,
},
});
```

## Contributions

Contributions via [issues](https://github.com/klarna/electron-redux/issues/new) or [pull requests](https://github.com/klarna/electron-redux/compare) are hugely welcome!

Feel free to let me know whether you're successfully using `electron-redux` in your project and I'm happy to add them here as well!

## Contributors

Special thanks go out to:

- [Charlie Hess](https://github.com/CharlieHess)
- [Roman Paradeev](https://github.com/sameoldmadness)
- [Pelle Jacobs](https://github.com/pellejacobs)
- [Victor Quiroz Castro](https://github.com/victorhqc)
- [musou1500](https://github.com/musou1500)
- [Andreas Dolk](https://github.com/Treverix)
- Everyone who has contributed by [asking questions & raising issues](https://github.com/klarna/electron-redux/issues?q=is%3Aissue+is%3Aclosed)