Coder Social home page Coder Social logo

react-ionize's Introduction

react-ionize

react-ionize is a library which lets you build the "non-browser" parts of an Electron app using React components to manage your application's state.

Electron applications consist of two types of process: a main process which manages the lifecycle of the application, and several renderer processes, which display webpages which comprise the application's GUI. While it's fairly common to use React and ReactDOM to build an HTML/CSS/JS interface in the renderer process, react-ionize runs in the main process, managing things like window size/position, menu contents, and application-wide events.

react-ionize demo

Caveat Developer

react-ionize is still an EXPERIMENTAL, PRE-ALPHA library, and is not yet suitable for for use in a production app! It's a custom renderer built on top of the React Fiber reconciliation API, which itself is still under active development. (Not to mention, I've got a whole crop of Electron features yet to add.)

Getting Started

* npm install --save electron
* npm install --save [email protected]
* npm install --save react-ionize

Take a look at Ionize Example App to get started.

Hello, world!

const React = require('react');
const Ionize = require('react-ionize');
const path = require('path');
const fs = require('fs');

const INDEX_HTML_PATH = path.resolve(__dirname, 'index.html');
const INDEX_HTML_SOURCE = `
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello, Electron!</title>
  </head>
  <body>
    <h1>Hello, Electron!</h1>
  </body>
</html>
`;

fs.writeFileSync(INDEX_HTML_PATH, INDEX_HTML_SOURCE);

Ionize.start(
  <app>
    <window show file={INDEX_HTML_PATH} />
  </app>
);

(Normally, you'd build and distribute an index.html along with your JS during your build process, but I wanted this example to be as independent of build processes as possible.)

API

Ionize.start(element, [callback])

Starts up an Electron application under Ionize. (Note: this will wait on the 'ready' Electron event before starting to render any elements.)

Elements

Generally speaking, the presence of an Ionize element in your component tree indicates that you want it to be there, and that Ionize should ensure its presence when rendering. This can lead to slightly surprising behavior if you're unfamiliar with React- for instance, if you want a window to actually go away when you close it, you need to make sure that the corresponding <window/> element actually gets unmounted!

<app>

Attachment point for event handlers related to the global app. Not strictly necessary if you don't need to register any of these (since React Fiber now supports multiple children without a parent element).

Generally speaking, children of <app> are things related to the entire application: browser windows, dialogs, tray elements, and so forth. (Or at least, they will be once I get a chance to implement them.)

  • Event Handlers
    • onReady- Fired immediately when the component is mounted. Under the hood, Ionize actually waits for 'ready' before even trying to start mounting everything, but this is here for the sake of API compatibility.

<window>

Represents an Electron BrowserWindow object.

  • file

    • The HTML file you want to render in the Chrome browser instance. (Note that Ionize looks up this file relative to the runtime location of your project!)
  • show

    • When this prop transitions from false -> true, Ionize displays the browser window. When it transitions from true -> false, Ionize hides (but does not close) the browser window. If this is true when the element is mounted, Ionize will immediately show the window.
  • onReadyToShow

    • Called when the window's ready-to-show event is triggered. You can use this to keep the window hidden until it's ready.
  • showDevTools

    • If this is true when the element is mounted, Ionize will open the Chrome Developer Tools when opening the browser window.
  • size

    • This is a controlled prop, and behaves much like an <input> element in traditional React apps. That is to say, if you provide size by itself, the user will not be able to resize the window- it will simply be that size, unless you provide an onResize handler to update the value provided.
    • See also: https://facebook.github.io/react/docs/forms.html#controlled-components
  • onResize

    • This event handler is called with the new window size when the window size changes. If provided in conjunction with the size prop, it will allow the window to be resized (although you will need to update the value of the size prop accordingly).
  • defaultSize

    • If you don't care about tracking the window's size manually, you may provide a defaultSize prop to set the window to an initial size when it's mounted.
  • position

  • onMove

  • onMoved

  • defaultPosition

    • These props work pretty much the same as size, onResize, and defaultSize, except they represent the position on the screen.
    • Keep in mind that these values might be funky if you're dealing with multiple monitors.

<menu>

The <menu> element defines an Electron application menu. By nesting <submenu>s inside it, you can construct your menu.

TODO: When nested inside a <window> element, this should attach the menu to that window, specifically- and I'd like to have something like a <contextmenu> element for right-clicks.

<submenu>

  • label
    • The label of the submenu. (Keep in mind that on OSX, the first submenu in the list will take the application's name, no matter what label you might give it.)
  • children
    • <submenu> and <item> elements only!

<item> and related

Pretty much equivalent to new MenuItem({ type: 'normal' }) in vanilla Electron.

  • label

    • The text shown for this menu item
  • onClick

    • An onclick handler for this menu item.

There are a couple special-case elements related to <item>.

<sep>

A separator menu item. Equivalent to new MenuItem({ type: 'separator' }) in vanilla Electron.

Role-based elements

In the Electron API, you can create MenuItems with a "role", which assigns them some OS-native functionality (think copy, paste, select all, and so on.) As a shorthand, you can use these directly as an element name within a <submenu>. For instance,

<pasteandmatchstyle />

is equivalent to

new MenuItem({ role: 'pasteandmatchstyle' }) in vanilla Electron.

Feedback and Contributions

Feedback is welcome! Add an issue or hit me up on Twitter. I'm still working out how all this should work, but I would love to hear your suggestions.

If you'd like to contribute, right now we could really use some more feature implementations. Basically, what I've been doing is picking Electron APIs and figuring out how to turn them into either elements or props on existing elements. For instance, the app.setBadge(text) API call would make for a pretty good prop on <app />.

To assist the would-be contributor, I've included a bunch of documentation and notes about React Fiber in the comments of IonizeHostConfig.js. For further reading, I'd basically recommend cloning down the React repo and digging through its /src/renderers directory, starting with /src/renderers/shared/fiber/ReactFiberReconciler.js and going from there.

react-ionize's People

Contributors

hajjitarik avatar marshallofsound avatar mhink avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-ionize's Issues

Add handler to update menu

I'm wondering how you'd do something like have a menu item with a different label depending on the state. For example, "Enter Fullscreen" and "Exit Fullscreen".
I haven't seen a straightforward way to re-render or update the menu on state changes like this.

Testing of elements

Thought I'd get a topic started on the tests.

I ran into some issues when trying to write a test for acceptFirstMouse, which seem to be related to the fact that the electron mock creates instances of _BrowserWindow on its own (like here). This works as long as you don't really care about how the constructor was called, but when checking for options that are passed to the constructor things get a bit tricky.

An idea I had was that instead allowing the electron mock to create instances whenever it wants, it could allow tests to register a callback that is called whenever the BrowserWindow constructor mock is called. This way, tests could register a callback to stub whatever they need, and all instances are created 'on demand', instead of whenever the electron mock is loaded or getWindow is called.

Let me know what you think. If you want, I could get something going and submit a PR.

Fiber renderer notes

Nice reverse engineering work. (Sorry nothing is documented yet.)

A few notes on your custom renderer implementation:

// These functions have something to do with how updates are prioritized and
// scheduled. I have NO idea how the 'timeRemaining' piece works, but I've
// pretty much lifted it wholesale from ReactTestRendererFiber and it seems to
// work OK.

Take a look at requestIdleCallback doc which scheduleDeferredCallback() was modeled after. The idea is that timeRemaining() is provided by the browser (or other host environment). We do a little bit of work and then check timeRemaining(). If it gets below zero then it’s time for us to stop doing work. @linclark’s talk on Fiber goes into more details, but the idea is that we let the host environment notify us about when idle periods start and end, and we squeeze some work in that period. We only do this for low priority updates (which can “wait” and don’t happen synchronously). Which brings me to...

// For these last two, I got nothin'. That's why they're at the bottom.
export const useSyncScheduling = false;

This actually opts you into using low priority updates for setState by default. Which currently has bugs and starvation issues (some updates may never flush if they arrive too fast, and we haven’t solved this yet). So you might want to use true for now if you care about correctness. But false is more fun for experimenting with how Fiber schedules work. ReactDOM 16 has this set to true for backwards compatibility, and we’ll look into making it false by default in 17 when we figure out all the edge cases and bugs.

export function shouldDeprioritizeSubtree(

This is an interesting one. If you return true from it for some props, it will treat this tree as unimportant (but still existing)—unimportant enough to not show it. Think something like display:none in ReactDOM. You know this thing isn’t displayed, and this means React can skip this work on initial render. However if it’s idle, it might as well start rendering it piece by piece. This is what happens for deprioritized subtrees: if you return true, you’re saying it’s safe to completely skip this subtree, but it would be nice to start rendering it in background.

Of course, this relies on a high fidelity scheduleDeferredCallback() implementation. If it’s just a setTimeout() then it’s going to be a really bad one because instead of being called in idle time, it will clobber up your CPU. So if you don’t have a good scheduleDeferredCallback() implementation, it’s better not to enable shouldDeprioritizeSubtree(), and it’s best to stick with useSyncScheduling.

Let me know if you have more questions!

Better path handling

Dealing with Electron runtime paths can be a pain, and the current resolution strategy for e.g. <window file="something.html"> is unsatisfactory to me.

It looks like Electron has a method called app.getPath(name) which we could use to manage things a bit better. I bet this would be a good use for getRootHostContext.

Add <ipc> element

This is something I've been wanting to do since I started. This element will be where you attach IPC handlers for messages coming to/from browser windows. I imagine the syntax should look something like this:

<ipc channel="my-channel" onMessage={/* handler function */} />

It might be a good idea to set things up so that nesting an <ipc> inside a <window> would only handle messages from THAT window. Or not, it's just an idea.

Another idea: explicitly create a "sync" prop which indicates that the return value of the onMessage function will be synchronously returned to the client.

TouchBar support

Hey, just found this project and I must say I love it!

From the first day the TouchBar was announced, I wanted to use it in electron and after the initial support landed, I started to playing around with the idea of how awesome it would be if we could use React to define its layout. Well, now it is possible thanks to this project and thanks to Fiber!

I’d like to ask if you’re interested in TouchBar support or plan it at all. I was so excited I went ahead and tried to hack in a very experimental support for buttons just to see if it’s doable and it’s looking good! Keep in mind I have never even tried anything with Fiber, so it might very well have some flaws despite being based on <menu /> and <item />.

Supporting TouchBar would mean using electron 1.6.3+, which is currently in beta, but I don’t see it as a problem granted this project itself is highly experimental.

TouchBar Docs

// edit: added first batch of TouchBar components
// edit: added second batch of TouchBar components

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.