edata is the nested observable reactive EventEmitter with .value
getter/setter, lodash style path, and keep Observer Pattern in mind.
It roughly referenced Object.observe API, but instead using getter/setter to wrap object, lightweight than Proxy.
NPM
npm install --save edata
import edata from 'edata'
Below can give you a quick idea of edata:
var root = edata({a: {b: {c: {}}}})
// plain_object ---> edata
Complete example:
import edata from 'edata'
const root = edata({
age: 20,
firstName: 'Hello',
lastName: 'World',
address: {
city: 'Earth'
}
})
const callback = ({type, path}) => console.log(`--> ${type}: ${path}`)
root.watch(callback)
root.set('address.city', 'Moon') // LOG: --> update: ['address', 'city']
root.get('address.city').value // Moon
Plain object wrapped into edata:
edata = new EventEmitter(object)
so use edata.on
can watch changes, use edata.value
(getter/setter) to get nested edata.
root.value.firstName.value // get -> firstName
root.value.firstName.value = 'name' // set -> firstName
root.value.address.value.city.on('change', callback) // watch on 'change'
Can also use lodash style:
root.get('firstName').value // get: firstName
root.set('firstName', 'name') // set: firstName
root.get('address.city').on('change', ...) // watch change
Proxy usage:
use .proxy()
to shorten the path:
const data = root.proxy()
data.address.city // Moon
data.__edata__ // get back the `root` edata
Any edata can watch
data changes inside(any level):
const onDataChange = ({data, type, path})=>{
console.log('value mutated:', path, type, data.unwrap())
}
const unwatch = root.watch('change', onDataChange)
root.set('address.city', 'Mars')
// [LOG] data mutated: [ 'address', 'city' ] add Mars
unwatch() // stop watch
root.get('address.city').value = 'Earth'
// nothing outout
Watch single data change
event:
root.get('firstName').on('change', ({path, type, data})=>{
console.log(path, type, 'to: ' + data)
})
root.set('firstName', 'Hi')
//[LOG] firstName update to: Hi
Note:
edata.on('change', callback)
has shortcut:edata.map(callback)
You can define data relations using setComputed
, as below:
const root = edata({
firstName: 'Hello',
lastName: 'World'
})
root.setComputed(
'fullName',
['firstName', 'lastName'],
([firstName, lastName]) => firstName + ' ' + lastName
)
assert.equal(root.unwrap('fullName'), 'Hello World')
root.set('firstName', 'Green')
assert.equal(root.unwrap('fullName'), 'Green World')
Since the edata objects nested as EventEmitter, you can flat this structure into plain object using edata.unwrap()
:
root.unwrap()
// flat root: {age: 20, firstName: 'Hello', lastName: 'World', address: {city: 'Earth'}}
root.unwrap('address')
// flat address: {city: 'Earth'}
root.unset('address')
// delete address
root.unwrap()
// {age: 20, firstName: 'Hello', lastName: 'World'}
Also exist edata.toJSON()
, so the JSON.stringify(edata)
just like
JSON.stringify(edata.unwrap({json:true}))
The pros is you can use edata.set()
instead of this.setState
:
import edata from 'edata'
const root = edata({user: {name: 'earth'}})
class App extends React.Component {
constructor(props){
super(props)
const {model} = this.props
this.state = model.unwrap()
// state: {name: 'earth'}
model.watch(()=>{
this.setState(model.unwrap())
})
}
render(){
const {model} = this.props
const name = model.unwrap('name')
return <div>
<h3>Name: {name}</h3>
<input
value={name}
onChange={e=>{model.set('name', e.target.value)}}
/>
</div>
}
}
ReactDOM.render(<App edata={root.cut('user')} />, app)
You can play with the demo here
See API Document