Skip to content

Commit

Permalink
Merge pull request #3 from canjs/nested-property-rerender
Browse files Browse the repository at this point in the history
Rerender React component when nested properties change (redux)
  • Loading branch information
bmomberger-bitovi authored Jan 22, 2020
2 parents 47f16b6 + d7e1748 commit 78d1ec5
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 12 deletions.
21 changes: 13 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,21 @@
"version": "0.0.1",
"description": "Convert react components to native Web Components that work with CanJS.",
"main": "react-to-can-webcomponent",
"dependencies": {},
"dependencies": {
"can-observation": "^4.2.0"
},
"devDependencies": {
"@webcomponents/custom-elements": "^1.2.4",
"can-stache": "^4.17.20",
"can-stache-bindings": "^4.10.9",
"preact": "^8.5.2",
"can-observable-array": "^1.0.6",
"can-observable-object": "^1.0.1",
"can-stache": "^5.1.1",
"can-stache-bindings": "^5.0.4",
"preact": "^8.5.3",
"preact-compat": "^3.19.0",
"prop-types": "^15.7.2",
"react": "^16.9.0",
"react-dom": "^16.9.0",
"steal": "^2.2.2",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"steal": "^2.2.4",
"steal-qunit": "^2.0.0",
"steal-tools": "^2.2.2"
},
Expand All @@ -24,7 +28,8 @@
"postpublish": "git push --tags && git checkout master && git branch -D release && git push origin master",
"release:patch": "npm version patch && npm publish",
"release:minor": "npm version minor && npm publish",
"release:major": "npm version major && npm publish"
"release:major": "npm version major && npm publish",
"test": "testee --browsers firefox test.html"
},
"repository": {
"type": "git",
Expand Down
96 changes: 93 additions & 3 deletions react-to-webcomponent-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import PropTypes from 'prop-types';
import PreactCompat from "preact-compat";
import stache from "can-stache";
import stacheBindings from "can-stache-bindings";
import ObservableObject from "can-observable-object";
import ObservableArray from "can-observable-array";


stache.addBindings(stacheBindings);
Expand All @@ -19,7 +21,7 @@ QUnit.test("basics with react", function(assert) {
render() {
return <h1>Hello, {
this.props.name
}< /h1>;
}</h1>;
}
}

Expand Down Expand Up @@ -49,7 +51,7 @@ QUnit.test("works with attributes set with propTypes", function(assert) {
render() {
return <h1 >Hello, {
this.props.name
}< /h1>;
}</h1>;
}
}
Greeting.propTypes = {
Expand Down Expand Up @@ -110,7 +112,7 @@ QUnit.test("basics with preact", function(assert){
myWelcome.name = "Justin";

assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Justin", "can update");
})
});

QUnit.test("works within can-stache and can-stache-bindings (propTypes are writable)", function(assert){
class Welcome extends React.Component {
Expand Down Expand Up @@ -144,3 +146,91 @@ QUnit.test("works within can-stache and can-stache-bindings (propTypes are writa

assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Bohdi", "can update");
});


QUnit.test("works with nested properties of observable objects and arrays", function(assert) {
class Welcome extends React.Component {
render() {
return <React.Fragment>
<h1>
Hello, { this.props.name.full }
</h1>
<h1>
I see you like { this.props.hobbies.join(" and ")}
</h1>
</React.Fragment>;
}
}

class MyWelcome extends reactToWebComponent(Welcome, React, ReactDOM) {}

customElements.define("nested-props-welcome", MyWelcome);

var fixture = document.getElementById("qunit-fixture");

var myWelcome = new MyWelcome();
myWelcome.name = new (class extends ObservableObject {
get full() {
return this.first + ' ' + this.last;
}
})({
first: "Justin",
last: "Meyer",
});
myWelcome.hobbies = new ObservableArray([
"basketball",
"javascript"
]);
fixture.appendChild(myWelcome);
assert.deepEqual(myWelcome.name.get(), { first: "Justin", last: "Meyer" });

assert.equal(myWelcome.nodeName, "NESTED-PROPS-WELCOME", "able to read nodeName");
assert.equal(myWelcome.childNodes.length, 2, "able to render something")
assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Justin Meyer", "renders the right thing");
assert.equal(myWelcome.childNodes[1].innerHTML, "I see you like basketball and javascript", "renders the right array");

myWelcome.name.first = "Ramiya";
// Note: myWelcome.hobbies[0] = "school" will not update the view as binding of list-likes
// listens for changes on the length.
myWelcome.hobbies.splice(1, 1, "school");

assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Ramiya Meyer", "can update object properties");
assert.equal(myWelcome.childNodes[1].innerHTML, "I see you like basketball and school", "can update array elements");

});

QUnit.test("subproperties update with can-stache and can-stache-bindings", function(assert){
class Welcome extends React.Component {
render() {
return <h1>Hello, {
this.props.user.name
}</h1>;
}
}
Welcome.propTypes = {
user: PropTypes.object
};

class MyWelcome extends reactToWebComponent(Welcome, React, ReactDOM) {}

customElements.define("can-welcome-ii", MyWelcome);

var view = stache("<can-welcome-ii user:from='this.person'/>");
var person = new ObservableObject({name: "Bohdi"});
var frag = view({
person
});

var fixture = document.getElementById("qunit-fixture");
var myWelcome = frag.firstElementChild;
fixture.appendChild(frag);

assert.equal(myWelcome.nodeName, "CAN-WELCOME-II", "able to read nodeName");

assert.equal(myWelcome.childNodes.length, 1, "able to render something")

assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Bohdi", "can update");

person.name = "Cherif";
assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Cherif", "can update");
});
13 changes: 12 additions & 1 deletion react-to-webcomponent.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import Observation from "can-observation";
var reactComponentSymbol = Symbol.for("r2wc.reactComponent");
var renderSymbol = Symbol.for("r2wc.reactRender");
var shouldRenderSymbol = Symbol.for("r2wc.shouldRender");
var observationSymbol = Symbol.for("r2wc.observation");

var define = {
// Creates a getter/setter that re-renders everytime a property is set.
Expand Down Expand Up @@ -72,8 +74,17 @@ export default function(ReactComponent, React, ReactDOM) {
// Once connected, it will keep updating the innerHTML.
// We could add a render method to allow this as well.
this[shouldRenderSymbol] = true;
this[renderSymbol]();
// Also catch any sub-properties of observables which
// are read while rendering the React component.
this[observationSymbol] = this[observationSymbol] || new Observation(() => {
this[renderSymbol]();
});
this[observationSymbol].on();
};
targetPrototype.disconnectedCallback = function() {
this[observationSymbol].off();
};

targetPrototype[renderSymbol] = function() {
if (this[shouldRenderSymbol] === true) {
var data = {};
Expand Down

0 comments on commit 78d1ec5

Please sign in to comment.