Bless
Last active
January 12, 2019 13:58
-
-
Save loklaan/bcdfded782d346cd9936 to your computer and use it in GitHub Desktop.
Knockout + React
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head lang="en"> | |
<meta charset="UTF-8"> | |
<title></title> | |
</head> | |
<body> | |
<h3>Knockout Example</h3> | |
<div id="koTest"> | |
<div data-bind="text: text"></div> | |
<div data-bind="react: { $: Components.ToDoList, props: { todos: todos }}"></div> | |
</div> | |
<h3>React Example</h3> | |
<div id="reactTest"></div> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.1/react.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.3.0/knockout-min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.6.15/browser.min.js"></script> | |
<script type="text/babel"> | |
window.Components = {}; | |
/* ================================================================== */ | |
/* knockout-react.js BEGIN */ | |
/** | |
* Mixin for allowing ko bindings inside of React components. | |
* | |
* When a react component changes, its `state` & `props` objects are wrapped | |
* and applied as a ViewModel to the root DOM node of the React component. | |
*/ | |
var KnockoutMixin = { | |
updateKnockout() { | |
// This has been bound to the dependancy chain in __koModel, | |
// found in the componentDidMount lifecycle stage. Changing this | |
// observable will cause __koModel to revaluate it's values. | |
this.__koTrigger(!this.__koTrigger()); | |
}, | |
componentDidMount() { | |
this.__koTrigger = ko.observable(true); | |
this.__koModel = ko.computed(function () { | |
// Magic. | |
// Calling this observable will add it to the dependancy | |
// chain for __koModel, as it is a computedObservable. | |
// Anytime __koTrigger is changed, such as in updateKnockout(), | |
// this model will revaluate | |
this.__koTrigger(); | |
return { | |
props: this.props, | |
state: this.state | |
}; | |
}, this); // Bind this computedObservable to the components 'this' | |
// Bind the __koModel view model to the components mounted DOM | |
// node | |
ko.applyBindings(this.__koModel, this.getDOMNode()); | |
}, | |
componentWillUnmount() { | |
ko.cleanNode(this.getDOMNode()); | |
}, | |
componentDidUpdate() { | |
this.updateKnockout(); | |
} | |
}; | |
/** | |
* Creates a ko handler for react components, with the keyword 'react'. | |
* It must be passed an $ option, with the name of the React Component, | |
* which is exposed globally. | |
* It can be passed a props option, which can be a ko observable, that | |
* will be passed as the props to the react component. | |
* | |
* <ul data-bind="react: { $: ToDoList, props: { todos: todos } }><ul> | |
* | |
* ToDoList must be on window. Tip: Namespace your components in window.Components | |
* todos must exist in the view model that is bound to the nearest bound DOM node parent | |
*/ | |
// | |
var reactHandler = ko.bindingHandlers.react = { | |
render: function ( el, Component, props ) { | |
React.render( | |
React.createElement(Component,props), | |
el | |
); | |
}, | |
init: function ( el, valueAccessor, allBindingsAccessor, viewModel, bindingContext ) { | |
var options = valueAccessor(); | |
var Component = ko.unwrap(options.component || options.$); | |
var props = ko.toJS(options.props || viewModel); | |
reactHandler.render(el, Component, props); | |
return { controlsDescendantBindings: true }; | |
}, | |
update: function ( el, valueAccessor, allBindingsAccessor, viewModel, bindingContext ) { | |
var options = valueAccessor(); | |
var Component = ko.unwrap(options.component || options.$); | |
var props = ko.toJS(options.props || viewModel); | |
reactHandler.render(el, Component, props); | |
return { controlsDescendantBindings: true }; | |
} | |
}; | |
/* knockout-react.js END */ | |
/* ================================================================== */ | |
// Our react component | |
var ToDoList = React.createClass({ | |
// Since we have ko bindings on inner elements, we use our Mixin | |
mixins: [ KnockoutMixin ], | |
propTypes: { | |
todos: React.PropTypes.array.isRequired | |
}, | |
render() { | |
return ( | |
<ul data-bind="foreach: props.todos"> | |
<li data-bind="text: $data"></li> | |
</ul> | |
); | |
} | |
}); | |
window.Components.ToDoList = ToDoList; // Expose the component globally | |
/** Knockout example */ | |
var viewModel = { | |
text: "some text", | |
todos: ko.observableArray([ | |
"one","two","three" | |
]) | |
}; | |
ko.applyBindings(viewModel, document.getElementById("koTest")); | |
setInterval(function () { | |
viewModel.todos.shift(); | |
viewModel.todos.push("" + Math.random()); | |
}, 100); | |
/** React example */ | |
var render = function (todos) { | |
React.render( | |
React.createElement(ToDoList, { todos: todos }), | |
document.getElementById("reactTest") | |
); | |
}; | |
var reactList = ["one","two","three"]; // Initial values | |
render(reactList); | |
setInterval(function () { | |
reactList.shift(); | |
reactList.push("" + Math.random()); | |
render(reactList); | |
}, 100); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment