mobx-react
Advanced tools
Comparing version 3.4.0 to 3.5.0
# MobX-React Changelog | ||
### 3.5.0 | ||
* Introduced `inject("store1", "store2")(component)` as alternative syntax to inject stores. Should address #77, #70 | ||
* Introduced the `wrappedComponent` property on injected higher order components, addresses #70, #72 | ||
* Fixed #76: error when no stores are provided through context | ||
* Added typings for devTools related features (@benjamingr). | ||
* Added MobX specific propTypes (@mattruby) | ||
* Merged #44, fixes #73: don't re-render if component was somehow unmounted | ||
### 3.4.0 | ||
@@ -4,0 +13,0 @@ |
/** | ||
* Turns a React component or stateless render function into a reactive component. | ||
*/ | ||
import React = require('react'); | ||
import React = require("react"); | ||
export function observer<P>(clazz: React.StatelessComponent<P>): React.ClassicComponentClass<P>; | ||
export function observer<P>(renderFunction: (props: P) => React.ReactElement<any>): React.ClassicComponentClass<P>; | ||
export function observer<P>(renderFunction: (props: P) => React.ReactElement<P>): React.ClassicComponentClass<P>; | ||
export function observer<P>(clazz: React.ClassicComponentClass<P>): React.ClassicComponentClass<P>; | ||
export function observer<P>(clazz: React.ComponentClass<P>): React.ComponentClass<P>; | ||
export function observer<TFunction extends React.ComponentClass<any>>(target: TFunction): TFunction; // decorator signature | ||
export function observer<P, TFunction extends React.ComponentClass<P>>(target: TFunction): TFunction; // decorator signature | ||
@@ -17,6 +17,42 @@ // with stores | ||
export function observer<P>(stores: string[], clazz: React.ComponentClass<P>): React.ComponentClass<P>; | ||
export function observer(stores: string[]): <TFunction extends React.ComponentClass<any>>(target: TFunction) => TFunction; // decorator signature | ||
export function observer<P>(stores: string[]): <TFunction extends React.ComponentClass<P>>(target: TFunction) => TFunction; // decorator signature | ||
// inject | ||
export function inject<P>(...stores: string[]): <TFunction extends React.ComponentClass<P>>(target: TFunction) => TFunction; // decorator signature | ||
export function inject<T, P>(storesToProps : (stores: any, nextProps: P, context:any) => T): <TFunction extends React.ComponentClass<T | P>>(target: TFunction) => TFunction; // decorator | ||
export class Provider extends React.Component<any, {}> { | ||
} | ||
} | ||
/** | ||
* Enable dev tool support, makes sure that renderReport emits events. | ||
*/ | ||
export function trackComponents():void; | ||
export const renderReporter: RenderReporter; | ||
export interface RenderReporter { | ||
on(handler: (data: IRenderEvent) => void): void; | ||
} | ||
export interface IRenderEvent { | ||
event: "render" | "destroy"; | ||
renderTime?: number; | ||
totalTime?: number; | ||
component: React.ReactElement<any>; // Component instance | ||
node: any; // DOMNode | ||
} | ||
/** | ||
* WeakMap DOMNode -> Component instance | ||
*/ | ||
export const componentByNodeRegistery: any; | ||
export const propTypes: { | ||
observableArray: React.Requireable<any>; | ||
observableMap: React.Requireable<any>; | ||
observableObject: React.Requireable<any>; | ||
arrayOrObservableArray: React.Requireable<any>; | ||
objectOrObservableObject: React.Requireable<any>; | ||
} |
@@ -102,3 +102,4 @@ (function() { | ||
self.componentWillReact(); | ||
React.Component.prototype.forceUpdate.call(self) | ||
if (self.__$mobxMounted) | ||
React.Component.prototype.forceUpdate.call(self) | ||
} | ||
@@ -129,2 +130,3 @@ }); | ||
this.render.$mobx && this.render.$mobx.dispose(); | ||
this.__$mobxMounted = false; | ||
if (isDevtoolsEnabled) { | ||
@@ -144,2 +146,3 @@ var node = findDOMNode(this); | ||
componentDidMount: function() { | ||
this.__$mobxMounted = true; | ||
if (isDevtoolsEnabled) | ||
@@ -155,6 +158,7 @@ reportRendering(this); | ||
shouldComponentUpdate: function(nextProps, nextState) { | ||
// TODO: if context changed, return true.., see #18 | ||
// if props or state did change, but a render was scheduled already, no additional render needs to be scheduled | ||
if (this.render.$mobx && this.render.$mobx.isScheduled() === true) | ||
return false; | ||
// update on any state changes (as is the default) | ||
@@ -201,3 +205,4 @@ if (this.state !== nextState) | ||
} else { | ||
return createStoreInjector(arg1, observer(arg2)); | ||
// TODO: deprecate this invocation style | ||
return inject.apply(null, arg1)(observer(arg2)); | ||
} | ||
@@ -285,3 +290,3 @@ } | ||
*/ | ||
function createStoreInjector(stores, component) { | ||
function createStoreInjector(grabStoresFn, component) { | ||
var Injector = React.createClass({ | ||
@@ -293,14 +298,9 @@ displayName: "MobXStoreInjector", | ||
newProps[key] = this.props[key]; | ||
var baseStores = this.context.mobxStores; | ||
stores.forEach(function(storeName) { | ||
if (storeName in newProps) // prefer props over stores | ||
return; | ||
if (!(storeName in baseStores)) | ||
throw new Error("MobX observer: Store '" + storeName + "' is not available! Make sure it is provided by some Provider"); | ||
newProps[storeName] = baseStores[storeName]; | ||
}, this); | ||
newProps = grabStoresFn(this.context.mobxStores || {}, newProps, this.context); | ||
return React.createElement(component, newProps); | ||
} | ||
// TODO: should have shouldComponentUpdate? | ||
}); | ||
Injector.contextTypes = { mobxStores: PropTypes.object }; | ||
Injector.wrappedComponent = component; | ||
return Injector; | ||
@@ -310,2 +310,66 @@ } | ||
/** | ||
* higher order component that injects stores to a child. | ||
* takes either a varargs list of strings, which are stores read from the context, | ||
* or a function that manually maps the available stores from the context to props: | ||
* storesToProps(mobxStores, props, context) => newProps | ||
*/ | ||
function inject(/* fn(stores, nextProps) or ...storeNames */) { | ||
var grabStoresFn; | ||
if (typeof arguments[0] === "function") { | ||
grabStoresFn = arguments[0]; | ||
} else { | ||
var storesNames = []; | ||
for (var i = 0; i < arguments.length; i++) | ||
storesNames[i] = arguments[i]; | ||
grabStoresFn = grabStoresByName(storesNames); | ||
} | ||
return function(componentClass) { | ||
return createStoreInjector(grabStoresFn, componentClass); | ||
}; | ||
} | ||
function grabStoresByName(storeNames) { | ||
return function(baseStores, nextProps) { | ||
storeNames.forEach(function(storeName) { | ||
if (storeName in nextProps) // prefer props over stores | ||
return; | ||
if (!(storeName in baseStores)) | ||
throw new Error("MobX observer: Store '" + storeName + "' is not available! Make sure it is provided by some Provider"); | ||
nextProps[storeName] = baseStores[storeName]; | ||
}); | ||
return nextProps; | ||
} | ||
} | ||
/** | ||
* PropTypes | ||
*/ | ||
function observableTypeChecker (type) { | ||
return function(props, propName, componentName) { | ||
if (!mobx['isObservable' + type](props[propName])) { | ||
return new Error( | ||
'Invalid prop `' + propName + '` supplied to' + | ||
' `' + componentName + '`. Expected a mobx observable ' + type + '. Validation failed.' | ||
); | ||
} | ||
}; | ||
} | ||
// oneOfType is used for simple isRequired chaining | ||
var propTypes = { | ||
observableArray: React.PropTypes.oneOfType([observableTypeChecker('Array')]), | ||
observableMap: React.PropTypes.oneOfType([observableTypeChecker('Map')]), | ||
observableObject: React.PropTypes.oneOfType([observableTypeChecker('Object')]), | ||
arrayOrObservableArray: React.PropTypes.oneOfType([ | ||
React.PropTypes.array, | ||
observableTypeChecker('Array') | ||
]), | ||
objectOrObservableObject: React.PropTypes.oneOfType([ | ||
React.PropTypes.object, | ||
observableTypeChecker('Object') | ||
]) | ||
}; | ||
/** | ||
* Export | ||
@@ -316,2 +380,4 @@ */ | ||
Provider: Provider, | ||
inject: inject, | ||
propTypes: propTypes, | ||
reactiveComponent: function() { | ||
@@ -318,0 +384,0 @@ console.warn("[mobx-react] `reactiveComponent` has been renamed to `observer` and will be removed in 1.1."); |
/** | ||
* Turns a React component or stateless render function into a reactive component. | ||
*/ | ||
import React = require('react'); | ||
import React = require("react"); | ||
export function observer<P>(clazz: React.StatelessComponent<P>): React.ClassicComponentClass<P>; | ||
export function observer<P>(renderFunction: (props: P) => React.ReactElement<any>): React.ClassicComponentClass<P>; | ||
export function observer<P>(renderFunction: (props: P) => React.ReactElement<P>): React.ClassicComponentClass<P>; | ||
export function observer<P>(clazz: React.ClassicComponentClass<P>): React.ClassicComponentClass<P>; | ||
export function observer<P>(clazz: React.ComponentClass<P>): React.ComponentClass<P>; | ||
export function observer<TFunction extends React.ComponentClass<any>>(target: TFunction): TFunction; // decorator signature | ||
export function observer<P, TFunction extends React.ComponentClass<P>>(target: TFunction): TFunction; // decorator signature | ||
@@ -17,6 +17,42 @@ // with stores | ||
export function observer<P>(stores: string[], clazz: React.ComponentClass<P>): React.ComponentClass<P>; | ||
export function observer(stores: string[]): <TFunction extends React.ComponentClass<any>>(target: TFunction) => TFunction; // decorator signature | ||
export function observer<P>(stores: string[]): <TFunction extends React.ComponentClass<P>>(target: TFunction) => TFunction; // decorator signature | ||
// inject | ||
export function inject<P>(...stores: string[]): <TFunction extends React.ComponentClass<P>>(target: TFunction) => TFunction; // decorator signature | ||
export function inject<T, P>(storesToProps : (stores: any, nextProps: P, context:any) => T): <TFunction extends React.ComponentClass<T | P>>(target: TFunction) => TFunction; // decorator | ||
export class Provider extends React.Component<any, {}> { | ||
} | ||
} | ||
/** | ||
* Enable dev tool support, makes sure that renderReport emits events. | ||
*/ | ||
export function trackComponents():void; | ||
export const renderReporter: RenderReporter; | ||
export interface RenderReporter { | ||
on(handler: (data: IRenderEvent) => void): void; | ||
} | ||
export interface IRenderEvent { | ||
event: "render" | "destroy"; | ||
renderTime?: number; | ||
totalTime?: number; | ||
component: React.ReactElement<any>; // Component instance | ||
node: any; // DOMNode | ||
} | ||
/** | ||
* WeakMap DOMNode -> Component instance | ||
*/ | ||
export const componentByNodeRegistery: any; | ||
export const propTypes: { | ||
observableArray: React.Requireable<any>; | ||
observableMap: React.Requireable<any>; | ||
observableObject: React.Requireable<any>; | ||
arrayOrObservableArray: React.Requireable<any>; | ||
objectOrObservableObject: React.Requireable<any>; | ||
} |
90
index.js
@@ -102,3 +102,4 @@ (function() { | ||
self.componentWillReact(); | ||
React.Component.prototype.forceUpdate.call(self) | ||
if (self.__$mobxMounted) | ||
React.Component.prototype.forceUpdate.call(self) | ||
} | ||
@@ -129,2 +130,3 @@ }); | ||
this.render.$mobx && this.render.$mobx.dispose(); | ||
this.__$mobxMounted = false; | ||
if (isDevtoolsEnabled) { | ||
@@ -144,2 +146,3 @@ var node = findDOMNode(this); | ||
componentDidMount: function() { | ||
this.__$mobxMounted = true; | ||
if (isDevtoolsEnabled) | ||
@@ -155,6 +158,7 @@ reportRendering(this); | ||
shouldComponentUpdate: function(nextProps, nextState) { | ||
// TODO: if context changed, return true.., see #18 | ||
// if props or state did change, but a render was scheduled already, no additional render needs to be scheduled | ||
if (this.render.$mobx && this.render.$mobx.isScheduled() === true) | ||
return false; | ||
// update on any state changes (as is the default) | ||
@@ -201,3 +205,4 @@ if (this.state !== nextState) | ||
} else { | ||
return createStoreInjector(arg1, observer(arg2)); | ||
// TODO: deprecate this invocation style | ||
return inject.apply(null, arg1)(observer(arg2)); | ||
} | ||
@@ -285,3 +290,3 @@ } | ||
*/ | ||
function createStoreInjector(stores, component) { | ||
function createStoreInjector(grabStoresFn, component) { | ||
var Injector = React.createClass({ | ||
@@ -293,14 +298,9 @@ displayName: "MobXStoreInjector", | ||
newProps[key] = this.props[key]; | ||
var baseStores = this.context.mobxStores; | ||
stores.forEach(function(storeName) { | ||
if (storeName in newProps) // prefer props over stores | ||
return; | ||
if (!(storeName in baseStores)) | ||
throw new Error("MobX observer: Store '" + storeName + "' is not available! Make sure it is provided by some Provider"); | ||
newProps[storeName] = baseStores[storeName]; | ||
}, this); | ||
newProps = grabStoresFn(this.context.mobxStores || {}, newProps, this.context); | ||
return React.createElement(component, newProps); | ||
} | ||
// TODO: should have shouldComponentUpdate? | ||
}); | ||
Injector.contextTypes = { mobxStores: PropTypes.object }; | ||
Injector.wrappedComponent = component; | ||
return Injector; | ||
@@ -310,2 +310,66 @@ } | ||
/** | ||
* higher order component that injects stores to a child. | ||
* takes either a varargs list of strings, which are stores read from the context, | ||
* or a function that manually maps the available stores from the context to props: | ||
* storesToProps(mobxStores, props, context) => newProps | ||
*/ | ||
function inject(/* fn(stores, nextProps) or ...storeNames */) { | ||
var grabStoresFn; | ||
if (typeof arguments[0] === "function") { | ||
grabStoresFn = arguments[0]; | ||
} else { | ||
var storesNames = []; | ||
for (var i = 0; i < arguments.length; i++) | ||
storesNames[i] = arguments[i]; | ||
grabStoresFn = grabStoresByName(storesNames); | ||
} | ||
return function(componentClass) { | ||
return createStoreInjector(grabStoresFn, componentClass); | ||
}; | ||
} | ||
function grabStoresByName(storeNames) { | ||
return function(baseStores, nextProps) { | ||
storeNames.forEach(function(storeName) { | ||
if (storeName in nextProps) // prefer props over stores | ||
return; | ||
if (!(storeName in baseStores)) | ||
throw new Error("MobX observer: Store '" + storeName + "' is not available! Make sure it is provided by some Provider"); | ||
nextProps[storeName] = baseStores[storeName]; | ||
}); | ||
return nextProps; | ||
} | ||
} | ||
/** | ||
* PropTypes | ||
*/ | ||
function observableTypeChecker (type) { | ||
return function(props, propName, componentName) { | ||
if (!mobx['isObservable' + type](props[propName])) { | ||
return new Error( | ||
'Invalid prop `' + propName + '` supplied to' + | ||
' `' + componentName + '`. Expected a mobx observable ' + type + '. Validation failed.' | ||
); | ||
} | ||
}; | ||
} | ||
// oneOfType is used for simple isRequired chaining | ||
var propTypes = { | ||
observableArray: React.PropTypes.oneOfType([observableTypeChecker('Array')]), | ||
observableMap: React.PropTypes.oneOfType([observableTypeChecker('Map')]), | ||
observableObject: React.PropTypes.oneOfType([observableTypeChecker('Object')]), | ||
arrayOrObservableArray: React.PropTypes.oneOfType([ | ||
React.PropTypes.array, | ||
observableTypeChecker('Array') | ||
]), | ||
objectOrObservableObject: React.PropTypes.oneOfType([ | ||
React.PropTypes.object, | ||
observableTypeChecker('Object') | ||
]) | ||
}; | ||
/** | ||
* Export | ||
@@ -316,2 +380,4 @@ */ | ||
Provider: Provider, | ||
inject: inject, | ||
propTypes: propTypes, | ||
reactiveComponent: function() { | ||
@@ -318,0 +384,0 @@ console.warn("[mobx-react] `reactiveComponent` has been renamed to `observer` and will be removed in 1.1."); |
/** | ||
* Turns a React component or stateless render function into a reactive component. | ||
*/ | ||
import React = require('react'); | ||
import React = require("react"); | ||
export function observer<P>(clazz: React.StatelessComponent<P>): React.ClassicComponentClass<P>; | ||
export function observer<P>(renderFunction: (props: P) => React.ReactElement<any>): React.ClassicComponentClass<P>; | ||
export function observer<P>(renderFunction: (props: P) => React.ReactElement<P>): React.ClassicComponentClass<P>; | ||
export function observer<P>(clazz: React.ClassicComponentClass<P>): React.ClassicComponentClass<P>; | ||
export function observer<P>(clazz: React.ComponentClass<P>): React.ComponentClass<P>; | ||
export function observer<TFunction extends React.ComponentClass<any>>(target: TFunction): TFunction; // decorator signature | ||
export function observer<P, TFunction extends React.ComponentClass<P>>(target: TFunction): TFunction; // decorator signature | ||
@@ -17,6 +17,42 @@ // with stores | ||
export function observer<P>(stores: string[], clazz: React.ComponentClass<P>): React.ComponentClass<P>; | ||
export function observer(stores: string[]): <TFunction extends React.ComponentClass<any>>(target: TFunction) => TFunction; // decorator signature | ||
export function observer<P>(stores: string[]): <TFunction extends React.ComponentClass<P>>(target: TFunction) => TFunction; // decorator signature | ||
// inject | ||
export function inject<P>(...stores: string[]): <TFunction extends React.ComponentClass<P>>(target: TFunction) => TFunction; // decorator signature | ||
export function inject<T, P>(storesToProps : (stores: any, nextProps: P, context:any) => T): <TFunction extends React.ComponentClass<T | P>>(target: TFunction) => TFunction; // decorator | ||
export class Provider extends React.Component<any, {}> { | ||
} | ||
} | ||
/** | ||
* Enable dev tool support, makes sure that renderReport emits events. | ||
*/ | ||
export function trackComponents():void; | ||
export const renderReporter: RenderReporter; | ||
export interface RenderReporter { | ||
on(handler: (data: IRenderEvent) => void): void; | ||
} | ||
export interface IRenderEvent { | ||
event: "render" | "destroy"; | ||
renderTime?: number; | ||
totalTime?: number; | ||
component: React.ReactElement<any>; // Component instance | ||
node: any; // DOMNode | ||
} | ||
/** | ||
* WeakMap DOMNode -> Component instance | ||
*/ | ||
export const componentByNodeRegistery: any; | ||
export const propTypes: { | ||
observableArray: React.Requireable<any>; | ||
observableMap: React.Requireable<any>; | ||
observableObject: React.Requireable<any>; | ||
arrayOrObservableArray: React.Requireable<any>; | ||
objectOrObservableObject: React.Requireable<any>; | ||
} |
@@ -102,3 +102,4 @@ (function() { | ||
self.componentWillReact(); | ||
React.Component.prototype.forceUpdate.call(self) | ||
if (self.__$mobxMounted) | ||
React.Component.prototype.forceUpdate.call(self) | ||
} | ||
@@ -129,2 +130,3 @@ }); | ||
this.render.$mobx && this.render.$mobx.dispose(); | ||
this.__$mobxMounted = false; | ||
if (isDevtoolsEnabled) { | ||
@@ -144,2 +146,3 @@ var node = findDOMNode(this); | ||
componentDidMount: function() { | ||
this.__$mobxMounted = true; | ||
if (isDevtoolsEnabled) | ||
@@ -155,6 +158,7 @@ reportRendering(this); | ||
shouldComponentUpdate: function(nextProps, nextState) { | ||
// TODO: if context changed, return true.., see #18 | ||
// if props or state did change, but a render was scheduled already, no additional render needs to be scheduled | ||
if (this.render.$mobx && this.render.$mobx.isScheduled() === true) | ||
return false; | ||
// update on any state changes (as is the default) | ||
@@ -201,3 +205,4 @@ if (this.state !== nextState) | ||
} else { | ||
return createStoreInjector(arg1, observer(arg2)); | ||
// TODO: deprecate this invocation style | ||
return inject.apply(null, arg1)(observer(arg2)); | ||
} | ||
@@ -285,3 +290,3 @@ } | ||
*/ | ||
function createStoreInjector(stores, component) { | ||
function createStoreInjector(grabStoresFn, component) { | ||
var Injector = React.createClass({ | ||
@@ -293,14 +298,9 @@ displayName: "MobXStoreInjector", | ||
newProps[key] = this.props[key]; | ||
var baseStores = this.context.mobxStores; | ||
stores.forEach(function(storeName) { | ||
if (storeName in newProps) // prefer props over stores | ||
return; | ||
if (!(storeName in baseStores)) | ||
throw new Error("MobX observer: Store '" + storeName + "' is not available! Make sure it is provided by some Provider"); | ||
newProps[storeName] = baseStores[storeName]; | ||
}, this); | ||
newProps = grabStoresFn(this.context.mobxStores || {}, newProps, this.context); | ||
return React.createElement(component, newProps); | ||
} | ||
// TODO: should have shouldComponentUpdate? | ||
}); | ||
Injector.contextTypes = { mobxStores: PropTypes.object }; | ||
Injector.wrappedComponent = component; | ||
return Injector; | ||
@@ -310,2 +310,66 @@ } | ||
/** | ||
* higher order component that injects stores to a child. | ||
* takes either a varargs list of strings, which are stores read from the context, | ||
* or a function that manually maps the available stores from the context to props: | ||
* storesToProps(mobxStores, props, context) => newProps | ||
*/ | ||
function inject(/* fn(stores, nextProps) or ...storeNames */) { | ||
var grabStoresFn; | ||
if (typeof arguments[0] === "function") { | ||
grabStoresFn = arguments[0]; | ||
} else { | ||
var storesNames = []; | ||
for (var i = 0; i < arguments.length; i++) | ||
storesNames[i] = arguments[i]; | ||
grabStoresFn = grabStoresByName(storesNames); | ||
} | ||
return function(componentClass) { | ||
return createStoreInjector(grabStoresFn, componentClass); | ||
}; | ||
} | ||
function grabStoresByName(storeNames) { | ||
return function(baseStores, nextProps) { | ||
storeNames.forEach(function(storeName) { | ||
if (storeName in nextProps) // prefer props over stores | ||
return; | ||
if (!(storeName in baseStores)) | ||
throw new Error("MobX observer: Store '" + storeName + "' is not available! Make sure it is provided by some Provider"); | ||
nextProps[storeName] = baseStores[storeName]; | ||
}); | ||
return nextProps; | ||
} | ||
} | ||
/** | ||
* PropTypes | ||
*/ | ||
function observableTypeChecker (type) { | ||
return function(props, propName, componentName) { | ||
if (!mobx['isObservable' + type](props[propName])) { | ||
return new Error( | ||
'Invalid prop `' + propName + '` supplied to' + | ||
' `' + componentName + '`. Expected a mobx observable ' + type + '. Validation failed.' | ||
); | ||
} | ||
}; | ||
} | ||
// oneOfType is used for simple isRequired chaining | ||
var propTypes = { | ||
observableArray: React.PropTypes.oneOfType([observableTypeChecker('Array')]), | ||
observableMap: React.PropTypes.oneOfType([observableTypeChecker('Map')]), | ||
observableObject: React.PropTypes.oneOfType([observableTypeChecker('Object')]), | ||
arrayOrObservableArray: React.PropTypes.oneOfType([ | ||
React.PropTypes.array, | ||
observableTypeChecker('Array') | ||
]), | ||
objectOrObservableObject: React.PropTypes.oneOfType([ | ||
React.PropTypes.object, | ||
observableTypeChecker('Object') | ||
]) | ||
}; | ||
/** | ||
* Export | ||
@@ -316,2 +380,4 @@ */ | ||
Provider: Provider, | ||
inject: inject, | ||
propTypes: propTypes, | ||
reactiveComponent: function() { | ||
@@ -318,0 +384,0 @@ console.warn("[mobx-react] `reactiveComponent` has been renamed to `observer` and will be removed in 1.1."); |
{ | ||
"name": "mobx-react", | ||
"version": "3.4.0", | ||
"version": "3.5.0", | ||
"description": "React bindings for MobX. Create fully reactive components.", | ||
@@ -37,3 +37,3 @@ "main": "index.js", | ||
"tape-run": "2.1.0", | ||
"typescript": "^1.7.5" | ||
"typescript": "^1.8.10" | ||
}, | ||
@@ -40,0 +40,0 @@ "dependencies": {}, |
@@ -70,2 +70,5 @@ # mobx-react | ||
It is possible to set a custom `shouldComponentUpdate`, but in general this should be avoid as MobX will by default provide a highly optimized `shouldComponentUpdate` implementation, based on `PureRenderMixin`. | ||
If a custom `shouldComponentUpdate` is provided, it is consulted when the props changes (because the parent passes new props) or the state changes (as a result of calling `setState`), but if an observable used by the rendering is changed, the component will be re-rendered and `shouldComponent` is not consulted. | ||
### `componentWillReact` (lifecycle hook) | ||
@@ -95,4 +98,15 @@ | ||
### `Provider` (Experimental) | ||
### `propTypes` | ||
MobX-react provides the following additional `propTypes` which can be used to validate against MobX structures: | ||
* `observableArray` | ||
* `observableMap` | ||
* `observableObject` | ||
* `arrayOrObservableArray` | ||
* `objectOrObservableObject` | ||
### `Provider` and `inject` (Experimental) | ||
_This feature is marked as experimental as the exact api might change in a next minor, pending any community feedback_. | ||
@@ -102,6 +116,9 @@ | ||
This is useful if you have things that you don't want to pass through multiple layers of components explicitly. | ||
By passing a string array as first argument to `observer`, observer will pick up the named stores from the context and make them available as props of the decorated component: | ||
`inject` can be used to pick up those stores. It is a higher order component that takes a list of strings and makes those stores available to the wrapped component. | ||
Example (based on the official [context docs](https://facebook.github.io/react/docs/context.html#passing-info-automatically-through-a-tree)): | ||
```javascript | ||
@observer(["color"]) | ||
@inject("color") @observer | ||
class Button extends React.Component { | ||
@@ -128,6 +145,2 @@ render() { | ||
class MessageList extends React.Component { | ||
getChildContext() { | ||
return {color: "purple"}; | ||
} | ||
render() { | ||
@@ -146,6 +159,35 @@ const children = this.props.messages.map((message) => | ||
Some note about passing stores around: | ||
Notes: | ||
* If a component ask a store and receives a store via a property with the same name, the property takes precedence. Use this to your advantage when testing! | ||
* Values provided through `Provider` should be final, to avoid issues like mentioned in [React #2517](https://github.com/facebook/react/issues/2517) and [React #3973](https://github.com/facebook/react/pull/3973), where optimizations might stop the propagation of new context. Instead, make sure that if you put things in `context` that might change over time, that they are `@observable` or provide some other means to listen to changes, like callbacks. | ||
* When using both `@inject` and `@observer`, make sure to apply them in the correct order: `observer` should be the inner decorator, `inject` the outher. There might be additional decorators in between. | ||
* The original component wrapped by `inject` is available as the `wrappedComponent` property of created the higher order component. | ||
#### Inject as function | ||
The above example in ES5 would start like: | ||
```javascript | ||
var Button = inject("color")(observer(React.createClass({ | ||
/* ... etc ... */ | ||
}))) | ||
``` | ||
#### Strongly typing inject | ||
`inject` also accepts a function (`(allStores, nextProps, nextContext) => nextProps`) that can be used to pick all the desired stores from the available stores like this: | ||
```typescript | ||
import {IUserStore} from "myStore" | ||
@inject((allStores) => ({ | ||
userStore: allStores.userStore as IUserStore | ||
})) | ||
class MyComponent extends React.Component<{ userStore?: IUserStore; otherProp: number }, {}> { | ||
/* etc */ | ||
} | ||
``` | ||
Make sure to mark `userStore` as optional property. It should not (necessarily) be passed in by parent components after all! | ||
## FAQ | ||
@@ -152,0 +194,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
71775
1218
244