yarn add react @poa/core
Poa — opinionated React framework.
What does opinionated mean?
- Framework will not suit for every case
- It has unique vision on how things should work
- #0CJS
- We hame some opinions on how things should work ;)
Since it is a framework, not a library, it bundled with all needed stuff to create small, medium and large projects.
We are assuming that you're using some official starter kit (though it should work with custom setup as well).
TLDR; https://codesandbox.io/s/yzzqk57lx
Initialize the Poa:
// index.js
import { boot } from '@poa/core';
boot();
It should open a dummy Poa screen.
Let's add React component first;
// src/pages/home.page.js
import React from 'react';
import { Component } from '@poa/core';
class HomePage extends React.Component {
render() {
return <p>Heelo!</p>;
}
}
export default Component(/* additional config */)(HomePage);
Then let's declare our routing!
// src/routes.js
import HomePage from './pages/home.page';
export const routes = [
{
path: '',
redirectTo: '/home'
},
{
path: '/home',
component: HomePage
}
];
Note, that we are using redirectTo
functionality from mobx-little-route. (TODO: describe the Poa routing API)
The last thing, add your routes to Poa boot
import { boot } from '@poa/core';
import { routes } from './routes';
boot({ router: { routes } });
Okay. Now on screen you will see you HomePage
component and /home
route;
Let's define our locales first
// src/locales/en.js
export const en = {
helloScreen: {
helloMessage: 'Jumbo jumbo!'
}
};
Then let's update our component
// src/pages/home.page.js
import React from 'react';
import { Component } from '@poa/core';
class HomePage extends React.Component {
t; // injected by Poa
render() {
return <p>{this.t('helloMessage')}</p>;
}
}
export default Component({ namespaces: ['homeScreen'] })(HomePage);
Notice that we using this.t(...)
. Poa gently inject translation function to your components if you use enhancer Component(config)(ReactComponent)
Also, we pass namespaces
in the Component({ namespaces: ['homeScreen'] })(HomePage)
poa component enhancer.
After this, pass localization into i18n config.
// src/index.js
import { boot } from '@poa/core';
import { routes } from './routes';
import { en } from './locales/en';
boot({
router: { routes },
i18n: { lng: 'en', resources: { en } }
});
Notice, that in i18n you can pass any additional config that is supported by i18next. We just wrap it API (TODO: describe the Poa internationalization API)
Let's add moar complexity. Add initial state of your application.
// src/stores/hello.state.js
export const initialState = {
dunnoState: false
};
Add some actions!
// src/stores/hello.actions.js
import { createAction, addMutator, addSideEffects } from '@poa/state';
// action that can be dispatched with just functino invoking
export const dunnoAction = createAction('dunno', newValue => ({ newValue }));
// the only place where data can be changed
addMutator(dunnoAction, (payload, { store }) => {
store.dunnoState = payload.newValue;
});
// any side-effects
addSideEffects(dunnoAction, async (payload, { store }) => {
console.log('new store', store);
});
Add actions to our component
import React from 'react';
import { Component } from '@poa/core';
class HomePage extends React.Component {
t; // injected by Poa
store; // injected by Poa
actions; // injected by Poa
handleClick = () => {
this.actions.dunnoAction(true);
};
render() {
return (
<p>
{this.t('helloMessage')}
<br />
{this.store.dunnoState ? 'Poa!' : null}
<button onClick={this.handleClick}>Do jumbo!</button>
</p>
);
}
}
export default Component({ namespaces: ['homeScreen'] })(HomePage);
Notice that store
and actions
are injected gently by Poa.
And the last step, update our boot configuration!
import { boot } from '@poa/core';
import { routes } from './routes';
import { en } from './locales/en';
import { initialState } from './store/hello.state';
import * as actions from './store/hello.actions';
boot({
// routing configuration
router: { routes },
// localization configuration
i18n: {
lng: 'en',
resources: { en }
},
// state managment configuration
state: {
initial: initialState,
actions: actions
}
});
}
Now, when you click on "Do jumbo!" it should dispatch the action, than this action will go throught mutator and after that, side affects are triggered. After that your component will re-render automatically!
In Poa we use custom version of satcheljs. But for core principles you can check (https://github.com/Microsoft/satcheljs)[https://github.com/Microsoft/satcheljs]
// TODO: describe Poa internal state management
You can pass additional env
can, and it will be injected to your components and side effects
// src/index.js
import { boot } from '@poa/core';
import { routes } from './routes';
import { en } from './locales/en';
import { initialState } from './store/hello.state';
import * as actions from './store/hello.actions';
boot({
router: { routes },
i18n: {
lng: 'en',
resources: { en }
},
state: {
initial: initialState,
actions: actions
},
// environment confugiration
env: {
http: fetch.bind(window)
}
});
}
So now, you can do the following
// src/stores/hello.actions.js
import { createAction, addMutator, addSideEffects } from '@poa/state';
export const dunnoAction = createAction('dunno', newValue => ({ newValue }));
addMutator(dunnoAction, (payload, { store }) => {
store.dunnoState = payload.newValue;
});
addSideEffects(dunnoAction, async (payload, { store, env }) => {
// here is your injected env!
await env.http('/users');
});
If you need to invoke another actions from sideEffects (you definetly need it), they are injected!
// src/stores/hello.actions.js
import { createAction, addMutator, addSideEffects } from '@poa/state';
export const dunnoAction = createAction('dunno', newValue => ({ newValue }));
export const dunnoActionSuccess = createAction('dunnoSuccess', () => ({}));
addMutator(dunnoAction, (payload, { store }) => {
store.dunnoState = payload.newValue;
});
addSideEffects(dunnoAction, async (payload, { store, env, actions }) => {
await env.http('/users');
// here is your actions!
actions.dunnoActionSuccess();
});
This is needed, when you need to access your actions from another module to avoid circular imports.
To avoid doing like
addSideEffects(dunnoAction, async (payload, { store, env, actions }) => {
try {
// start action sets isLoading=true
actions.dunnoActionStart();
// fetch data
await env.http('/users');
// success action sets isLoadgin=false
actions.dunnoActionSuccess();
} catch (e) {
// failed action isLoadgin=false isError=true
actions.dunnoActionFailed();
}
});
You can await
on action! We make sure that all side affects are resolved.
...
async handleClick() {
this.setState({ isLoading: true });
this.actions.dunnoAction();
this.setState({ isLoading: false });
}
...
Under construction.
Runtime:
- react
- react-dom
- mobx
- mobx-react
- mobx-little-router
- mobx-little-router-react
- satcheljs
- i18next
Build:
- lerna
- microbundle
- parcel
Code:
- prettier
- i18next .use()
- export all mobx stuff
- HTTP Client
Future:
- CLI for project setup / create boilerplate code
- Isomorphic support
- React Native support