Repository: RoyalIcing/react-organism
Branch: master
Commit: 4879bc0a0066
Files: 42
Total size: 76.9 KB
Directory structure:
gitextract_z5_js_hy/
├── .gitignore
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── demo/
│ └── src/
│ ├── components/
│ │ ├── Calculator.js
│ │ ├── Counter.js
│ │ ├── FriendsList.js
│ │ ├── Items.js
│ │ ├── Notifications.js
│ │ ├── PhotosList.js
│ │ └── Row.js
│ ├── index.js
│ ├── organisms/
│ │ ├── Calculator.js
│ │ ├── Counter.js
│ │ ├── Counter2.js
│ │ ├── Counter3.js
│ │ ├── Counter4.js
│ │ ├── Items.js
│ │ ├── ItemsChoice.js
│ │ └── Social.js
│ └── state/
│ ├── counter.js
│ ├── friends.js
│ ├── photos.js
│ ├── placeholderAPI.js
│ └── selection.js
├── nwb.config.js
├── package.json
├── packages/
│ └── create-react-organism/
│ ├── .gitignore
│ ├── README.md
│ ├── bin/
│ │ └── create-react-organism.js
│ └── package.json
├── src/
│ ├── adjustArgs/
│ │ └── extractFromDOM.js
│ ├── index.d.ts
│ ├── index.js
│ ├── multi.js
│ └── nextFrame.js
├── tests/
│ ├── .eslintrc
│ ├── extractFromDOM-test.js
│ ├── index-test.js
│ └── multi-test.js
└── umd/
└── react-organism.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
/coverage
/demo/dist
/es
/lib
/node_modules
npm-debug.log*
================================================
FILE: .travis.yml
================================================
sudo: false
language: node_js
node_js:
- 4
- 6
- 7
- 8
before_install:
- npm install codecov.io coveralls
after_success:
- cat ./coverage/lcov.info | ./node_modules/codecov.io/bin/codecov.io.js
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
branches:
only:
- master
================================================
FILE: CONTRIBUTING.md
================================================
## Prerequisites
[Node.js](http://nodejs.org/) >= v4 must be installed.
## Installation
- Running `npm install` in the components's root directory will install everything you need for development.
## Demo Development Server
- `npm start` will run a development server with the component's demo app at [http://localhost:3000](http://localhost:3000) with hot module reloading.
## Running Tests
- `npm test` will run the tests once.
- `npm run test:coverage` will run the tests and produce a coverage report in `coverage/`.
- `npm run test:watch` will run the tests on every change.
## Building
- `npm run build` will build the component for publishing to npm and also bundle the demo app.
- `npm run clean` will delete built resources.
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2017 Patrick Smith
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# React Organism
[![Travis][build-badge]][build]
[![npm package][npm-badge]][npm]
[![Coveralls][coveralls-badge]][coveralls]
**Dead simple React/Preact state management to bring pure components alive**
- Supports `async`/`await` and easy loading (e.g. `fetch()`)
- Reload when particular props change
- Animate using generator functions: just `yield` the new state for each frame
- Tiny: 1.69 KB gzipped (3.49 KB uncompressed)
- Embraces the existing functional `setState` while avoiding boilerplate (no writing `this.setState()` or `.bind` again)
- Easy to unit test
#### Table of contents
- [Installation](#installation)
- [Demos](#demos)
- [Usage](#usage)
- [Basic](#basic)
- [Using props](#using-props)
- [Async & promises](#async)
- [Handling events](#handling-events)
- [Animation](#animation)
- [Serialization: Local storage](#serialization-local-storage)
- [Separate and reuse state handlers](#separate-and-reuse-state-handlers)
- [Multicelled organisms](#multicelled-organisms)
- [API](#api)
- [`makeOrganism(PureComponent, StateFunctions, options)`](#makeorganismpurecomponent-statefunctions-options)
- [State functions](#state-functions)
- [Argument enhancers](#argument-enhancers)
- [Why instead of Redux?](#why-instead-of-redux)
## Installation
```
npm i react-organism --save
```
## Demos
- [Animated counter](https://codesandbox.io/s/2vx12v3qmn)
- [Dynamic loading with `import()`](https://codesandbox.io/s/X6mLEwG7W)
- [Live form error validation with Yup](https://codesandbox.io/s/4xQpKRRWx)
- [Multicelled component — using multiple states](https://codesandbox.io/s/Yv7j1xLqM)
- [Todo List](https://codesandbox.io/s/yME5Y3Yz)
- [Inputs, forms, animation, fetch](https://react-organism.now.sh) · [code](https://github.com/BurntCaramel/react-organism/tree/master/demo/src)
- [User Stories Maker](https://codesandbox.io/s/xkZ5ZONl)
- [React Cheat Sheet](https://react-cheat.now.sh/) · [code](https://github.com/BurntCaramel/react-cheat)
## Usage
### Basic
```js
// organisms/Counter.js
import makeOrganism from 'react-organism'
import Counter from './components/Counter'
export default makeOrganism(Counter, {
initial: () => ({ count: 0 }),
increment: () => ({ count }) => ({ count: count + 1 }),
decrement: () => ({ count }) => ({ count: count - 1 })
})
```
```js
// components/Counter.js
import React, { Component } from 'react'
export default function Counter({
count,
handlers: {
increment,
decrement
}
}) {
return (
{ count }
)
}
```
### Using props
The handlers can easily use props, which are always passed as the first argument
```js
// organisms/Counter.js
import makeOrganism from 'react-organism'
import Counter from './components/Counter'
export default makeOrganism(Counter, {
initial: ({ initialCount = 0 }) => ({ count: initialCount }),
increment: ({ stride = 1 }) => ({ count }) => ({ count: count + stride }),
decrement: ({ stride = 1 }) => ({ count }) => ({ count: count - stride })
})
// Render passing prop:
```
### Async
Asynchronous code to load from an API is easy:
```js
// components/Items.js
import React, { Component } from 'react'
export default function Items({
items,
collectionName,
handlers: {
load
}
}) {
return (
```
### Handling events
Handlers can easily accept arguments such as events.
```js
// components/Calculator.js
import React, { Component } from 'react'
export default function Calculator({
value,
handlers: {
changeValue,
double,
add3,
initial
}
}) {
return (
)
}
```
```js
// organisms/Calculator.js
import makeOrganism from 'react-organism'
import Calculator from '../components/Calculator'
export default makeOrganism(Calculator, {
initial: ({ initialValue = 0 }) => ({ value: initialValue }),
// Destructure event to get target
changeValue: (props, { target }) => ({ value }) => ({ value: parseInt(target.value, 10) }),
double: () => ({ value }) => ({ value: value * 2 }),
add3: () => ({ value }) => ({ value: value + 3 })
})
```
### Animation
```js
import makeOrganism from 'react-organism'
import Counter from '../components/Counter'
export default makeOrganism(Counter, {
initial: ({ initialCount = 0 }) => ({ count: initialCount }),
increment: function * ({ stride = 20 }) {
while (stride > 0) {
yield ({ count }) => ({ count: count + 1 })
stride -= 1
}
},
decrement: function * ({ stride = 20 }) {
while (stride > 0) {
yield ({ count }) => ({ count: count - 1 })
stride -= 1
}
}
})
```
### Automatically extract from `data-` attributes and ``
Example coming soon
### Serialization: Local storage
```js
// organisms/Counter.js
import makeOrganism from 'react-organism'
import Counter from '../components/Counter'
const localStorageKey = 'counter'
export default makeOrganism(Counter, {
initial: ({ initialCount = 0 }) => ({ count: initialCount }),
load: async (props, prevProps) => {
if (!prevProps) {
// Try commenting out:
/* throw (new Error('Oops!')) */
// Load previously stored state, if present
return await JSON.parse(localStorage.getItem(localStorageKey))
}
},
increment: ({ stride = 1 }) => ({ count }) => ({ count: count + stride }),
decrement: ({ stride = 1 }) => ({ count }) => ({ count: count - stride })
}, {
onChange(state) {
// When state changes, save in local storage
localStorage.setItem(localStorageKey, JSON.stringify(state))
}
})
```
### Separate and reuse state handlers
React Organism supports separating state handlers and the component into their own files. This means state handlers could be reused by multiple smart components.
Here’s an example of separating state:
```js
// state/counter.js
export const initial = () => ({
count: 0
})
export const increment = () => ({ count }) => ({ count: count + 1 })
export const decrement = () => ({ count }) => ({ count: count - 1 })
```
```js
// organisms/Counter.js
import makeOrganism from 'react-organism'
import Counter from './components/Counter'
import * as counterState from './state/counter'
export default makeOrganism(Counter, counterState)
```
```js
// App.js
import React from 'react'
import CounterOrganism from './organisms/Counter'
class App extends React.Component {
render() {
return (
)
}
}
```
### Multicelled Organisms
Example coming soon.
## API
### `makeOrganism(PureComponent, StateFunctions, options?)`
```js
import makeOrganism from 'react-organism'
```
Creates a smart component, rendering using React component `PureComponent`, and managing state using `StateFunctions`.
#### `PureComponent`
A React component, usually a pure functional component. This component is passed as its props:
- The props passed to the smart component, combined with
- The current state, combined with
- `handlers` which correspond to each function in `StateFunctions` and are ready to be passed to e.g. `onClick`, `onChange`, etc.
- `loadError?`: Error produced by the `load` handler
- `handlerError?`: Error produced by any other handler
#### `StateFunctions`
Object with functional handlers. See [state functions below](#state-functions).
Either pass a object directly with each function, or create a separate file with each handler function `export`ed out, and then bring in using `import * as StateFunctions from '...'`.
#### `options`
##### `adjustArgs?(args: array) => newArgs: array`
Used to enhance handlers. See [built-in handlers below](#argument-enhancers).
##### `onChange?(state)`
Called after the state has changed, making it ideal for saving the state somewhere (e.g. Local Storage).
### State functions
Your state is handled by a collection of functions. Each function is pure: they can only rely on the props and state passed to them. Functions return the new state, either immediately or asynchronously.
Each handler is passed the current props first, followed by the called arguments:
- `(props, event)`: most event handlers, e.g. `onClick`, `onChange`
- `(props, first, second)`: e.g. `handler(first, second)`
- `(props, ...args)`: get all arguments passed
- `(props)`: ignore any arguments
- `()`: ignore props and arguments
Handlers must return one of the following:
- An object with new state changes, a la React’s `setState(changes)`.
- A function accepting the previous state and current props, and returns the new state, a la React’s `setState((prevState, props) => changes)`.
- A promise resolving to any of the above (object / function), which will then be used to update the state. Uncaught errors are stored in state under the key `handlerError`. Alternatively, your handler can use the `async`/`await` syntax.
- An iterator, such as one made by using a [generator function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function%2A). Each object passed to `yield` may be one of the above (object / function / promise).
- An array of any of the above (object / function / promise / iterator).
- Or optionally, nothing.
There are some handlers for special tasks, specifically:
#### `initial(props) => object` (required)
Return initial state to start off with, a la React’s `initialState`. Passed props.
#### `load(props: object, prevProps: object?, { handlers: object }) => object | Promise