Migrating from v0.x to v1.x

We are still keeping majority of the API in v0.x backwards compatible with the new v1.x.

But using old API would give you deprecated warnings, and they can be eliminated by following the guide below.

app

Recommended equivalent methods for App instance, introduced in new API:

v0.x v1.x
app.getService(name) app.get(serviceName)
app.getFactory(name) app.get(factoryName)
app.getModel(name) app.get(modelName)
app.getStore() app.get('store')
app.getState$() app.get('store').getState$()
app.dispatch(action) app.get('store').dispatch(action)
app.render app.get('component')
app.getWidgets() app.getApps$()

Component

v0.x

import { createComponent } from 'frint';

const Root = createComponent({
  render() {
    return (
      <p>Hello World</p>
    );
  }
});

v1.x

Now we use React directly:

import React, { Component } from 'react';

class Root extends Component {
  render() {
    return (
      <p>Hello world</p>
    );
  }
}

Note: Remember that the lifecycle methods in Components need to follow React's API too now:

v0.x - Frint createComponent v1.x - React Component
beforeMount componentWillMount
afterMount componentDidMount
beforeUnmount componentWillUnmount

Root components

v0.x

import { createApp } from 'frint';

const App = createApp({
  name: 'MyApp',
  component: MyComponent
});

v1.x

Components are optional in v1.x:

import { createApp } from 'frint';

const App = createApp({
  name: 'MyApp',
  providers: [
    {
      name: 'component',
      useValue: MyRootComponent
    }
  ]
});

PropTypes

v0.x

import { PropTypes } from 'frint';

v1.x

In React v0.14:

import { PropTypes } from 'react';

To keep things compatible with React v15+:

import PropTypes from 'prop-types';

Store

v0.x

import { createApp, combineReducers } from 'frint';

const rootReducer = combineReducers({
  foo: fooReducer,
  bar: barReducer
});

const App = createApp({
  name: 'MyApp',
  reducer: rootReducer,
  initialState: {}
});

const app = new App();
const store = app.getStore();

v1.x

Stores are optional in v1.x:

import { createApp } from 'frint';
import { createStore, combineReducers } from 'frint-store';

const rootReducer = combineReducers({
  foo: fooReducer,
  bar: barReducer
});

const App = createApp({
  name: 'MyApp',
  providers: [
    {
      name: 'store',
      useFactory: function ({ app }) {
        const Store = createStore({
          reducer: rootReducer,
          initialState: {},
          thunkArgument: { app }, // for async actions
        });

        return new Store();
      },
      deps: ['app']
    }
  ]
});

const store = app.get('store');

Services

v0.x

import { createApp, createService } from 'frint';

const FooService = createService({
  getAppName() {
    return this.app.getName();
  }
});

const App = createApp({
  name: 'MyApp',
  services: {
    foo: FooService
  }
});

const app = new App();
const foo = app.getService('foo');

v1.x

import { createApp } from 'frint';

// just a regular ES6-compatible class
class FooService {
  constructor({ app }) {
    this.app = app;
  }

  getAppName() {
    return this.app.getName();
  }
}

const App = createApp({
  name: 'MyApp',
  providers: [
    {
      name: 'foo',
      useClass: FooService,
      cascade: true, // if you want Apps to access it
      deps: ['app'] // values are made available in constructor argument
    }
  ]
});

const app = new App();
const foo = app.get('foo');

Factories

v0.x

import { createApp, createFactory } from 'frint';

const BarFactory = createFactory({
  getAppName() {
    return this.app.getName();
  }
});

const App = createApp({
  name: 'MyApp',
  factories: {
    bar: BarFactory
  }
});

const app = new App();
const bar = app.getFactory('bar');

v1.x

import { createApp } from 'frint';

// just a regular ES6-compatible class
class BarFactory {
  constructor({ app }) {
    this.app = app;
  }

  getAppName() {
    return this.app.getName();
  }
}

const App = createApp({
  name: 'MyApp',
  providers: [
    {
      name: 'bar',
      useClass: BarFactory,
      cascade: true, // if you want Apps to access it
      scoped: true, // means Apps will get a fresh new scoped instance themselves
      deps: ['app']
    }
  ]
});

const app = new App();
const bar = app.get('bar');

Models

v0.x

import { createModel, createApp } from 'frint';

const BazModel = createModel({});

const App = createApp({
  name: 'MyApp',
  models: {
    baz: BazModel
  },
  modelAttributes: {
    baz: {
      key: 'value'
    }
  }
});

const app = new App();
const baz = app.getModel('baz');

v1.x

import { createModel, createApp } from 'frint';

const BazModel = createModel({});

const App = createApp({
  name: 'MyApp',
  providers: [
    {
      name: 'baz',
      useFactory: function () {
        return new BazModel({
          key: 'value'
        });
      },
      cascade: true
    }
  ]
});

const app = new App();
const baz = app.get('baz');

mapToProps

v0.x

// ./components/Root.js
import { mapToProps, createComponent } from 'frint';
import { Observable } from 'rxjs';

import { addTodo } from '../actions/todos';

const Root = createComponent({
  render() {
    // ...
  }
});

export default mapToProps({
  state(state) => ({
    todos: state.todos.records
  }),
  dispatch: {
    handleAddButton: addTodo
  },
  app(app) {
    return {
      appName: app.getName()
    }
  }),
  shared(sharedState) => ({
    counter: sharedState.SomeOtherAppName[reducerName].someKey
  }),
  services: {
    foo: 'foo'
  },
  factories: {
    bar: 'bar'
  },
  models: {
    baz: 'baz'
  },
  observe(app) {
    return Observable
      .interval(1000)
      .map(x => ({ interval: x }));
  }
})(Root);

v1.x

The usage of mapToProps can be replaced with observe, along with a helper function streamProps for keeping code even shorter:

// ./components/Root.js
import React from 'react';
import { Observable } from 'rxjs';
import { observe, streamProps } from 'frint-react';

import { addTodo } from '../actions/todos';

const Root = React.createClass({
  render() {
    // ...
  }
});

export default observe(function (app) {
  // We need to return a single Observable from this function.
  // Either manually for greater control, or using `streamProps`:

  return streamProps({}) // default props to start with
    // state
    .set(
      app.get('store').getState$(),
      state => ({ todos: state.todos.records })
    )

    // dispatch
    .setDispatch({
      handleAddButton: addTodo
    }, app.get('store'))

    // app
    .set('appName', app.getName())

    // shared state
    .set(
      app.getAppOnceAvailable$('SomeOtherAppName'),
      app => app.get('store').getState$(),
      state => ({ counter: state[reducerName].someKey })
    )

    // services
    .set('foo', app.get('foo'))

    // factories
    .set('bar', app.get('bar'))

    // models
    .set('baz', app.get('baz'))

    // observe
    .set(
      Observable.interval(1000),
      x => ({ interval: x })
    )

    // return final Observable
    .get$();
})(Root);