by Nicholas Boll
// components/article.js
const Heading = ({ text }) => {text}
;
const Content = ({ text }) => {text}
;
const Article = ({ heading, content }) => (
<Heading text={heading} />
<Content text={content} />
);
export default Article;
// containers/article.js
import { connect } from 'react-redux';
import Article from '../components/Article';
export default connect(state => state)(Article);
MorphDOM proves it isn't
What happens if the data we're passing isn't "Smart"? Or what if we need to touch something small on many similar components?
We need to load the right data at the right level
import {observer} from 'mobx-react';
const TodoView = observer(({todo}) => {todo.title})
props.children?
shouldComponentUpdate
import * as React from 'react'
import { isObservable } from 'mobx'
import { observer } from 'mobx-react'
export const mx = (Object.keys(React.DOM)
.reduce((tags, tag) => {
tags[tag] = createWrapper(tag);
return answer;
}, {}): any)
function createWrapper(tag) {
class ReactiveClass extends React.Component {
static displayName = `mx.${tag}`;
propKeys: string[];
constructor(props){
super(props);
this.propKeys = Object.keys(props);
}
render() {
const propValues = this.propKeys
.reduce((answer, key) => {
const value = this.props[key];
if(isObservableArray(value)){
answer[key] = value.peek();
}
else if(isObservable(value) && value.get){
answer[key] = value.get();
}
else {
answer[key] = value;
}
return answer;
}, {});
return React.createElement(tag, propValues);
}
}
return observer(ReactiveClass);
}
import * as React from 'react'
import { mx } from './utils/mxReact'
const Heading = ({ text }) => {text} ;
const Content = ({ text }) => {text} ;
const Article = ({ heading, content }) => (
<Heading text={heading} />
<Content text={content} />
);
export default Article;
store.subscribe()
computedProperty
computedProperty
// Take 1 or more observables, combine to to 1 result
const computedProperty = (...observables) =>
Rx.Observable
.combineLatest(...observables) // combine the last value of each observable
.distinctUntilChanged() // only emit a value if it is different from previous
.cache(1) // cache the last result if anyone asks...
const a$ = Rx.Observable.of(1)
const b$ = Rx.Observable.of(2)
const c$ = computedProperty(
a$,
b$,
(a, b) => a + b
)
c$.subscribe((c) => console.log(c)) // 3
getValue()
// get the last value to flow through a computed property
// synchronous resolution of a value
const getValue = (computedProperty) => {
let value
rxValue.subscribe((x:T) => value = x).unsubscribe()
return (value: any)
}
const a$ = Rx.Observable.of(1)
const b$ = Rx.Observable.of(2)
const c$ = computedProperty(
a$,
b$,
(a, b) => a + b
)
console.log(getValue(c$)) // 3
computedProperty
and getValue
together to extract values
const list = ({ scrollTop$, onScroll }) => {
// This only gets defined once because properties are immutable pipelines
// that don't change references from one render to the next. No breaking
// pure-render
const onWheel = (event) => (
onScroll(getValue(scrollTop$) + event.wheelDeltaY)
)
const style$ = computeProperty(
scrollTop$,
(scrollTop) => {
return { transform: `transform: translate3d(0px, ${scrollTop}px, 0px);` }
}
)
return (
<rx.div
class="scrollable-container"
onWheel={onWheel}
style={style$}
>
{ /* reactive children */ }
</rx.div>
)
}
Note: What I didn't show was the reactive children - it is a bit complicated
to get into now, children are a computedProperty
of observer
components. Ex: Rx.Observable.of([ ListItem1, ListItem2 ])
. We have
a helper that takes a projection component (Ex: ListItem) and an array of indexes
and returns an observable of an array of projected components. Similar to { items.map((item, index) => <ListItem key={index} data={item} />) }
shouldComponentUpdate