Giter VIP home page Giter VIP logo

inferno's Introduction

Inferno

Build Status Coverage Status MIT NPM npm downloads Discord gzip size Backers on Open Collective Sponsors on Open Collective

Inferno is an insanely fast, React-like library for building high-performance user interfaces on both the client and server.

Description

The main objective of the InfernoJS project is to provide the fastest possible runtime performance for web applications. Inferno excels at rendering real time data views or large DOM trees.

The performance is achieved through multiple optimizations, for example:

  • Inferno's own JSX compilers creates monomorphic createVNode calls, instead of createElement calls. Optimizing runtime performance of the application.
  • Inferno's diff process uses bitwise flags to memoize the shape of objects
  • Child nodes are normalized only when needed
  • Special JSX flags can be used during compile time to optimize runtime performance at application level
  • Many micro optimizations

Features

  • Component driven + one-way data flow architecture
  • React-like API, concepts and component lifecycle events
  • Partial synthetic event system, normalizing events for better cross browser support
  • Inferno's linkEvent feature removes the need to use arrow functions or binding event callbacks
  • Isomorphic rendering on both client and server with inferno-server
  • Unlike React and Preact, Inferno has lifecycle events on functional components
  • Unlike Preact and other React-like libraries, Inferno has controlled components for input/select/textarea elements
  • Components can be rendered outside their current html hierarchy using createPortal - API
  • Support for older browsers without any polyfills
  • defaultHooks for Functional components, this way re-defining lifecycle events per usage can be avoided
  • Inferno supports setting styles using string <div style="background-color: red"></div> or using object literal syntax <div style={{"background-color": "red"}}></div>. For camelCase syntax support see inferno-compat.
  • Fragments (v6)
  • createRef and forwardRef APIs (v6)
  • componentDidAppear, componentWillDisappear and componentWillMove (v8) - class and function component callbacks to ease animation work, see inferno-animation package

Runtime requirements

Inferno v9 requires following features to be present in the executing runtime:

  • Promise
  • String.prototype.includes()
  • String.prototype.startsWith()
  • Array.prototype.includes()
  • Object.spread()
  • for ... of

Browser support

Since version 4 we have started running our test suite without any polyfills. Inferno is now part of Saucelabs open source program and we use their service for executing the tests.

InfernoJS is actively tested with browsers listed below, however it may run well on older browsers as well. This is due to limited support of browser versions in recent testing frameworks. https://github.com/jasmine/jasmine/blob/main/release_notes/5.0.0.md

Browser Test Status

Migration guides

Benchmarks

Live examples at https://infernojs.github.io/inferno

Code Example

Let's start with some code. As you can see, Inferno intentionally keeps the same design ideas as React regarding components: one-way data flow and separation of concerns.

In these examples, JSX is used via the Inferno JSX Babel Plugin to provide a simple way to express Inferno virtual DOM. You do not need to use JSX, it's completely optional, you can use hyperscript or createElement (like React does). Keep in mind that compile time optimizations are available only for JSX.

import { render } from 'inferno';

const message = "Hello world";

render(
  <MyComponent message={ message } />,
  document.getElementById("app")
);

Furthermore, Inferno also uses ES6 components like React:

import { render, Component } from 'inferno';

class MyComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      counter: 0
    };
  }
  render() {
    return (
      <div>
        <h1>Header!</h1>
        <span>Counter is at: { this.state.counter }</span>
      </div>
    );
  }
}

render(
  <MyComponent />,
  document.getElementById("app")
);

Because performance is an important aspect of this library, we want to show you how to optimize your application even further. In the example below we optimize diffing process by using JSX $HasVNodeChildren and $HasTextChildren to predefine children shape compile time. In the MyComponent render method there is a div that contains JSX expression node as its content. Due to dynamic nature of Javascript that variable node could be anything and Inferno needs to go through the normalization process to make sure there are no nested arrays or other invalid data. Inferno offers a feature called ChildFlags for application developers to pre-define the shape of vNode's child node. In this example case it is using $HasVNodeChildren to tell the JSX compiler, that this vNode contains only single element or component vNode. Now inferno will not go into the normalization process runtime, but trusts the developer decision about the shape of the object and correctness of data. If this contract is not kept and node variable contains invalid value for the pre-defined shape (fe. null), then application would crash runtime. There is also span-element in the same render method, which content is set dynamically through _getText() method. There $HasTextChildren child-flag fits nicely, because the content of that given "span" is never anything else than text. All the available child flags are documented here.

import { createTextVNode, render, Component } from 'inferno';

class MyComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      counter: 0
    };
  }

  _getText() {
     return 'Hello!';
  }
  
  render() {
    const node = this.state.counter > 0 ? <div>0</div> : <span $HasTextChildren>{this._getText()}</span>;
      
    return (
      <div>
        <h1>Header!</h1>
        <div $HasVNodeChildren>{node}</div>
      </div>
    );
  }
}

render(
  <MyComponent />,
  document.getElementById("app")
);

Tear down

To tear down inferno application you need to render null on root element. Rendering null will trigger unmount lifecycle hooks for whole vDOM tree and remove global event listeners. It is important to unmount unused vNode trees to free browser memory.

import { createTextVNode, render, Component } from 'inferno';

const rootElement = document.getElementById("app");

// Start the application
render(
  <ExampleComponent/>,
  rootElement
);

// Tear down
render(
  null,
  rootElement
);

More Examples

If you have built something using Inferno you can add them here:

Getting Started

The easiest way to get started with Inferno is by using Create Inferno App.

Alternatively, you can try any of the following:

Core package:

npm install --save inferno

Addons:

# server-side rendering
npm install --save inferno-server
# routing
npm install --save inferno-router

Pre-bundled files for browser consumption can be found on our cdnjs:

Or on jsDelivr:

https://cdn.jsdelivr.net/npm/inferno@latest/dist/inferno.min.js

Or on unpkg.com:

https://unpkg.com/inferno@latest/dist/inferno.min.js

Creating Virtual DOM

JSX:

npm install --save-dev babel-plugin-inferno

Hyperscript:

npm install --save inferno-hyperscript

createElement:

npm install --save inferno-create-element

Compatibility with existing React apps

npm install --save-dev inferno-compat

Note: Make sure you read more about inferno-compat before using it.

Third-party state libraries

Inferno now has bindings available for some of the major state management libraries out there:

JSX

Inferno has its own JSX Babel plugin.

Differences from React

  • Inferno doesn't have a fully synthetic event system like React does. Inferno has a partially synthetic event system, instead opting to only delegate certain events (such as onClick).
  • Inferno doesn't support React Native. Inferno was only designed for the browser/server with the DOM in mind.
  • Inferno doesn't support legacy string refs, use createRef or callback ref API
  • Inferno provides lifecycle events on functional components. This is a major win for people who prefer lightweight components rather than ES2015 classes.

Differences from Preact

  • Inferno has a partial synthetic event system, resulting in better performance via delegation of certain events.
  • Inferno is much faster than Preact in rendering, updating and removing elements from the DOM. Inferno diffs against virtual DOM, rather than the real DOM (except when loading from server-side rendered content), which means it can make drastic improvements. Unfortunately, diffing against the real DOM has a 30-40% overhead cost in operations.
  • Inferno fully supports controlled components for input/select/textarea elements. This prevents lots of edgecases where the virtual DOM is not the source of truth (it should always be). Preact pushes the source of truth to the DOM itself.
  • Inferno provides lifecycle events on functional components. This is a major win for people who prefer lightweight components rather than ES2015 classes.

Event System

Like React, Inferno also uses a light-weight synthetic event system in certain places (although both event systems differ massively). Inferno's event system provides highly efficient delegation and an event helper called linkEvent.

One major difference between Inferno and React is that Inferno does not rename events or change how they work by default. Inferno only specifies that events should be camel cased, rather than lower case. Lower case events will bypass Inferno's event system in favour of using the native event system supplied by the browser. For example, when detecting changes on an <input> element, in React you'd use onChange, with Inferno you'd use onInput instead (the native DOM event is oninput).

Available synthetic events are:

  • onClick
  • onDblClick
  • onFocusIn
  • onFocusOut
  • onKeyDown
  • onKeyPress
  • onKeyUp
  • onMouseDown
  • onMouseMove
  • onMouseUp
  • onTouchEnd
  • onTouchMove
  • onTouchStart

linkEvent (package: inferno)

linkEvent() is a helper function that allows attachment of props/state/context or other data to events without needing to bind() them or use arrow functions/closures. This is extremely useful when dealing with events in functional components. Below is an example:

import { linkEvent } from 'inferno';

function handleClick(props, event) {
  props.validateValue(event.target.value);
}

function MyComponent(props) {
  return <div><input type="text" onClick={ linkEvent(props, handleClick) } /><div>;
}

This is an example of using it with ES2015 classes:

import { linkEvent, Component } from 'inferno';

function handleClick(instance, event) {
  instance.setState({ data: event.target.value });
}

class MyComponent extends Component {
  render () {
    return <div><input type="text" onClick={ linkEvent(this, handleClick) } /><div>;
  }
}

linkEvent() offers better performance than binding an event in a class constructor and using arrow functions, so use it where possible.

Controlled Components

In HTML, form elements such as <input>, <textarea>, and <select> typically maintain their own state and update it based on user input. In Inferno, mutable state is typically kept in the state property of components, and only updated with setState().

We can combine the two by making the Inferno state be the "single source of truth". Then the Inferno component that renders a form also controls what happens in that form on subsequent user input. An input form element whose value is controlled by Inferno in this way is called a "controlled component".

Inferno Top-Level API

render (package: inferno)

import { render } from 'inferno';

render(<div />, document.getElementById("app"));

Render a virtual node into the DOM in the supplied container given the supplied virtual DOM. If the virtual node was previously rendered into the container, this will perform an update on it and only mutate the DOM as necessary, to reflect the latest Inferno virtual node.

Warning: If the container element is not empty before rendering, the content of the container will be overwritten on the initial render.

createRenderer (package: inferno)

createRenderer creates an alternative render function with a signature matching that of the first argument passed to a reduce/scan function. This allows for easier integration with reactive programming libraries, like RxJS and Most.

import { createRenderer } from 'inferno';
import { scan, map } from 'most';

const renderer = createRenderer();


// NOTE: vNodes$ represents a stream of virtual DOM node updates
scan(renderer, document.getElementById("app"), vNodes$);

See inferno-most-fp-demo for an example of how to build an app architecture around this.

createElement (package: inferno-create-element)

Creates an Inferno VNode using a similar API to that found with React's createElement()

import { Component, render } from 'inferno';
import { createElement } from 'inferno-create-element';

class BasicComponent extends Component {
  render() {
    return createElement('div', {
        className: 'basic'
      },
      createElement('span', {
        className: this.props.name
      }, 'The title is ', this.props.title)
    )
  }
}

render(
  createElement(BasicComponent, { title: 'abc' }),
  document.getElementById("app")
);

Component (package: inferno)

Class component:

import { Component } from 'inferno';

class MyComponent extends Component {
  render() {
      return <div>My Component</div>
  }
}

This is the base class for Inferno Components when they're defined using ES6 classes.

Functional component:

const MyComponent = ({ name, age }) => (
  <span>My name is: { name } and my age is: {age}</span>
);

Another way of using defaultHooks.

export function Static() {
    return <div>1</div>;
}

Static.defaultHooks = {
    onComponentShouldUpdate() {
        return false;
    }
};

Default props

export function MyFunctionalComponent({value}) {
    return <div>{value}</div>;
}

MyFunctionalComponent.defaultProps = {
    value: 10
};

Functional components are first-class functions where their first argument is the props passed through from their parent.

createVNode (package: inferno)

import { createVNode } from 'inferno';

createVNode(
  flags,
  type,
  [className],
  [...children],
  [childFlags],
  [props],
  [key],
  [ref]
)

createVNode is used to create html element's virtual node object. Typically createElement() (package: inferno-create-element), h() (package: inferno-hyperscript) or JSX are used to create VNodes for Inferno, but under the hood they all use createVNode(). Below is an example of createVNode usage:

import { VNodeFlags, ChildFlags } from 'inferno-vnode-flags';
import { createVNode, createTextVNode, render } from 'inferno';

const vNode = createVNode(VNodeFlags.HtmlElement, 'div', 'example', createTextVNode('Hello world!'), ChildFlags.HasVNodeChildren);

// <div class="example">Hello world!</div>

render(vNode, container);

createVNode arguments explained:

flags: (number) is a value from VNodeFlags, this is a numerical value that tells Inferno what the VNode describes on the page.

type: (string) is tagName for element for example 'div'

className: (string) is the class attribute ( it is separated from props because it is the most commonly used property )

children: (vNode[]|vNode) is one or array of vNodes to be added as children for this vNode

childFlags: (number) is a value from ChildFlags, this tells inferno shape of the children so normalization process can be skipped.

props: (Object) is object containing all other properties. fe: {onClick: method, 'data-attribute': 'Hello Community!}

key: (string|number) unique key within this vNodes siblings to identify it during keyed algorithm.

ref: (function) callback which is called when DOM node is added/removed from DOM.

createComponentVNode (package: 'inferno')

import { createComponentVNode } from 'inferno';

createComponentVNode(
  flags,
  type,
  [props],
  [key],
  [ref]
)

createComponentVNode is used for creating vNode for Class/Functional Component.

Example:

import { VNodeFlags, ChildFlags } from 'inferno-vnode-flags';
import { createVNode, createTextVNode, createComponentVNode, render } from 'inferno';

function MyComponent(props, context) {
  return createVNode(VNodeFlags.HtmlElement, 'div', 'example', createTextVNode(props.greeting), ChildFlags.HasVNodeChildren);
}

const vNode = createComponentVNode(VNodeFlags.ComponentFunction, MyComponent, {
  greeting: 'Hello Community!'
}, null, {
  onComponentDidMount() {
    console.log("example of did mount hook!")
  }
})

// <div class="example">Hello Community!</div>

render(vNode, container);

createComponentVNode arguments explained:

flags: (number) is a value from VNodeFlags, this is a numerical value that tells Inferno what the VNode describes on the page.

type: (Function/Class) is the class or function prototype for Component

props: (Object) properties passed to Component, can be anything

key: (string|number) unique key within this vNodes siblings to identify it during keyed algorithm.

ref: (Function|Object) this property is object for Functional Components defining all its lifecycle methods. For class Components this is function callback for ref.

createTextVNode (package: 'inferno')

createTextVNode is used for creating vNode for text nodes.

createTextVNode arguments explained: text: (string) is a value for text node to be created. key: (string|number) unique key within this vNodes siblings to identify it during keyed algorithm.

import { createTextVNode } from 'inferno';

createTextVNode(
  text,
  key
)

cloneVNode (package: inferno-clone-vnode)

This package has same API as React.cloneElement

import { cloneVNode } from 'inferno-clone-vnode';

cloneVNode(
  vNode,
  [props],
  [...children]
)

Clone and return a new Inferno VNode using a VNode as the starting point. The resulting VNode will have the original VNode's props with the new props merged in shallowly. New children will replace existing children. key and ref from the original VNode will be preserved.

cloneVNode() is almost equivalent to:

<VNode.type {...VNode.props} {...props}>{children}</VNode.type>

An example of using cloneVNode:

import { createVNode, render } from 'inferno';
import { cloneVNode } from 'inferno-clone-vnode';
import { VNodeFlags } from 'inferno-vnode-flags';

const vNode = createVNode(VNodeFlags.HtmlElement, 'div', 'example', 'Hello world!');
const newVNode = cloneVNode(vNode, { id: 'new' }); // we are adding an id prop to the VNode

render(newVNode, container);

If you're using JSX:

import { render } from 'inferno';
import { cloneVNode } from 'inferno-clone-vnode';

const vNode = <div className="example">Hello world</div>;
const newVNode = cloneVNode(vNode, { id: 'new' }); // we are adding an id prop to the VNode

render(newVNode, container);

createPortal (package: 'inferno')

HTML:

<div id="root"></div>
<div id="outside"></div>

Javascript:

const { render, Component, version, createPortal } from 'inferno';

function Outsider(props) {
	return <div>{`Hello ${props.name}!`}</div>;
}

const outsideDiv = document.getElementById('outside');
const rootDiv = document.getElementById('root');

function App() {
	return (
  	    <div>
    	    Main view
            ...
            {createPortal(<Outsider name="Inferno" />, outsideDiv)}
        </div>
    );
}


// render an instance of Clock into <body>:
render(<App />, rootDiv);

Results into:

<div id="root">
    <div>Main view ...</div>
</div>
<div id="outside">
    <div>Hello Inferno!</div>
</div>

Cool, huh? Updates (props/context) will flow into "Outsider" component from the App component the same way as any other Component. For inspiration on how to use it click here!

createRef (package: inferno)

createRef API provides shorter syntax than callback ref when timing of element is not needed.

import { Component, render, createRef } from 'inferno';

class Foobar extends Component {
  constructor(props) {
    super(props);

    // Store reference somewhere
    this.element = createRef(); // Returns object {current: null}
  }

  render() {
    return (
      <div>
        <span id="span" ref={this.element}>
          Ok
        </span>
      </div>
    );
  }
}

render(<Foobar />, container);

createFragment (package: inferno)

createFragment is the native way to createFragment vNode. createFragment(children: any, childFlags: ChildFlags, key?: string | number | null)

createFragment arguments explained:

children: (Array) Content of fragment vNode, typically array of VNodes

childFlags: (number) is a value from ChildFlags, this tells inferno shape of the children so normalization process can be skipped.

key: (string|number) unique key within this vNodes siblings to identify it during keyed algorithm.

Alternative ways to create fragment vNode are:

  • Using JSX <> ... </>, <Fragment> .... </Fragment> or <Inferno.Fragment> ... </Inferno.Fragment>
  • Using createElement API createElement(Inferno.Fragment, {key: 'test'}, ...children)
  • Using hyperscript API h(Inferno.Fragment, {key: 'test'}, children)

In the below example both fragments are identical except they have different key

import { Fragment, render, createFragment } from 'inferno';
import { ChildFlags } from 'inferno-vnode-flags';

function Foobar()ย {
    return (
      <div $HasKeyedChildren>
        {createFragment(
            [<div>Ok</div>, <span>1</span>],
            ChildFlags.HasNonKeyedChildren,
            'key1'
        )}
        <Fragment key="key2">
          <div>Ok</div>
          <span>1</span>
        </Fragment>
      </div>
    );
}

render(<Foobar />, container);

forwardRef (package: inferno)

forwardRef is a new mechanism to "forward" ref inside a functional Component. It can be useful if you have simple functional Components and you want to create reference to a specific element inside it.

import { forwardRef, Component, render } from 'inferno';

const FancyButton = forwardRef((props, ref) => (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
));

class Hello extends Component {
  render() {
    return (
      <FancyButton
        ref={btn => {
          if (btn) {
            // btn variable is the button rendered from FancyButton
          }
        }}
      >
        Click me!
      </FancyButton>
    );
  }
}

render(<Hello />, container);

hydrate (package: inferno-hydrate)

import { hydrate } from 'inferno-hydrate';

hydrate(<div />, document.getElementById("app"));

Same as render(), but is used to hydrate a container whose HTML contents were rendered by inferno-server. Inferno will attempt to attach event listeners to the existing markup.

findDOMNode (package: inferno-extras)

This feature has been moved from inferno to inferno-compat in v6. No options are needed anymore.

Note: we recommend using a ref callback on a component to find its instance, rather than using findDOMNode(). findDOMNode() cannot be used on functional components.

If a component has been mounted into the DOM, this returns the corresponding native browser DOM element. This method is useful for reading values out of the DOM, such as form field values and performing DOM measurements. In most cases, you can attach a ref to the DOM node and avoid using findDOMNode() at all. When render returns null or false, findDOMNode() returns null. If Component has rendered fragment it returns the first element.

Inferno Flags (package: inferno-vnode-flags)

VNodeFlags:

  • VNodeFlags.HtmlElement
  • VNodeFlags.ComponentUnknown
  • VNodeFlags.ComponentClass
  • VNodeFlags.ComponentFunction
  • VNodeFlags.Text
  • VNodeFlags.SvgElement
  • VNodeFlags.InputElement
  • VNodeFlags.TextareaElement
  • VNodeFlags.SelectElement
  • VNodeFlags.Portal
  • VNodeFlags.ReCreate (JSX $ReCreate) always re-creates the vNode
  • VNodeFlags.ContentEditable
  • VNodeFlags.Fragment
  • VNodeFlags.InUse
  • VnodeFlags.ForwardRef
  • VNodeFlags.Normalized

VNodeFlags Masks:

  • VNodeFlags.ForwardRefComponent Functional component wrapped in forward ref
  • VNodeFlags.FormElement - Is form element
  • VNodeFlags.Element - Is vNode element
  • VNodeFlags.Component - Is vNode Component
  • VNodeFlags.DOMRef - Bit set when vNode holds DOM reference
  • VNodeFlags.InUseOrNormalized - VNode is used somewhere else or came from normalization process
  • VNodeFlags.ClearInUseNormalized - Opposite mask of InUse or Normalized

ChildFlags

  • ChildFlags.UnknownChildren needs Normalization
  • ChildFlags.HasInvalidChildren is invalid (null, undefined, false, true)
  • ChildFlags.HasVNodeChildren (JSX $HasVNodeChildren) is single vNode (Element/Component)
  • ChildFlags.HasNonKeyedChildren (JSX $HasNonKeyedChildren) is Array of vNodes non keyed (no nesting, no holes)
  • ChildFlags.HasKeyedChildren (JSX $HasKeyedChildren) is Array of vNodes keyed (no nesting, no holes)
  • ChildFlags.HasTextChildren (JSX $HasTextChildren) vNode contains only text

ChildFlags Masks

  • ChildFlags.MultipleChildren Is Array

renderToString (package: inferno-server)

import { renderToString } from 'inferno-server';

const string = renderToString(<div />);

Render a virtual node into an HTML string, given the supplied virtual DOM.

Functional component lifecycle events

Name Triggered when Arguments to callback
onComponentWillMount a functional component is about to mount
onComponentDidMount a functional component has mounted successfully domNode
onComponentShouldUpdate a functional component has been triggered to update lastProps, nextProps
onComponentWillUpdate a functional component is about to perform an update lastProps, nextProps
onComponentDidUpdate a functional component has performed an update lastProps, nextProps
onComponentWillUnmount a functional component is about to be unmounted domNode
onComponentDidAppear a functional component has mounted and is ready for animations domNode, props
onComponentWillDisappear a functional component is unmounted before DOM node is removed domNode, props, callback

onComponentWillDisappear has special type of argument "callback" which needs to be called when component is ready to be removed from the DOM. fe. after animations are finished.

Class component lifecycle events

All these Component lifecycle methods ( including render and setState - callback) are called with Component instance context. You don't need to "bind" these methods.

Name Triggered when Arguments to callback
componentDidMount component has been mounted successfully
componentWillMount component is about to mount
componentWillReceiveProps before render when component updates nextProps, context
shouldComponentUpdate component has been triggered to update nextProps, nextState
componentWillUpdate component is about to perform an update nextProps, nextState, context
componentDidUpdate component has performed an update lastProps, lastState, snapshot
componentWillUnmount component is about to be unmounted
getChildContext before render method, return value object is combined to sub tree context
getSnapshotBeforeUpdate before component updates, return value is sent to componentDidUpdate as 3rd parameter lastProps, lastState
static getDerivedStateFromProps before render method nextProps, state
componentDidAppear component has mounted and is ready for animations domNode
componentWillDisappear component is unmounted before DOM node is removed domNode, callback

componentWillDisappear has special type of argument "callback" which needs to be called when component is ready to be removed from the DOM. fe. after animations are finished.

Using functional lifecycle events

Functional lifecycle events must be explicitly assigned via props onto a functional component like shown below:

import { render } from 'inferno';

function mounted(domNode) {
  // [domNode] will be available for DOM nodes and components (if the component has mounted to the DOM)
}

function FunctionalComponent({ props }) {
  return <div>Hello world</div>;
}

render(
  <FunctionalComponent onComponentDidMount={ mounted } />,
  document.getElementById("app")
);

Please note: class components (ES2015 classes) from inferno do not support the same lifecycle events (they have their own lifecycle events that work as methods on the class itself).

Development vs Production modes

By default, Inferno will run in development mode. Development mode provides extra checks and better error messages at the cost of slower performance and larger code to parse. When using Inferno in a production environment, it is highly recommended that you turn off development mode.

Running Inferno on Node JS

Ensure the environment variable process.env.NODE_ENV is set to production.

Application bundling

When building your application bundle, ensure process.env.NODE_ENV is replaced with string"development" or "production" based on the workflow. It is recommended to use ts-plugin-inferno for typescript TSX compilation and babel-plugin-infeno for javascript JSX compilation.

When building for development, you may want to use inferno.dev.mjs for v9 or newer and inferno.dev.esm.js for older than v9. That bundle file contains ES6 exports for better tree-shaking support, improved error messages and added validation to help fixing possible issues during development. The file is found from package.json - dev:module entry point and the files are physically located in node_modules/inferno/dist/ folder. Remember that it is not recommended to use that file in production due to slower performance. For production usage use node_modules/inferno/dist/inferno.mjs -file for v9 or newer and node_modules/inferno/dist/inferno.esm.js -file for older than v9.

Example of Webpack configuration:

const path = require('path');
const infernoTsx = require('ts-plugin-inferno').default;

... webpack config ...

    module: {
        rules: [
            {
                test: /\.js$/, // Add "jsx" if your application uses `jsx` file extensions
                exclude: /node_modules/,
                use: [{
                    loader: 'babel-loader',
                    options: {
                        plugins: [
                            // Compile javascript JSX syntax using inferno's own plugin
                            ['babel-plugin-inferno', {imports: true}]
                        ]
                    }
                }]
            },
            {
                test: /\.ts+(|x)$/, // Compile ts and tsx extensions
                exclude: /node_modules/,
                use: [{
                    loader: 'ts-loader',
                    options: {
                        getCustomTransformers: () => ({
                            // inferno custom TSX plugin
                            after: [infernoTsx()]
                        }),
                        compilerOptions: {
                            /* typescript compiler options */
                        }
                    }
                }]
            }
        ]
    },
    resolve: {
        extensions: ['.js', '.ts', '.tsx'],
        alias: {
            // This maps import "inferno" to es6 module entry based on workflow
            inferno: path.resolve(__dirname, 'node_modules/inferno/dist', isProduction ? 'index.dev.mjs' : 'index.mjs')
        }
    },
    plugins: [
        new webpack.DefinePlugin({
            'process.env': {
                'NODE_ENV':  JSON.stringify(isProduction ? 'production' : 'development')
            }
        })
    ]

Example of Rollup configuration:

const path = require('path');
const alias = require('@rollup/plugin-alias');
const {babel} = require('@rollup/plugin-babel');
const replace = require('@rollup/plugin-replace');
const typescript = require('rollup-plugin-typescript2');
const transformInferno = require('ts-plugin-inferno').default;

... Rollup config ...
{
    input: /* entry file */,
    plugins: [
            alias({
                resolve: ['.js'],
                entries: [
                    // This maps import "inferno" to es6 module entry based on workflow
                    {find: 'inferno', replacement: path.resolve(__dirname, 'node_modules/inferno/dist', isProduction ? 'index.dev.mjs' : 'index.mjs')}
                ]
            }),
            typescript({
                include: ['*.ts+(|x)', '**/*.ts+(|x)'],
                transformers: [
                    () => ({
                        after: [transformInferno()]
                    })
                ],
                tsconfig: 'tsconfig.json',
                tsconfigOverride: {
                    /* typescript compiler options */
                }
            }),
            babel({
                babelrc: false,
                sourceMaps: isDeploy,
                plugins: [
                    // Compile javascript JSX syntax using inferno's own plugin
                    ['babel-plugin-inferno', {imports: true}]
                ],
                babelHelpers: 'bundled'
            })
    ]
}

Custom namespaces

Inferno always wants to deliver great performance. In order to do so, it has to make intelligent assumptions about the state of the DOM and the elements available to mutate. Custom namespaces conflict with this idea and change the schema of how different elements and attributes might work, so Inferno makes no attempt to support namespaces. Instead, SVG namespaces are automatically applied to elements and attributes based on their tag name.

Development

If you want to contribute code, fork this project and submit a PR from your fork. To run browser tests you need to build the repos. A complete rebuild of the repos can take >5 mins.

$ git clone [email protected]:infernojs/inferno.git
$ cd inferno && npm i
$ npm run test:node
$ npm run build
$ npm run test:browser

If you only want to run the browser tests when coding, use the following to reduce turnaround by 50-80%:

$ npm run quick-test:browser # Compiles all packages and runs browser tests
$ npm run quick-test:browser-inferno # Only compiles the inferno package and runs browser tests
$ npm run quick-test:browser-debug # Compiles all packages and runs browser tests with "debug"

Community

There is an InfernoJS Discord. You can join via https://discord.gg/SUKuhgaBpF.

Contributors

This project exists thanks to all the people who contribute. [Contribute].

Backers

Thank you to all our backers! ๐Ÿ™ [Become a backer]

Sponsors

Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor]

inferno's People

Contributors

abazhenov avatar aretecode avatar craigmichaelmartin avatar davedbase avatar deamme avatar doytch avatar greenkeeperio-bot avatar gwenio avatar havunen avatar hbstone avatar jhsware avatar kanzelm3 avatar longlho avatar lucasavila00 avatar lukeed avatar lukesheard avatar marudor avatar marvinhagemeister avatar mciparelli avatar mstijak avatar nightwolfz avatar nykula avatar rtsao avatar schnueggel avatar srolel avatar tejacques avatar terinjokes avatar trueadm avatar wagerfield avatar wasd171 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  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

inferno's Issues

JSX rendering issue

hello,

  • expected output is:
a
  • however returned output is:
b

code:

import Inferno from 'inferno';
import InfernoDOM from 'inferno-dom';
import InfernoComponent from 'inferno-component';

class MyComponent98 extends InfernoComponent.Component {
  constructor(props) {
    super(props);
    this.state = {
        isok: false,
    };
  };
  componentDidMount() { 
    this.setState({isok: true});
  };
  render() {
    return (
      <MyComponent99
            isok={this.state.isok} />
    )
  };
}

class MyComponent99 extends InfernoComponent.Component {
  constructor(props) {
    super(props);
  };
  render() {
    console.log("isok="+this.props.isok);

    var z = function(v) {
        if (v) {
            return (
                <span>a</span>
            );
        } else {
            return (
                <span>b</span>
            );
        };
    };

    return (
      <div>
          <div>
            {z(this.props.isok)}
          </div>
      </div>
    )
  };
}

InfernoDOM.render(
  <MyComponent98 />,
  document.getElementById("app"));

  • note that if you modify the return () block in MyComponent99 from:
    return (
      <div>
          <div>
            {z(this.props.isok)}
          </div>
      </div>
    )
  • to:
    return (
      <div>
            {z(this.props.isok)}
      </div>
    )
  • real DOM render will return expected output correctly, thus in this case:
a

regards,

Can't get my bundle working.

Trying to compile this JSX crap with new babel 6 after taking a month off and shit is broken. Please help so I can pitch using inferno over react to my new team.

using this in gulpfile:

browserify("src/index.jsx")
.transform("babelify", {
presets: [
'es2015',
'stage-0'
],
plugins: [
"babel-plugin-syntax-jsx",
"babel-plugin-inferno"
]
})
.bundle()
.pipe(fs.createWriteStream("dist/bundle.js"));

Opening the index.html and loading the bundle.js I get this:

Uncaught TypeError: Super expression must either be null or a function, not undefined

Anyone else go through this?

EDIT:

I'm trying to get the example working so here is my index.jsx file:

import Inferno from 'inferno';
import InfernoDOM from 'inferno-dom';

class Component extends Inferno.Component {
constructor(props) {
super(props);
this.state = {
counter: 0
}
}
render() {
return (
// JSX markup
)
}
}

InfernoDOM.render(, document.body);

Children are not set correctly in createElement();

@trueadm @edge Children are not set correctly in createElement()

var template = Inferno.createTemplate(function(createElement) { 
var child = createElement('li', null, "I'm a li-tag");

var element = createElement('ul', { className: 'my-list' }, child);

// will show no child nodes -  NodeList[]
console.log(element.childNodes)

return element;
});

This will not create any children for the ul element as illustrated.

If we dig deeper into this, and use the console.log inside the createElement() function, we will see this:

 NodeList[<TextNode textContent="Text Content">]
<li>
NodeList[]

and if we look at the rendered HTML in the console.log, we will see this:

<ul class="my-list"></ul>

There are no children!

It seems that only the li-element get it's children - text node. This is not attached to the parent container.

This cause other issues as well when we are setting properties. Let us say we want to set select / select multiple.
This will not work, because it has no children. As proved above.

Further. it will try to set properties / attributes on each created node - included the child nodes.
Because in fact there are no child nodes - only rendered elements.

This API function doesn't take an array of children either it seems. So we can't create multiple li-elements.

var template = Inferno.createTemplate(function(createElement) { 
return createElement('ul', { className: 'my-list' }, [child1, child2, child3])  
});

There are no tests created for this either.

build inferno+ES6 code as dev (with webpack) and dist (with rollup) script

Hi, I am trying to setup Inferno with npm build scripts. At this point my approach is to use webpack+watch+reload npm run dev for development and bundle a script with rollup for production npm run bundle.

The inferno packages on npm currently have a jsnext:main field, but that does not point to the es6 code, so rollup will rollup the distributed uml script that was already rollup-ed-up.

One approach could be to exclude Inferno from rolling up.

With the current inferno distribution rollup mycode+inferno is not supported is this correct?

Could inferno distribute the es6 code too?
Else jsnext:main does not make sence.

Here is my hello world. Note: the webpack dev reloading is working with this approach. The Rollup script does rollup the dist/inferno that was already rolledup and builds a scritps that errors in the browser.

main.js
import Inferno from 'inferno';
import InfernoDOM from 'inferno-dom';

const template = Inferno.createTemplate(() => ({
    tag: 'section',
    children: [
        { tag: 'h1', text: 'Hello World'}
    ]
}));

const main = document.querySelector('body');

InfernoDOM.render(template(), main);
package.json scripts
    "dev": "webpack-dev-server --config ./conf/webpack.dev.js --inline",
    "bundle": "node ./conf/rollup.prod.js",
webpack.dev.js
/* global __dirname */

const webpack = require('webpack');
const ExtractText = require('extract-text-webpack-plugin');

module.exports = {
  'entry': {
    'main': './app/main.js',
    'html': './app/index.html'
  },
  'output': {
    'path': './build',
    'publicPath': '/',
    'filename': './[name].js'
  },
  'devServer': {
    'contentBase': './build',
  },
  'module': {
    'loaders': [
      {
        'test': /\.html$/,
        'loader': 'file',
        'query': {
          'name': '[name].[ext]'
        }
      },
      {
        'loader': 'babel-loader',
        'test': /\.js$/,
        // 'exclude': /(node_modules)/,
        'query': {
          'presets': ['es2015']
        }
      }
      ]
    },
    'plugins': [
      new ExtractText('index.html'),
      new webpack.NoErrorsPlugin()
    ],
    'devtool': 'source-map'
  };
rollup.prod.js
const rollup = require('rollup').rollup;
const npm = require('rollup-plugin-npm');
const commonjs = require('rollup-plugin-commonjs');
const babel = require('rollup-plugin-babel');

rollup({
  'entry': './main.js',
  'plugins': [
    npm({ 'jsnext': true, 'main': true }),
    commonjs(),
    babel({ 'babelrc': false, 'presets': ['es2015-rollup'] }),
  ]
})
.then(function(bundle){
  return bundle.write({
    'banner': '/* Bundle */',
    'dest': 'dist/scripts/bundle.js',
    'format': 'iife',
    'indent': false,
    'sourceMap': false
  });
})
.catch(function(error){
  console.log(error);
});

when running the prod script built with rollup there is a Uncaught TypeError: babelHelpers.typeof is not a function because babelHelpers is defined in 2 places and the second overwrites the first one.

Children of template are not rendering properly

Here is a demo of the problem:

http://codepen.io/Pauan/pen/572d2d405fcbbbaf6a85b38fd5517094

It uses the latest code from trueadm/inferno/packages/inferno/dist/ on the dev branch.

The demo is a reduced test case: my real application is much more complex.

But the basic idea is the same: depending on a boolean condition, either render one subtree, or render another subtree.

The problem is, when it updates, it should display BarQux but instead it only displays Bar

Am I doing something wrong? Do templates not allow for a variable number of children? If so, then how can I accomplish this use case?

Docs: README.md typo

"Writing intensive modern UIs that require many updates/animations falls apart and becomings overly complicated"

Replace "and becomings" with ", becoming" or "and becomes".

Uncaught RangeError: Maximum call stack size exceeded in most recent dev branch

I created a repo that has a minimal code sample to reproduce the bug, with latest dev branch of inferno as of 01/01/2016 20:38 PST

https://github.com/andyrj/inferno-max-call-stack-exceeded-bug

just git clone that repo, and then you can run the app with npm start... load up localhost:8080 in browser and it will dump out the error in chrome dev tools. The whole tab gets borked and chrome dev tools will stop responding on my machine.

Ran into this while making my inferno-redux-starter-kit demo project.

Roadmap 2016

Upcoming roadmap for Q1-Q2 2016.

  • Break inferno into separate packages, such as: inferno-dom, inferno-server, inferno-component
  • DOM node/component hooks
  • inferno-test-utilities
  • DOM node transitions
  • Server side rendering
  • Chrome dev tool extension
  • propTypes
  • Performance optimisations
  • When refs are strings, imply the same logic as React and add them to their component like this.refs.node
  • Documentation
  • External libraries that we deem essential: inferno-router and inferno-redux
  • Solve polyfill/legacy browser problems

focus are lost!

You can't maintain focus in Inferno. You have no support for document.activeElement().

#perf-spike: Having trouble using Inferno with JSX (babel-plugin-inferno)

Hi,

First of all, thank you all for your efforts on this project!

Like I said in the title, I get this error: _src2.default.createFragment is not a function. By the way, I am also using webpack.

I can see that compiler creates this template:

_src2.default.render(_src2.default.createFragment({
  component: Component,
  props: {
    title: "abc",
    name: "basic-render"
  }
}, tpl1126106956), document.getElementById("app"));

Here is my configuration so far:

// webpack-dev-server.config.js
var webpack = require('webpack');
var path = require('path');
var buildPath = path.resolve(__dirname, 'build');
var nodeModulesPath = path.resolve(__dirname, 'node_modules');

var config = {
  entry: [
    'webpack/hot/dev-server',
    'webpack/hot/only-dev-server',
    path.join(__dirname, '/src/app/app.js')
  ],

  resolve: {
    extensions: ["", ".js", ".jsx"]
  },

  devServer:{
    contentBase: 'src',
    devtool: 'eval',
    hot: true,
    inline: true,
    port: 3000
  },

  devtool: 'eval',

  output: {
    path: buildPath,
    filename: 'app.js'
  },

  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ],

  module: {
    loaders: [{
      test: /\.(js|jsx)?$/,
      loaders: ['babel-loader']
      //exclude: [nodeModulesPath]
    }]
  }
};

module.exports = config;
//.babelrc
{
    "env": {
        "development": {
            "presets": [
                "es2015", "stage-0"
            ]
        },
        "production": {
            "presets": [
                "es2015-rollup"
            ]

        }
    },
    "plugins": ["babel-plugin-inferno", "babel-plugin-syntax-jsx"]
}

And the app.js:

import Inferno from '../../node_modules/Inferno/src';

class MyComponent extends Inferno.Component {
  render() {
    return (
      <div className="basic">
        <span className={ this.props.name }>The title is { this.props.title }</span>
      </div>
    );
  }
}

Inferno.render(<MyComponent title="abc" name="basic-render" />, document.getElementById("app"));

I am sorry if this is just a rookie mistake and a useless question, but I'm quite new to this npm world. Thank you!

EDIT: I can run the example app without JSX just fine:

const { createElement } = Inferno.TemplateFactory;

class MyComponent extends Inferno.Component {
  render() {
    let template = Inferno.createTemplate((name, title) =>
      createElement("div", {className: "basic"},
        createElement("span", {className: name}, "The title is ", title)
      )
    );

    return template(this.props.name, this.props.title);
  }
}

let template = Inferno.createTemplate((MyComponent, title) =>
  createElement('div', null,
    createElement(MyComponent, {title: title, name: "basic-render"})
  )
);

Inferno.render(template(MyComponent, 'abc'), document.getElementById("app"));

Test sometimes fails

Error below -- the test fails about half the time, which seems VERY odd -- maybe there is a race condition or something?

DOM component tests (jsx)
should render a component with a list of children that dynamically update via setState
Second render (update) โ€ฃ

Error: Uncaught AssertionError: expected '<div><div class="my-component"><h1>Saab 0</h1><button type="button">Increment</button></div><div class="my-component"><h1>Volvo 0</h1><button type="button">Increment</button></div><div class="my-component"><h1>BMW 0</h1><button type="button">Increment</button></div></div>' to equal '<div><div class="my-component"><h1>Saab 1</h1><button type="button">Increment</button></div><div class="my-component"><h1>Volvo 1</h1><button type="button">Increment</button></div><div class="my-component"><h1>BMW 1</h1><button type="button">Increment</button></div></div>' (http://localhost:8080/setup/chai.js:206)Error: Uncaught AssertionError: expected '<div><div class="my-component"><h1>Saab 0</h1><button type="button">Increment</button></div><div class="my-component"><h1>Volvo 0</h1><button type="button">Increment</button></div><div class="my-component"><h1>BMW 0</h1><button type="button">Increment</button></div></div>' to equal '<div><div class="my-component"><h1>Saab 1</h1><button type="button">Increment</button></div><div class="my-component"><h1>Volvo 1</h1><button type="button">Increment</button></div><div class="my-component"><h1>BMW 1</h1><button type="button">Increment</button></div></div>' (setup/chai.js:206)
    at global.onerror (eval at <anonymous> (specs.js:8176:9), <anonymous>:12308:10)
_src2.default.render(tpl2280747625(Wrapper), container);
(0, _waits2.default)(30, function () {
    expect(container.innerHTML).to.equal('<div><div class="my-component"><h1>Saab 1</h1><button type="button">Increment</button></div><div class="my-component"><h1>Volvo 1</h1><button type="button">Increment</button></div><div class="my-component"><h1>BMW 1</h1><button type="button">Increment</button></div></div>');
    done();
});

Documentation

Good day, @trueadm !

We have // TODO in README.md in API section.
Could you finish this section, please?

Thanks!

state change in parent component will not reflect props value change in children's JSX template

hello,

can some one help to clarify, why this will not return this expected output:

isok=true
ab

but instead returns this:

isok=true

code:

import Inferno from 'inferno';
import InfernoDOM from 'inferno-dom';
import InfernoComponent from 'inferno-component';

class MyComponent98 extends InfernoComponent.Component {
  constructor(props) {
    super(props);
    this.state = {
        isok: false,
    };
  };
  componentDidMount() { 
    this.setState({isok: true});
  };
  render() {
    return (
      <MyComponent99
            isok={this.state.isok} />
    )
  };
}

class MyComponent99 extends InfernoComponent.Component {
  constructor(props) {
    super(props);
  };
  render() {
    console.log("isok="+this.props.isok);
    return (
      <div>
        isok={this.props.isok?'true':'false'}

        <div>
            {this.props.isok &&
                ['a', 'b'].map( (x) => {
                    return (
                        <span>{x}</span>
                    );
            })}
        </div>
      </div>
    )
  };
}

InfernoDOM.render(
  <MyComponent98 />,
  document.getElementById("app"));

regards,

inferno without jsx (jsx alternatives)

Hi I found inferno while looking for jsx alternatives though t7. Call me not openminded for not falling in love with jsx. Watching the redux series made me want to use redux and just confirmed that I never touch{' '} . I know jsx is optional, but calling createElement() for every element does not make it any better.

From reading some of the tests I noticed that Inferno allows to create multiple elements with Inferno.createTemplate https://github.com/trueadm/inferno/blob/dev/test/browser/acceptance/no-jsx/dom-elements-tests4.js#L454 , so my first question is: would it be possible to just pass objects around or does the createTemplate need to be called everywhere?

I believe citojs uses just objects and at some point lastly calls cito.vdom.append once. Deku calls it the virtual element. I know this is syntax sugar but I bet there are some others not wanting to use jsx ever or trying to get away at some point in the future. Calling createElement might have a negative perf impact.

My second question is how deep is jsx to be built into Inferno? Will there ever be Inferno without jsx? smaller library size, less code to execute, no compile step when using es6-module-loader.

Also reading about Inferno and state-diffing "feels just rightโ„ข"! I read all the issues and looked at the dev branch. But I want to know more :)

Would you suggest to start playing around with the dev branch? I dont mind things are not final...

new toggle issue in latest dev

There is new issue with toggling components / elements.

var tpl3578458729 = Inferno.createTemplate(function (v0) {
    return {
        tag: 'div',
        attrs: {
            className: 'login-view bg-visma'
        },
        children: v0
    };
});

var tpl188998005 = Inferno.createTemplate(function () {
    return {
        tag: 'div',
        children: 'VISIBLE'
    };
});

var tpl3754840163 = Inferno.createTemplate(function (v0) {
    return {
        tag: 'div',
        children: {
            tag: 'button',
            attrs: {
                onClick: v0
            },
            children: 'Make visible'
        }
    };
});

var TEST = function() {
    this.state = {
        show: false
    };

    this.makeVisible = function() {
        this.setState({
            show: true
        });
    }.bind(this);


    this.render = function() {
        return tpl3578458729((function () {
            if (this.state.show === true) {
                return tpl188998005(null);
            } else {
                return tpl3754840163(this.makeVisible);
            }
        }).call(this));
    };
};
TEST.prototype = new Inferno.Component(null);
TEST.constructor = TEST;

var tpl79713834 = Inferno.createTemplate(function (v0) {
    return {
        tag: v0
    };
});

InfernoDOM.render(tpl79713834(TEST), document.body);

I think it has something to do with children types as it works with other types / combinations

Children not rendering properly

Simple test case, tested against latest dev branch.

class Counter extends Inferno.Component {
        constructor(props) {
            super(props);
            this.state = {
                count: 0
            };
            this.incrementCount = this.incrementCount.bind(this);
        }
        incrementCount(){
            this.setState({
                count: this.state.count + 1
            });
        }
        render(){
            return (
                <div class="my-component">
                    <h1>{this.props.car} {this.state.count}</h1>
                    <button type="button" onClick={this.incrementCount}>Increment</button>
                </div>
            );
        }
    }
    class Wrapper extends Inferno.Component {
        constructor(props) {
            super(props);
        }
        render() {
            return (
                <div>
                    {["Saab", "Volvo", "BMW"].map(function(c) {
                        return (<Counter car={c} />)
                    })}
                </div>
            )
        }
    }
    Inferno.render(<Wrapper />, APP_ELEMENT);

Inside incrementCount the correct this context exists with the prop.car set as well as state.count set but the only render() method called is with the this context for BMW.

I'll try to track this down inside the code and see if I can provide more insight.

maximum callstack exceeded

I'm new to Inferno, so apologies if I am missing something obvious here. I have a really basic component with just a render() function:

render() {
    return (
      <div className="main">
        <Navbar />
        <div id="app"/>
      </div>
    );
  }

So Navbar is itself also a component, a basic <ul> for now. When I actually get to rendering the component as such:

Inferno.render(<Main/>, document.getElementById('root'));

I get a maximum call stack exceeded. It seems that in:

function getCorrectItemForValues(node, item) {
        if (node !== item.domTree && item.parent) {
            return getCorrectItemForValues(node, item.parent);
        } else {
            return item;
        }
    }

Its getting caught in the recursion. It has something to do with the Navbar component since when I set a breakpoint there I can see the item is the Navbar component. If I change that to just return item in all cases it works fine but I'm sure I am missing something obvious.

Any insight is appreciated! Thanks!

Child component focus / blur events and state are messed up in the latest Dev branch.

There is issue that focus / blur trigger immediately. Expected result focus should happen when item is focused changing the text to True CarModel, and when moving out it should change False CarModel.

However those trigger immediately now.

latest Dev of inferno, including @KFlash 's workflow fix.
Inferno-babel-plugin: v0.2.9

Source:

import Inferno from './../../../../node_modules/inferno/dist/inferno.js';
import InfernoDOM from './../../../../node_modules/inferno/dist/inferno-dom.js';



    class FocusInput extends Inferno.Component {
        constructor(props) {
            super(props);

            this.state = {
                isEditMode: false
            };

            this.blur = this.blur.bind(this);
            this.focus = this.focus.bind(this);
        }

        blur() {
            console.log("BLUR");
            this.setState({
                isEditMode: false
            });
        }

        focus() {
            console.log("FOCUS");
            this.setState({
                isEditMode: true
            });
        }

        render(props) {
            return (
                <div>
                    <div contenteditable="true" class={this.state.isEditMode+''} onBlur={this.blur} onFocus={this.focus}>{this.state.isEditMode + this.props.value}</div>
                </div>
            )
        }
    }

    class Looper extends Inferno.Component {
        constructor(props) {
            super(props);
        }

        render() {
            return (
                <div class="loop">
                    {['Volvo', 'BMW', 'Mercedes'].map((car) => {
                        return (
                            <FocusInput value={car} />
                        )
                    })}
                </div>
            )
        }
    }

    InfernoDOM.render(<Looper />, APP_ELEMENT);

compiled output:

var tpl2011289543 = _inferno2.default.createTemplate(function (v0, v1, v2, v3) {
    return {
        tag: 'div',
        children: {
            tag: 'div',
            attrs: {
                contenteditable: 'true',
                class: v0,
                onBlur: v1,
                onFocus: v2
            },
            children: v3
        }
    };
});

var tpl1190498794 = _inferno2.default.createTemplate(function (v0) {
    return {
        tag: 'div',
        attrs: {
            class: 'loop'
        },
        children: v0
    };
});

var tpl3232491342 = _inferno2.default.createTemplate(function (v0, v1) {
    return {
        tag: v0,
        attrs: {
            value: v1
        }
    };
});

var tpl2014586923 = _inferno2.default.createTemplate(function (v0) {
    return {
        tag: v0
    };
});

    var FocusInput = (function (_Inferno$Component) {
        _inherits(FocusInput, _Inferno$Component);

        function FocusInput(props) {
            _classCallCheck(this, FocusInput);

            var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(FocusInput).call(this, props));

            _this.state = {
                isEditMode: false
            };

            _this.blur = _this.blur.bind(_this);
            _this.focus = _this.focus.bind(_this);
            return _this;
        }

        _createClass(FocusInput, [{
            key: 'blur',
            value: function blur() {
                console.log("BLUR");
                this.setState({
                    isEditMode: false
                });
            }
        }, {
            key: 'focus',
            value: function focus() {
                console.log("FOCUS");
                this.setState({
                    isEditMode: true
                });
            }
        }, {
            key: 'render',
            value: function render(props) {
                return tpl2011289543(this.state.isEditMode + '', this.blur, this.focus, this.state.isEditMode + this.props.value);
            }
        }]);

        return FocusInput;
    })(_inferno2.default.Component);

    var Looper = (function (_Inferno$Component2) {
        _inherits(Looper, _Inferno$Component2);

        function Looper(props) {
            _classCallCheck(this, Looper);

            return _possibleConstructorReturn(this, Object.getPrototypeOf(Looper).call(this, props));
        }

        _createClass(Looper, [{
            key: 'render',
            value: function render() {
                return tpl1190498794(['Volvo', 'BMW', 'Mercedes'].map(function (car) {
                    return tpl3232491342(FocusInput, car);
                }));
            }
        }]);

        return Looper;
    })(_inferno2.default.Component);

    _infernoDom2.default.render(tpl2014586923(Looper), _dom.APP_ELEMENT);

Fails on IE9/10 -- 'Map' is undefined

Comes from src/core/createTemplate.js:32

const dynamicNodeMap = new Map();

This issue is mostly just to document that a Map polyfill is currently necessary. One solution is to specify this in the README/documentation, another is to avoid using Map.

What is the roadmap ?

Hi, @trueadm

Seems great this project, but comparing to the readme. A couple of missing pieces?

  • createElement(). Is this something coming soon? I think that will give you even more stars. I was looking into the code, and shouldn't be too hard to copy from your very great t7 script? Or maybe I'm wrong.
  • different things seems unfinished inside the code, any plans for finishing this?
  • any plans for hooks ala Snabbdom? Not lifecycle, but example when you create a fragment, you can trigger a callback, same when you remove or update a fragment.
  • Server side rendring, is this going to happen soon? It's more or lest a must have if I should convert to Inferno. I'm from React world!

Btw. Is there any benchmark for latest version, and what is the roadmap?

I will absolutly start to use this script soon as a createElement() are done. And I like your component part too ๐Ÿ‘

relative import of modules that were build from repo

hello,

can some one help, how to use the master/dev repo modules builds?
after npm run clean && npm run build, i'll get these build results in inferno/dist/ directory:

-rw-r--r-- 1 zst zst   6963 Jan 19 23:29 inferno-component.js
-rw-r--r-- 1 zst zst   3304 Jan 19 23:29 inferno-component.min.js
-rw-r--r-- 1 zst zst 123599 Jan 19 23:29 inferno-dom.js
-rw-r--r-- 1 zst zst  41078 Jan 19 23:29 inferno-dom.min.js
-rw-r--r-- 1 zst zst  19992 Jan 19 23:29 inferno-server.js
-rw-r--r-- 1 zst zst   8303 Jan 19 23:29 inferno-server.min.js
-rw-r--r-- 1 zst zst   9114 Jan 19 23:29 inferno.js
-rw-r--r-- 1 zst zst   3209 Jan 19 23:29 inferno.min.js

however when trying to import modules via relative path:

import Inferno from './static/vendor/inferno/dist/inferno.min.js';
import InfernoDOM from './static/vendor/inferno/dist/inferno-dom.min.js';
import InfernoComponent from './static/vendor/inferno/dist/inferno-component.min.js';

no errors are reported during bundle creation, but inferno component will not get mounted into DOM node specified via getElementByID.
i apologize if the issue is obvious. i am not familiar with front end dev processes.

these are imports specified when packages are installed via npm, which do work ok:

import Inferno from 'inferno';
import InfernoDOM from 'inferno-dom';
import InfernoComponent from 'inferno-component';

note that i believe that i am doing something wrong rather than there is an issue/bug with the actual inferno package.

regards,

Wrapper components throw error

I'm building a Provider component for our inferno-reduxbindings.
Unfortunately, the following always throws the same error: Uncaught TypeError: Cannot read property 'create' of undefined

Here is the (minimal code):

class Provider extends Inferno.Component {
    render(){
        return <div>
            {this.props.children}
        </div>
    }
}

Inferno.render(
        <Provider>
            <h1>Hello World</h1>
        </Provider>
        , App.node
    )

Returning this.props.children does not work either, it gives me Uncaught Error: Inferno Error: A valid template node must be returned. You may have returned undefined, an array or some other invalid object.

Thanks!

npm run build error

On branch dev (8a3fd3d), after a fresh rm -rf node_modules and npm i I get the following error when building. All tests pass

npm run build

> [email protected] build /Users/user/source/examples/inferno
> npm run clean && mkdirp dist && npm run build:dev && npm run build:prod


> [email protected] clean /Users/user/source/examples/inferno
> rimraf coverage/ dist/


> [email protected] build:dev /Users/user/source/examples/inferno
> babel-node config/rollup.config.js dev

/Users/user/source/examples/inferno/config/rollup.config.js:139
    throw reason;
    ^

SyntaxError: Unterminated comment (30:59) in /Users/user/source/examples/inferno/src/DOM/createTree.js
    at Parser.pp$4.raise (/Users/user/source/examples/inferno/node_modules/rollup/dist/rollup.js:4271:13)
    at Parser.pp$7.skipBlockComment (/Users/user/source/examples/inferno/node_modules/rollup/dist/rollup.js:4525:24)
    at Parser.pp$7.skipSpace (/Users/user/source/examples/inferno/node_modules/rollup/dist/rollup.js:4576:18)
    at Parser.pp$7.nextToken (/Users/user/source/examples/inferno/node_modules/rollup/dist/rollup.js:4497:54)
    at Parser.pp$7.next (/Users/user/source/examples/inferno/node_modules/rollup/dist/rollup.js:4452:8)
    at Parser.pp$3.parseIdent (/Users/user/source/examples/inferno/node_modules/rollup/dist/rollup.js:4219:8)
    at Parser.pp$2.parseBindingAtom (/Users/user/source/examples/inferno/node_modules/rollup/dist/rollup.js:3475:19)
    at Parser.pp$2.parseMaybeDefault (/Users/user/source/examples/inferno/node_modules/rollup/dist/rollup.js:3522:23)
    at Parser.pp$2.parseBindingList (/Users/user/source/examples/inferno/node_modules/rollup/dist/rollup.js:3507:23)
    at Parser.pp.parseFunctionParams (/Users/user/source/examples/inferno/node_modules/rollup/dist/rollup.js:3169:22)

npm ERR! Darwin 14.5.0
npm ERR! argv "/Users/user/node/latest/bin/node" "/Users/user/source/examples/inferno/node_modules/.bin/npm" "run" "build:dev"
npm ERR! node v5.3.0
npm ERR! npm  v3.5.2
npm ERR! code ELIFECYCLE
npm ERR! [email protected] build:dev: `babel-node config/rollup.config.js dev`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the [email protected] build:dev script 'babel-node config/rollup.config.js dev'.
npm ERR! Make sure you have the latest version of node.js and npm installed.
npm ERR! If you do, this is most likely a problem with the inferno package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR!     babel-node config/rollup.config.js dev
npm ERR! You can get information on how to open an issue for this project with:
npm ERR!     npm bugs inferno
npm ERR! Or if that isn't available, you can get their info via:
npm ERR!     npm owner ls inferno
npm ERR! There is likely additional logging output above.

npm ERR! Please include the following file with any support request:
npm ERR!     /Users/user/source/examples/inferno/npm-debug.log

npm ERR! Darwin 14.5.0
npm ERR! argv "/Users/user/node/latest/bin/node" "/Users/user/node/latest/bin/npm" "run" "build"
npm ERR! node v5.3.0
npm ERR! npm  v3.3.12
npm ERR! code ELIFECYCLE
npm ERR! [email protected] build: `npm run clean && mkdirp dist && npm run build:dev && npm run build:prod`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the [email protected] build script 'npm run clean && mkdirp dist && npm run build:dev && npm run build:prod'.
npm ERR! Make sure you have the latest version of node.js and npm installed.
npm ERR! If you do, this is most likely a problem with the inferno package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR!     npm run clean && mkdirp dist && npm run build:dev && npm run build:prod
npm ERR! You can get their info via:
npm ERR!     npm owner ls inferno
npm ERR! There is likely additional logging output above.

npm ERR! Please include the following file with any support request:
npm ERR!     /Users/user/source/examples/inferno/npm-debug.log

[Q] How did you do it?

Hi. @KFlash here. Author of Trackira. I just wonder how you got this performance and your future plans?

Just to say it. When I did Trackira my goal was to create something average for people to use. And the performance are bob bob.

In my other private project I do things for real and this project is a component framework with a VD just like inferno. The performance on this is much better overall then citojs and almost same as inferno.

So I started to wonder. Inferno can work with or without templates? As in REACT createElement? How do you set your html attributes so they become an array? And can you explain the concept of your value nodes and static and dynamic?

When I attach attributes on a virtual node. I do:

vnode.attributes ()

Same concept as in jsBlocks.

So when I create a node I can use chaining like this:

CreateElement("div")
   .attributes ()
   .css ()
   .children () // create new child nodes

as you may notice. I can now only diff/patch children, attributes and css without touching anything else. And do a update and add the new changes.

Still you get super performance so could you tell me how?

Is there any mount / unmount in inferno and if so. Can you mount / unmount a single node or a whole tree?

I can in my project unmount all children on a specific node or a whole tree. And also mount.

And then I wonder about dirty checking. I haven't finished that yet. But I saw in the code you plan too. What would be the best approach here regarding performance?

Animation and transitions. Are you going to support? Your thoughts here. Just curious because I'm planning to add it but searching for the best approach.

simple if statement inside component DOM

Hey,

This might be silly question, but I couldn't get it working so I decided to post it here. I'm not able to render component DOM based on boolean condition. For example if I want to hide or show different DOM nodes based on some logic.

versions:
-infernoJs dev
-babel-plugin-inferno: ^0.2.5,

I have tried following:

    class Component extends Inferno.Component {
        constructor(props) {
            super(props);
            this.state = {
                counter: 0
            }
        }
        render() {
            let condition = true; // some logic
            return (
                <div>
                    {condition ? <h1>BIG</h1> : <h2>small</h2>}
                    <p>test</p>
                </div>
            )
        }
    }

    Inferno.render(<Component />, APP_ELEMENT);

output:

var tpl4282471407 = _inferno2.default.createTemplate(function (v0) {
        return {
            tag: 'div',
            children: ['                    ', v0, '                    ', {
                tag: 'p',
                children: 'test'
            }, '                ']
        };
    });

    var tpl3625453295 = _inferno2.default.createTemplate(function () {
        return {
            tag: 'h1',
            children: 'BIG'
        };
    });

    var tpl4021787591 = _inferno2.default.createTemplate(function () {
        return {
            tag: 'h2',
            children: 'small'
        };
    });

    var tpl1546018623 = _inferno2.default.createTemplate(function (v0) {
        return {
            tag: v0
        };
    });
// this function gets called runtime
    function loginPresenter() {

        var Component = (function (_Inferno$Component) {
            _inherits(Component, _Inferno$Component);

            function Component(props) {
                _classCallCheck(this, Component);

                var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(Component).call(this, props));

                _this.state = {
                    counter: 0
                };
                return _this;
            }

            _createClass(Component, [{
                key: 'render',
                value: function render() {
                    var condition = true; // some logic
                    return tpl4282471407(condition ? tpl3625453295(null) : tpl4021787591(null));
                }
            }]);

            return Component;
        })(_inferno2.default.Component);

        _inferno2.default.render(tpl1546018623(Component), _dom.APP_ELEMENT);
    }

fails with runtime exception:
Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.

Also tried following:

    class Component extends Inferno.Component {
        constructor(props) {
            super(props);
            this.state = {
                counter: 0
            }
        }
        render() {
            let condition = true; // some logic
            return (
                <div>
                    {function(){
                        if (condition) {
                            return <h1>BIG</h1>
                        } else {
                            return <h2>small</h2>
                        }
                    }.call(this)}
                    <p>test</p>
                </div>
            )
        }
    }

    Inferno.render(<Component />, APP_ELEMENT);

output:

    var tpl4282471407 = _inferno2.default.createTemplate(function (v0) {
        return {
            tag: 'div',
            children: ['                    ', v0, '                    ', {
                tag: 'p',
                children: 'test'
            }, '                ']
        };
    });

    var tpl3625453295 = _inferno2.default.createTemplate(function () {
        return {
            tag: 'h1',
            children: 'BIG'
        };
    });

    var tpl4021787591 = _inferno2.default.createTemplate(function () {
        return {
            tag: 'h2',
            children: 'small'
        };
    });

    var tpl1546018623 = _inferno2.default.createTemplate(function (v0) {
        return {
            tag: v0
        };
    });

// this function gets called runtime
    function loginPresenter() {

        var Component = (function (_Inferno$Component) {
            _inherits(Component, _Inferno$Component);

            function Component(props) {
                _classCallCheck(this, Component);

                var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(Component).call(this, props));

                _this.state = {
                    counter: 0
                };
                return _this;
            }

            _createClass(Component, [{
                key: 'render',
                value: function render() {
                    var condition = true; // some logic
                    return tpl4282471407((function () {
                        if (condition) {
                            return tpl3625453295(null);
                        } else {
                            return tpl4021787591(null);
                        }
                    }).call(this));
                }
            }]);

            return Component;
        })(_inferno2.default.Component);

        _inferno2.default.render(tpl1546018623(Component), _dom.APP_ELEMENT);
    }

and it also fails runtime with following exception:
Chrome: Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.
Firefox: TypeError: Argument 1 of Node.appendChild is not an object.

tested on Chrome 47.0.2526.106 m and Firefox 43.0.2

What am I doing wrong here... ?

Havunen

Babel plugin / Inferno fails with rendering SVG

Inferno babel plugin 0.2.5 or Inferno 0.4.0 is failing when there is component with SVG inside.

class Component extends Inferno.Component {
        constructor(props) {
            super(props);
         }
         render() {
            return (
               <svg class="alert-icon">
                    <use xlink:href="#error"></use> // This should link to inline svg with ID error
                </svg>
            )
         }
}

 Inferno.render(<Component />, APP_ELEMENT);

Exception is thrown:

 ModuleParseError: Module parse failed: ****\node_modules\babel-loader\index.js?{"presets":["es2015"],"plugins":["babel-plugin-syntax-jsx","babel-pl
You may need an appropriate loader to handle this file type.
|             tag: 'use',
|             attrs: {
|                 [object Object]: '#error'
|             }
|         }, '            ']

Expected result:
It should render SVG element with use element refering to error id.

React has solved it this way: http://stackoverflow.com/questions/26815738/svg-use-tag-and-reactjs

Havunen

Build system and code revamp

I love the design and concept behind Inferno -- it fascinates me and I'm just raring to switch from React. But before we get there, there are a few things Inferno needs to address. I'm a bit busy right now, so I'll just outline my main concerns:

  • Grunt is outdated. Gulp is a much better and faster build system.
  • Same with Closure. UglifyJS is a much more capable and supported utility.
  • Since you're using babel, it would be nice to use the new syntax. (let, import, etc.)
  • Inferno should be more modularized. Unless a library is really small, a single module is usually a bad sign.
  • Babel, Uglify, and more all come with webpack, which is what most new libraries (especially React era!) are using to build nowadays. With webpack, you can also get rid of bootstrap.js which nobody should be writing these days.

These changes should eventually reflect in t7, as well (modularizing; people would also love if the t7-precompiler were written as a babel plugin).

I'd be more than happy to submit a PR in a bit that works out all of these issues; you'll love the end result.

Broken source or what?

I investigated a little and everything works just fine until this commit: ae21741

I also managed to get a benchmark working with this one. And also you may notice
https://github.com/trueadm/inferno/blob/ae217413b73757c483cdd3c59751144768444726/build/inferno.js#L323-L368

This is all iteration through arrays. Updatefragment doesn't.

However, I got the benchmark running with this modified code:

function template1(fragment) {
  var root = document.createElement("div");
  fragment.dom = root;
  fragment.$e0 = root;
  fragment.valuesLength = 1;
  fragment.$t0 = Inferno.Type.LIST;
}

function template2(fragment) {
  var root = document.createElement("span");
  root.textContent = fragment.$v0;
  fragment.dom = root;
  fragment.$e0 = root;
  fragment.$t0 = Inferno.Type.TEXT;
}

function renderTree(nodes) {
  var children = [];
  var i;
  var e;
  var n;

  for (i = 0; i < nodes.length; i++) {
    n = nodes[i];
    if (n.children !== null) {
      children.push({dom: null, next: null, template: template1, templateKey: 0, key: n.key, $e0: null, $t0: 0, $v0: renderTree(n.children)});
    } else {
      children.push({dom: null, next: null, template: template2, templateKey: 1, key: n.key, $e0: null, $t0: 0, $v0: n.key.toString()});
    }
  }

  return children;
}

In the latest updates starting from 29 days ago. This all breaks, and if you try to run the same code, you will get in console. NotFoundError: Node was not found

You may notice I didn't put an length in the template2 function. If I do so, we will run into another console error: TypeError: element is null. This happen because it can't find the element when trying to update two text nodes.

If I modify even more, a various console error messages are showing in the console.log.

However. This commit 3fa8c82 contains almost identical code, except that the updateFragment now also iterate through arrays. And here it seems to break!

For update from august 24, when I changed the variable names, I ran into this console.log error with the same code:
TypeError: Argument 1 of Node.appendChild is not an object.
And when I tried with the newest update the same, or I got messages telling me that the Node was not found and a whole lot of others.

Even if the newer updates have different names, but should have worked. But doesn't simply because all the newest updates include the same strange behaviour as mentioned.

I was investigating this because I was looking deeper into creating a createElement() function and PR. But figured out I will let you add that feature. I think it should be simple enough to add createElement() if you get everything sorted out first.

I also noticed that the performance seems to be worse with latest updates. However I couldn't test the latest commits because of the strange things I mentioned.

I tried to compare with t7 when I did this manually to see what did I do wrong. But as long as valueLength is there and set to 1, it should have found the first item in the array. Same as I see here in t7 code: https://github.com/trueadm/t7/blob/master/t7.js#L108-L199

A createElement() function shouldn't be harder then this, or what?

function createElement(root, container, parent) {

    // Array holding the value types
    var values = [];

    // Template function
    var frag = function(fragment) {

        var t = document.createElement(root.tag);

        var children = root.children;

        fragment.dom = t;
        fragment.templateElement = t;

    // Iterate through the children or values here?
    // Anyhow, the idea was to push the values to the value array, and then count the length on it.

        if (children != null) {

            if (root.children instanceof Array) {

                for (var i = 0; i < root.children.length; i++) {

                    var child = root.children[i];

                    if (typeof child === "string" && root.children.length === 1) {

                        if (typeof child === "string") {
                            if (!parent) {
                                fragment.templateElement.textContext = child;
                            } else {
                                parent.textContext = child;
                            }
                        }
                    } else if (typeof child === "string" && root.children.length > 1) {

                    } else if (child != null) {

                    }
                }
            }
        }
        if (values.length === 1) {
            fragment.templateValue = templateValues[0];
            fragment.templateElements = null;
            fragment.templateTypes = null;
        } else if (values.length > 1) {

            fragment.templateValues = [templateValues.join(", ")];
            fragment.templateElements = Array(templateValues.length);
            fragment.templateTypes = Array(templateValues.length);
        }
   }

    return {
        dom: null,
        templateElement: null,
        template: root.children.length > 0 ? frag : ""
    };
}

// How to:
createElement( { tag:"div", children:"hello, world!" }, document.body);

I didn't continue on this due to all the other mess, and you know the code best. So I guess you can handle it better then me ๐Ÿ‘ I was thinking to get the same result as you do with t7. Then you could use your existing render function as before.

Anyhow. I really liked your script, I did. But I'm forced to abondon this now. Way to complicated for others I guess to contribute. Let's hope it will be better in the future!

And for now I remove my star.

Looping is broken at latest Dev.

Hey,

Looping components seems to be broken at latest dev. It should render two

elements inside div's but nothing happens.

    var BaseView = Inferno.createTemplate(function (v0, v1) {
        return {
            tag: 'div',
            attrs: {
                class: 'login-view'
            },
            children: [{
                tag: 'button',
                attrs: {
                    onClick: v0
                },
                children: 'ADD'
            }, {
                tag: 'br'
            }, v1]
        };
    });

    var Looper = Inferno.createTemplate(function (v0) {
        return {
            tag: 'div',
            children: [
                {
                    tag: 'h1',
                    children: v0
                }
            ]
        };
    });

    var starter = Inferno.createTemplate(function (v0) {
        return {
            tag: v0
        }
    });

    var SomeError = function() {
        this.state = {
            list: ['SS', 'SS1']
        };

        this.render = function() {
            return BaseView(this.toggle, (function () {
                this.state.list.map(function(result){
                    return Looper(result);
                });
            }).call(this));
        };
    };
    SomeError.prototype = new Inferno.Component(null);
    SomeError.constructor = SomeError;

    InfernoDOM.render(starter(SomeError), document.body);

why cito?

Hi @trueadm,
English is not my language, hope you understand me :)
I'm curious. Why you changed from Bobril to cito?
Seems to me that Bobril is superior code.
I think virtual-dom is another good alternative, because of its current penetration in the community.

Transitions/Animations

My idea of how we might implement some transition states, in this case when a DOM node enter and leaves the document, we want to apply some form of transition along with a delay before the DOM node completes its enter or leave.

<div className="box" transition={{ name: 'box-animate', enter: 500, leave: 250 }} />

This would work very much like React's ReactCSSTransitionGroup (https://facebook.github.io/react/docs/animation.html).

So in the case above, when the div DOM node gets removed/added from/to the document, Inferno will apply some additional classNames (prefixed with box-animate as per above) to the DOM node. These classes would then allow the user to implicitly set different styles for the duration of the delay (in the example above, this would be 500ms or 250ms).

What do people think about this idea?

Uncaught TypeError: Cannot read property 'nextSibling' of undefined

hello,

in relation to the example case here on line #50:
https://github.com/trueadm/inferno/blob/master/examples/playground/playground.js#L50

i'm wondering if throwing of exception below:
Uncaught TypeError: Cannot read property 'nextSibling' of undefined

is valid or not in case that you provide empty array to the map() call.

specifically, instead of currently provided list of items:
[ 'Volvo', 'BMW', 'Mercedes' ].map()
you would provide empty array:
[].map()
which is now throwing the exc mentioned above.

regards,

Make license more explicit

Hi @trueadm,

I've just discovered inferno and I liked the approach that you have had for the VritualDom and to use t7 as template system, actually I've discovered Inferno through t7.

I've seen that you decided to use ISC license, as it's in package.json, however I think that it's very helpful to add a LICENSE file to the root of the project or add a LICENSE section in the README.

Thanks.

issues with createTemplate()

@edge @trueadm

This test spec:

describe('should properly render input download attribute', () => {
                    let template;

                    beforeEach(() => {
                        template = Inferno.createTemplate((t, val1) =>
                            <input download={ val1 }></input>
                        );
                        Inferno.render(Inferno.createFragment(false, template), container);
                    });

                    it('Initial render (creation)', () => {
                        expect(
                            container.innerHTML
                        ).to.equal(
                            '<input>'
                        );
                    });

                    it('Second render (update)', () => {
                        Inferno.render(Inferno.createFragment(true, template), container);
                        expect(
                            container.innerHTML
                        ).to.equal(
                            '<input download="">'
                        );
                    }); 
                });

will throw this error

AssertionError: expected '<input download="[object Object]">' to equal '<input>'[email protected]:14602:19

My question is. What is this [object Object], are the value not replaced or what? Where is this [object Object] coming from?

This works if I set the value manually.

if we try to update, we will get an element is null in the console.log. I commented that out for now.

inferno-server fails simple case

The following code snippet results in thrown exception:

'use strict';
var assert = require('assert');
var Inferno = require('inferno');
var InfernoServer = require('inferno-server');

var template = Inferno.createTemplate(function() {
  return Inferno.TemplateFactory.createElement('div');
});

assert(InfernoServer.renderToString(template()), '<div></div>');
TypeError: Cannot read property 'create' of undefined
    at Object.renderToString (node_modules/inferno/dist/inferno-server.js:21:25)

I'm using "[email protected]" and "[email protected]".

Any way to implement React's context?

I'm currently implementing a basic app using Inferno + Redux.
All of my components in the tree need to have access to my store.

I could implement a top-level StoreComponent class that imports the Store from which all of my components would inherit but I'd prefer using something more transparent.

Any plans or ideas on how I could implement React's context in Inferno?

Efficient Subtree Patching Proposal (for Inferno)?

I'm not sure if the following proposal makes sense for Inferno.
Maybe it's already supported. (See: Matt-Esch/virtual-dom#343 )

  • tl;dr: A component should be able to listen to data changes and then update itself.

I want to combine:

  • virtual-dom
  • level-js (=leveldb in the browser),
  • visual components as stream modules

Concept:

  • After initial rendering of the DOM, each "user interaction" or "server side update" updates the db
  • Visual components (=stream modules) track certain parts of the database for "data updates"
  • "data updates" stream into visual component which use it to render themselves and pipe their updated "vtree" into their DOM node
  var db = require('./level-tracker.js')(require('level-js')("data.db"))
  var engine$ = require('./inferno-engine.js') || require('./virtual-dom-engine.js')
  var componentStream = require('./component-stream.js')


  function ListComponent (db) { // "db" can also be "memdb" and holds all "state"
    var state$ = db.trackable()
    state$.track('autocomplete/')
    state$.track('help/')

    var TMP = { 'autocomplete/': [ ], 'help/': "" }
    var list$ = componentStream(function (chunk, encoding, next) {
      TMP[chunk.key].push(chunk.value)
      this.push(h('div', [
        h('li', TMP['autocomplete/'].map(function (item) {
          h('ul', item)
        })),
        h('div', TMP['help/'])
    )})

    return state$.pipe(list$)
  }

  var list$ = ListComponent(db)
  list$.pipe(engine$)

I tried to hack together something using vThunks and vHooks, see:
http://requirebin.com/?gist=003ebd0d11a7f248c210

I haven't read a lot about inferno yet, but saw the discussion about it's awesome performance.

Question:
Do you have any suggestions how to port that to inferno (if possible)?

Strange bug with computed children

I'm playing with Inferno and it's really fast. Great work!
I encountered a strange problem with component updates. I don't know how to setup jsfiddle with babel-plugin-inferno and jsx so I'm going to paste here the code to repreduce the bug.

import Inferno from 'inferno';
import {Component} from 'inferno-component';
import InfernoDOM from 'inferno-dom';

var id = 0;

class Value extends Component {

    constructor(props){
        super(props);
        this.id = ++id;
    }

    render() {
        //console.log(this.id);
        return <div>{this.props.value}</div>
    }
}

class Repeater extends Component {

    render() {

        //this doesn't work - only the last value is updated

        var children = [];
        for (var i = 0; i < 3; i++)
            children.push(<Value key={i} value={this.props.value}/>);

        return <div>
            {children}
        </div>

        //this works - all values are updated

        //return <div>
        //    <Value value={this.props.value}/>
        //    <Value value={this.props.value}/>
        //    <Value value={this.props.value}/>
        //</div>
    }

function render(value) {
    return InfernoDOM.render(<Repeater value={value}/>, document.getElementById('root'));
}

var value = 0;

setInterval(function() {
    render(++value);
}, 100)

Basically it's a counter repeated three times. For some reason only the last instance is updated.
If you uncomment console.log statement in render method, you'll see that it reports id: 3 all the time, which is very strange :) Any explanation would be very welcome.

Adding listitems does nothing

This is related to bug #88, but instead removing, adding more listitems.

class ChangeChildrenCount extends Component {
    constructor(props) {
        super(props);

        this.state = {
            list: ['1']
        };

        // Bindings
        this.handleClick = this.handleClick.bind(this);
    }

    handleClick() {
        this.setState({
            list: ['1',  '2', '3', '4']
        });
    }

    render() {
        return (
            <div>
                <button onClick={this.handleClick}>1</button>
                {this.state.list.map(function (x, i) {
                    return <div>{i}</div>
                })}
            </div>
        );
    }
}


InfernoDOM.render(<ChangeChildrenCount />, document.body);

expected result: at the beginning there is single item: ( 0 ), then after clicking button there are four (0,1,2,3)

Removing listitems causes exception

When removing listitems in state list it causes exception:

Uncaught NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.

class ChangeChildrenCount extends Component {
    constructor(props) {
        super(props);

        this.state = {
            list: ['1', '2', '3', '4']
        };

        // Bindings
        this.handleClick = this.handleClick.bind(this);
    }

    handleClick() {
        this.setState({
            list: ['1']
        });
    }

    render() {
        return (
            <div>
                <button onClick={this.handleClick}>1</button>
                {this.state.list.map(function (x, i) {
                    return <div>{i}</div>
                })}
            </div>
        );
    }
}
InfernoDOM.render(<ChangeChildrenCount />, document.body);

Clicking button (changing state to smaller list) causes the exception.

Expected result there should be four elements (0,1,2,3) at the beginning and when clicking button there should be one element (0).

Component Life Cycle Events not all firing?

Everything is rendering fine, so this is more me trying to explore/understand the Inferno render process.

I am not sure if I've done something wrong here but I am only getting componentWillMount and componentWillUnmount events on my components. Here is a link to the repo I am working on: https://github.com/andyrj/inferno-redux-starter-kit

I have refactored my components until they could almost all be pure functions instead of extending Inferno.Component but I wanted to test out shouldComponentUpdate functionality like React's pure render mixin, since I'm using redux I can short circuit with simple === checks on props.

Am I doing something wrong in how I call render on this project or is the current dev branch only firing off will mount and will unmount, or perhaps this is expected behavior? Is this because of the simple nature of my components and not using this.state in my component? Are the t7 templates smart enough to already see I have dumb components and do this type of optimization and which is why I don't see a call to shouldComponentUpdate?

[Q] License

One of the issues my employers always have with React is the license issue and Facebook seems unwilling to go back to Apache. To date, I've used open source virtual-dom libraries to build apps where React would have worked.

Inferno looks like a great alternative (and possibly a better one) but you have a default "ISC" mentioned in the package.json and no license file. Is your intention to open source this?

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.