silvio v1.0.0
Acey - A React State Manager.
Acey is an innovative Global State Manager for React Apps. π‘
It allows you to encapsulate your states inside Model class. Then you can gather the methods you need to treat, access, format, and organize their state. π±
You can :
- Update and access your Modelβs state wherever you want in your App without any binding. π
- Connect conveniently any Model with any component, so they re-render when the Modelβs state changes. π
It implements many useful built-in features to make your life easier, building, and maintaining your app architecture. π οΈ
Acey helps you to keep your code organized, maintainable, and easy to scale. π±
Quick start
import React from 'react';
import { Model, useAcey } from 'acey'
class CounterModel extends Model {
constructor(initialState: any, options: any){
super(initialState, options)
}
get = () => this.state.counter
}
/* A connected Model re-render the components they are bound with
when their state change. */
const Counter = new CounterModel({counter: 0}, {connected: true, key: 'counter'})
const App = () => {
/* Bind the Counter Model with component. */
useAcey([ Counter ])
return (
<div>
<button onClick={ () => Counter.setState({counter: Counter.get() - 1}).save() }>decrement</button>
{Counter.get()}
<button onClick={ () => Counter.setState({counter: Counter.get() + 1}).save() }>increment</button>
</div>
);
/* i) `save()` re-render the components bound with the Model (if a change occured) */
}
export default App;
Get started
Installation
yarn add acey
To start the Acey engine you need to declare the configuration as done at the root of your application. Here's how according to your environment:
On ReactJS
import { config } from 'acey' //HERE
config.done() //HERE
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
On React-Native
React-native is not using the same local store engine as web does, so you need to set it up manually at the root of your application:
import AsyncStorage from '@react-native-community/async-storage'
import { config } from 'acey'
config.setStoreEngine(AsyncStorage)
config.done()
make sure you already installed and linked async-storage.
yarn add @react-native-community/async-storage
On NextJS
Refer to the Wrapper doc β¬οΈ
π― Next Acey wrapper documentation
Examples
class CounterApp extends React.Component {
render = () => { return ( <> decrement {Counter.get()} increment </> ) } }
export default connect( Counter )(CounterApp)
</details>
<details><summary>Nesting Models in Model</summary>
```js
import { Model } from 'acey'
import TweetCollection from '../collections/tweetlist'
const DEFAULT_STATE = {
id: '',
username: '',
device: {
brand: '',
model: '',
version: 0
},
tweet_list: []
}
class Device extends Model {
constructor(initialState, options){
super(initialState, options)
}
//getters
brand = () => this.state.brand
model = () => this.state.model
version = () => this.state.version
}
class User extends Model {
constructor(initialState = DEFAULT_STATE, options){
super(initialState, options)
const { device, tweet_list } = initialState
this.setState({
device: new Device(device, this.__childOptions),
tweet_list: new TweetCollection(tweet_list, this.__childOptions)
})
/*
__childOptions allow you to in some way connect Device and TweetCollection to the store,
while binding them to User.
*/
}
//getters
device = () => this.state.device //return the instanced Device Model
tweetList = () => this.state.tweet_list //return the instanced Tweet Collection
ID = () => this.state.id
username = () => this.state.username
//actions
updateUsername = (username) => this.setState({ username }).save()
}
export default User
import { Model, Collection, useAcey } from 'acey'
const Tweet extends Model {
constructor(initialState = { content: '' , id: '' } , options){ super(initialState, options) }
//getters content = () => this.state.content ID = () => this.state.id }
class TweetList extends Collection {
constructor(initialState = [], options){ super(initialState, Tweet, options) }
filterByWorld = (word) => new TweetList(this.filter(o => o.content.indexOf(word) != -1), this.childOptions) sortByContentLength = () => new TodoCollection(this.orderBy((o) => o.content.length, 'asc'), this.childOptions) }
const DEFAULT_TWEET_LIST = { content: 'this is a casual tweet', id: 'ID_1' }, { content: 'This is a frequent tweet, id: 'ID_2' }
const TweetList = new TodoCollection(DEFAULT_TWEET_LIST, {connected: true, key: 'todolist'})
const Tweets = () => {
useAcey( TweetList )
return ( <> {Tweetlist.filterByWorld('casual').sortByContentLength().map((tweet, index) => { return })} </> ) }
export default Tweets
</details>
<br />
# Tutorials
## ReactJS
<details><summary>Counter App (Single file)</summary>
### πΊ [Youtube](https://www.youtube.com/watch?v=dFCCcDKUi80) - π± [Github project](https://github.com/Fantasim/acey/tree/master/example/reactjs/counter) - π [Live demo](https://codesandbox.io/s/github/Fantasim/acey/tree/master/example/reactjs/counter)
```ts
import React from 'react';
import { Model, config, useAcey } from 'acey'
/* Set the Acey configuration done, once, at the entry point of the project. */
config.done()
/* STEP 1: Let's create a model to handle the counter state */
class CounterModel extends Model {
constructor(initialState: any, options: any){
super(initialState, options)
}
/* STEP 2: Add a getter for the counter number */
get = () => this.state.counter
/*
STEP 3: Add incrementer/decrementer actions to update the counter's state
_________________________________________________________________________
setState: works like setState in React Class Components,
it updates the current state of the Model
save: dispatch the new state to the store and re-render
all the components bound with the Model
localStore: Store the Model's state in the localStore. (OPTION)
i) The default Model state at the next app load is going
to be the last state stored.
*/
increment = () => this.setState({counter: this.get() + 1}).save().localStore()
decrement = () => this.setState({counter: this.get() - 1}).save().localStore()
}
/*
STEP 4: Instance the Counter Model, and define it as `connected Model
with the Acey Store`
i) connected Model have the ability to re-render the components they are bound with
when their state change.
*/
const Counter = new CounterModel({counter: 0}, {connected: true, key: 'counter'})
const App = () => {
/* STEP 5: Bind the Counter Model with the App components. */
useAcey([ Counter ])
return (
<div>
<button onClick={Counter.decrement}>decrement</button>
{Counter.get()}
<button onClick={Counter.increment}>increment</button>
</div>
);
}
export default App;
NextJS
πΊΒ Youtube Β Β Β - Β Β Β π±Β Github project
import { Model, useAcey} from 'acey'
/* STEP 1: Let's create a model to handle the counter state */
class CounterModel extends Model {
constructor(initialState: any, options: any){
super(initialState, options)
}
/* STEP 2: Add a getter for the counter number */
get = () => this.state.counter
/*
STEP 3: Add incrementer/decrementer actions to update the counter's state
_________________________________________________________________________
setState: works like setState in React Class Components,
it updates the current state of the Model
save: dispatch the new state to the store and re-render
all the components bound with the Model
cookie: Store the Model's state in the cookies. (OPTION)
*/
increment = () => this.setState({counter: this.get() + 1}).save().cookie()
decrement = () => this.setState({counter: this.get() - 1}).save().cookie()
}
/*
STEP 4: Instance the Counter Model, and define it as `connected Model
with the Acey Store`
i) connected Model have the ability to re-render the components they are bound with
when their state change.
*/
const Counter = new CounterModel({counter: 0}, {connected: true, key: 'counter'})
export default function Home() {
/* STEP 5: Bind the Counter Model with the App components. */
useAcey([ Counter ])
return (
<div>
<button onClick={Counter.decrement}>decrement</button>
{Counter.get()}
<button onClick={Counter.increment}>increment</button>
</div>
)
}
/*
STEP 6: We set the counter state at 10 before being executed on the client.
i) It's going to be displayed at 10 on the client.
*/
Home.getInitialProps = ({ query }) => {
Counter.setState({counter: 10}).save()
}
React Native
import AsyncStorage from '@react-native-community/async-storage' import { config, Model, useAcey } from 'acey'
/ Step 1: - Define the store engine (for localStorage feature) as AsyncStore - Set the Acey configuration done. i) All of this once at the entry point of the project / config.setStoreEngine(AsyncStorage) config.done()
/ STEP 2: Let's create a model to handle the counter state / class CounterModel extends Model {
constructor(initialState: any, options: any){ super(initialState, options) }
/ STEP 3: Add a getter for the counter number / get = () => this.state.counter
/* STEP 4: Add incrementer/decrementer actions to update the counter's state
_________________________________________________________________________
setState: works like setState in React Class Components,
it updates the current state of the Model
save: dispatch the new state to the store and re-render
all the components bound with the Model
cookie: Store the Model's state in the cookies. (OPTION)
*/ increment = () => this.setState({counter: this.get() + 1}).save().cookie() decrement = () => this.setState({counter: this.get() - 1}).save().cookie() }
/*
STEP 5: Instance the Counter Model, and define it as connected Model
with the Acey Store
i) connected Model have the ability to re-render the components they are bound with when their state change. */ const Counter = new CounterModel({counter: 0}, {connected: true, key: 'counter'})
const App = () => {
/ STEP 6: Bind the Counter Model with the App components. / useAcey( Counter )
return (
<>
decrement
{Counter.get()}
increment
</>
);
};
export default App;
</details>
<details><summary>Micro blogging app</summary>
### πΊ [Youtube](https://www.youtube.com/watch?v=sW14y3DGLwc) - π± [Github project](https://github.com/Fantasim/acey/tree/master/example/react-native/microBlogging)
</details>
<br />
<br />
# Acey - Core.
## Documentation
### Table of contents
* [Model](#model)
* [Collection](#collection)
<br />
## Model
<p align="center" font-style="italic" >
<a>
<img alt="react-ascey" src="https://i.postimg.cc/ZnmTKcNB/model.png" width="100%">
</a>
</p>
#### prototype: `class Model` πΏ
A Model is a class built with an **object of data**.
It allows you to create all the methods you need related to a specific type of data like **utils**, **getters**, and **setters**.
You build a Model from an Object and options.
`super(data: Object, options: IOptions)`
#### Example of a Model:
```js
import { Model } from 'acey'
// A Model must always have a default state.
const DEFAULT_STATE = {
id: 0,
content: ''
}
class Todo extends Model {
constructor(initialState = DEFAULT_STATE, options){
super(initialState, options)
}
content = () => this.state.content
ID = () => this.state.id
}
export default Todo
Model's values:
Name Type Description state Object
return the current Model's data state options Object
return the Model's options __childOptions Object
return the connected methods of the current Model (as options). You can then pass this object as options for any instanced Model/Collection inside a connected Model, to make them connected as well without separating each other.
Model's methods:
Prototype Return value Description setState(array: Array) IAction
update the state by assigning the current state with the array parameter. hydrate(state: Array) IAction
fill the Model's state with the JS array state
passed in parameter.toPlain() Object
return the state to a plain javascript object. isCollection() boolean return true if the Model is a Collection. defaultState() Object return the state of data of the instanciation. fetchCookies() Object (Only if connected
option is set totrue
andkey
option ismanually set
withan unique string
) return the cookies stored by the Model.clearCookies() any (Only if connected
option is set totrue
andkey
option ismanually set
withan unique string
) remove the cookies stored by the Model.
IOption (or Model's options):
Name Type Default Description key string
"" Model's unique key, if not set
Acey will set it automatically for you.connected bool
false If set to true
the Model is connected to the Acey Store, it will also re-render your component connected with it on changes.
IAction (or Model's actions):
Prototype Return value Description save() IAction
(Only if connected
option is set totrue
). Give the order to refresh the store with the new data when the function is called. It will then re-render all the components connected with the Model.cookie(expires = 365) IAction
(Only if connected
option is set totrue
andkey
option ismanually set
withan unique string
). Transform the current data of the model to JSON and store it in the cookies.
Collection
prototype: class Collection extends Model
π³
A Collection is a Model that has for state an array of Models. (Example: a Todolist is a Collection of Todo Models.)
You build a Collection with : 1. An array of Models or Objects. 2. A non-instanced Model class that represents the Model of the elements in the array. 3. Options
Example of a Collection:
import { Collection } from 'acey'
import Todo from './todo'
class Todolist extends Collection {
constructor(initialState = [], options){
super(initialState, Todo, options)
}
//method example
sortByID = () => {
/*
- orderBy sort the list by data and return an array
of model.
- We return a fresh instance of a collection with the array
returned by orderBy
- __childOptions passes the connected method of the current Collection to the
the new instanced one. This way if any data is updated in the fresh instance,
it will be in the state of the current Collection.
*/
return new TodoCollection( this.orderBy(['id'], ['desc]), this.__childOptions)
}
}
export default Todolist
Collection's values:
Name Type Description state Object
return the current Collection's data state options Object
return the Collection's options __childOptions Object
return the connected methods of the current Collection (as options). You can then pass this object as options for any instanced Model inside a connected Collection, to make them connected as well without separating each other.
Collection's methods:
Prototype Return value Description count() number
Return the length of the Collection toListClass(elem: any[]) Model[]
Transform an object array into an instanced Model array push(v: Object Model) IAction
Add an element in the array update(v: Object Model, index: number) IAction
Update the element at index with the Model passed in parameter pop() IAction
Remove the last element shift() IAction
Remove the first element map(callback: (v: Model, index: number) => any) any
creates a new array with the results of calling a function for every array element (same than javascript map on arrays) reduce(callback: (accumulator: any, currentValue: any) => any, initialAccumulator: any) any
Reduces Collection to a value which is the accumulated result of running each element in collection, where each successive invocation is supplied the return value of the previous. If initialAccumulator is not given, the first Model of Collection is used as the initial value. orderBy(iteratees: any[], orders: any[]) Model[]
Return a sorted array of instanced Model upon the parameters passed filter(predicate: any) Model[]
Pick up a list of node matching the predicate find(predicate: any) Model | undefined
Find the first node matching the predicate findIndex(predicate: any) number
Return the index of the first node matching the predicate deleteAll(predicate: any) IAction
Delete all the nodes matching the predicate delete(v: Object Model) IAction
Delete the model passed in parameter if in the list. deleteIndex(index: number) IAction
Remove an element at index. indexOf(v: Object Model) number
Get the index of a node in the list. nodeAt(index: number) Model
Get the node at index in the list, undefined it not found. newNode(v: Object) Model
Return fresh instanced Model with the value sent in parameter hydrate(state: Object) IAction
fill the Model's state with the JS object state
passed in parameter.toPlain() Object
return the state of model as a plain javascript array. isCollection() boolean return true if the Model is a Collection. defaultState() Object return the state of data of the instanciation. fetchCookies() Object (Only if connected
option is set totrue
andkey
option ismanually set
withan unique string
) return the cookies stored by the Collection.clearCookies() any (Only if connected
option is set totrue
andkey
option ismanually set
withan unique string
) remove the cookies stored by the Collection.
IOption (or Collection's options):
Name Type Default Description key string
"" Model's unique key, if not set
Acey will set it automatically for you.connected bool
false If set to true
the Collection is connected to the Acey Store, it will also re-render your component connected with it on changes.
IAction (or Collection's actions):
Prototype Return value Description save() IAction
(Only if connected
option is set totrue
). Give the order to refresh the store with the new data when the function is called. It will then re-render all the components connected with the Collection.cookie(expires = 365) IAction
(Only if connected
option is set totrue
andkey
option ismanually set
withan unique string
). Transform the current data of the model to JSON and store it in the cookies.
Questions / Answers
Ask your questions in issue request...
4 years ago