Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: bitovi/react-to-web-component
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: main
Choose a base ref
...
head repository: canjs/react-to-can-webcomponent
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
Can’t automatically merge. Don’t worry, you can still create the pull request.

Commits on Jan 21, 2020

  1. updating version number

    phillipskevin committed Jan 21, 2020
    Copy the full SHA
    23b96bf View commit details
  2. updating package name

    phillipskevin committed Jan 21, 2020
    Copy the full SHA
    47f16b6 View commit details

Commits on Jan 22, 2020

  1. Copy the full SHA
    5f5cf9d View commit details
  2. Copy the full SHA
    5d271f0 View commit details
  3. Copy the full SHA
    d7e1748 View commit details
  4. Merge pull request #3 from canjs/nested-property-rerender

    Rerender React component when nested properties change (redux)
    bmomberger-bitovi authored Jan 22, 2020
    Copy the full SHA
    78d1ec5 View commit details
  5. Copy the full SHA
    62d5d6d View commit details

Commits on Jan 23, 2020

  1. Copy the full SHA
    126094c View commit details
  2. Copy the full SHA
    a3895a8 View commit details

Commits on Jan 24, 2020

  1. Copy the full SHA
    5974344 View commit details
  2. Merge pull request #5 from canjs/use-use-observer

    Use ylem's useObserver() to solve issues with observations and ReactDom render timing [redux]
    bmomberger-bitovi authored Jan 24, 2020
    Copy the full SHA
    2c1d7c3 View commit details
  3. Copy the full SHA
    420d3e0 View commit details
  4. Copy the full SHA
    709cd17 View commit details
  5. Copy the full SHA
    b39900c View commit details
  6. Merge pull request #6 from canjs/update-docs

    Update docs to distinguish from react-to-webcomponent
    bmomberger-bitovi authored Jan 24, 2020
    Copy the full SHA
    834624c View commit details
  7. Copy the full SHA
    1f6b1a3 View commit details
  8. Merge pull request #7 from canjs/update-docs

    Fixes/cleanups to build and docs
    bmomberger-bitovi authored Jan 24, 2020
    Copy the full SHA
    9859d87 View commit details
  9. Copy the full SHA
    a36b515 View commit details

Commits on May 7, 2020

  1. Fix issue where root components were re-mounted instead of updated

    Previously, the ObservedComponent was recreated with every update. This fixes the issue by using the same component for the lifespan of the element.
    chasenlehara committed May 7, 2020
    Copy the full SHA
    d03ac2f View commit details
  2. Copy the full SHA
    89e3206 View commit details
  3. Update version number

    chasenlehara committed May 7, 2020
    Copy the full SHA
    0455da4 View commit details

Commits on Apr 3, 2024

  1. Copy the full SHA
    076c8ff View commit details
  2. Merge pull request #11 from canjs/react-18-support

    React 18 support
    bmomberger-bitovi authored Apr 3, 2024
    Copy the full SHA
    931cb99 View commit details
  3. Copy the full SHA
    c1c716f View commit details
  4. Copy the full SHA
    0f03a7b View commit details
Showing with 601 additions and 195 deletions.
  1. +28 −25 README.md
  2. +16 −4 build.js
  3. +94 −0 lib/can-observer.js
  4. +25 −0 lib/use-observer.js
  5. +32 −18 package.json
  6. +375 −0 react-to-can-webcomponent-test.js
  7. +30 −1 react-to-webcomponent.js → react-to-can-webcomponent.js
  8. +0 −146 react-to-webcomponent-test.js
  9. +1 −1 test.html
53 changes: 28 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# react-to-webcomponent
# react-to-can-webcomponent

`react-to-webcomponent` converts [React](https://reactjs.org/) components to [custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements)! It lets you share react components as native elements that __don't__ require mounted being through React. The custom element acts as a wrapper for the underlying react component. Use these custom elements in any framework (vue, svelte, angular, ember, canjs) the same way you would use standard HTML elements.
`react-to-can-webcomponent` converts [React](https://reactjs.org/) components to [custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements)! It lets you share react components as native elements that __don't__ require mounted being through React. The custom element acts as a wrapper for the underlying react component. Use these custom elements in any framework (vue, svelte, angular, ember, canjs) the same way you would use standard HTML elements.

`react-to-webcomponent`:
`react-to-can-webcomponent`:

- Works in all modern browsers. (Edge needs a [customElements polyfill](https://github.com/webcomponents/polyfills/tree/master/packages/custom-elements)).
- Is `1.11KB` minified and gzipped.
@@ -13,18 +13,18 @@ Given a react component like:

```js
class Greeting extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
```

Call `reactToWebComponent` and [customElements.define](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define) as follows:
Call `reactToCanWebComponent` and [customElements.define](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define) as follows:

```js
import reactToWebComponent from "react-to-webcomponent";
import reactToCanWebComponent from "react-to-can-webcomponent";

const WebGreeting = reactToWebComponent(Greeting, React, ReactDOM);
const WebGreeting = reactToCanWebComponent(Greeting, React, ReactDOM);

customElements.define("web-greeting", WebGreeting);
```
@@ -55,24 +55,24 @@ document.body.firstChild.innerHTML //-> "<h1>Hello, CoolBeans</h1>"

### Working with Attributes

By default, custom elements created by `reactToWebComponent` only
By default, custom elements created by `reactToCanWebComponent` only
pass properties to the underlying React component. To make attributes
work, you must specify your component's properties with
[PropTypes](https://reactjs.org/docs/typechecking-with-proptypes.html) as follows:

```js
class Greeting extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}

Greeting.propTypes = {
name: PropTypes.string.isRequired
name: PropTypes.string.isRequired
};
```

Now `reactToWebComponent` will know to look for `name` attributes
Now `reactToCanWebComponent` will know to look for `name` attributes
as follows:

```js
@@ -89,35 +89,35 @@ document.body.firstChild.innerHTML //-> "<h1>Hello, Amazed</h1>"
To install from npm:

```
npm i react-to-webcomponent
npm i react-to-can-webcomponent
```

#### CodePen

[Greeting example in a CodePen](https://codepen.io/justinbmeyer/pen/gOYrQax?editors=1010)
[Greeting example in a CodePen](https://codepen.io/bmomberger-bitovi/pen/BaybzQX?editors=1010)

## API

`reactToWebComponent(ReactComponent, React, ReactDOM)` takes the following:
`reactToCanWebComponent(ReactComponent, React, ReactDOM)` takes the following:

- `ReactComponent` - A react component that you want to
convert to a Web Component.
convert to a Web Component.
- `React` - A version of React (or [preact-compat](https://preactjs.com/guide/v10/switching-to-preact)) the
component works with.
component works with.
- `ReactDOM` - A version of ReactDOM (or preact-compat) that the component works with.

A new class inheriting from `HTMLElement` is
returned. This class can be directly passed to `customElements.define` as follows:

```js
customElements.define("web-greeting",
reactToWebComponent(Greeting, React, ReactDOM) );
reactToCanWebComponent(Greeting, React, ReactDOM) );
```

Or the class can be defined and used later:

```js
const WebGreeting = reactToWebComponent(Greeting, React, ReactDOM);
const WebGreeting = reactToCanWebComponent(Greeting, React, ReactDOM);

customElements.define("web-greeting", WebGreeting);

@@ -128,7 +128,7 @@ document.body.appendChild(myGreeting);
Or the class can be extended:

```js
class WebGreeting extends reactToWebComponent(Greeting, React, ReactDOM)
class WebGreeting extends reactToCanWebComponent(Greeting, React, ReactDOM)
{
disconnectedCallback(){
super.disconnectedCallback();
@@ -140,11 +140,14 @@ customElements.define("web-greeting", WebGreeting);

### How it works

`reactToWebComponent` creates a constructor function whose prototype is a [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). This acts as a trap for any property set on instances of the custom element. When a property is set, the proxy:
`reactToCanWebComponent` creates a constructor function whose prototype is a [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). This acts as a trap for any property set on instances of the custom element. When a property is set, the proxy:

- re-renders the React component inside the custom element.
- creates an enumerable getter / setter on the instance
to save the set value and avoid hitting the proxy in the future.
to save the set value and avoid hitting the proxy in the future.
- sets up an observer to listen for observable properties that are read by the component.
(Note: this is the only difference between this library and
[Bitovi's react-to-webcomponent library](https://github.com/bitovi/react-to-webcomponent) )

Also:

20 changes: 16 additions & 4 deletions build.js
Original file line number Diff line number Diff line change
@@ -7,21 +7,33 @@ var baseNormalize = globalJS.normalize();
stealTools.export({
steal: {
config: __dirname + "/package.json!npm",
main: "react-to-webcomponent",
main: "react-to-can-webcomponent",
},
outputs: {
"global core": {
modules: ["react-to-webcomponent"],
modules: ["react-to-can-webcomponent"],
format: "global",
dest: __dirname + "/dist/react-to-webcomponent.js",
dest: __dirname + "/dist/react-to-can-webcomponent.js",
removeDevelopmentCode: false,
useNormalizedDependencies: true,
exports: {
"react-to-webcomponent": "reactToWebComponent"
"react-to-can-webcomponent": "reactToCanWebComponent"
},
normalize: function(depName, depLoad, curName, curLoad, loader){
return baseNormalize.call(this, depName, depLoad, curName, curLoad, loader, true);
},
},
"+bundled-es core": {
modules: ["react-to-can-webcomponent"],
addProcessShim: true,
dest: __dirname + "/dist/react-to-can-webcomponent.mjs",
removeDevelopmentCode: false
},
"+bundled-es core minified": {
modules: ["react-to-can-webcomponent"],
addProcessShim: true,
minify: true,
dest: __dirname + "/dist/react-to-can-webcomponent.min.mjs"
}
}
}).catch(function(e){
94 changes: 94 additions & 0 deletions lib/can-observer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import canReflect from 'can-reflect';
import ObservationRecorder from 'can-observation-recorder';
import recorderHelpers from 'can-observation/recorder-dependency-helpers';
import queues from 'can-queues';

ObservationRecorder.resume = function resume(deps) {
ObservationRecorder.stack.push(deps);
};

let ORDER;
let weLeftSomethingOnTheStack = false;

export default class Observer {
constructor(onUpdate) {
this.newDependencies = ObservationRecorder.makeDependenciesRecorder();
this.oldDependencies = null;
this.onUpdate = onUpdate;

this.onDependencyChange = (newVal, oldVal) => {
this.dependencyChange(newVal, oldVal);
};
}

startRecording() {
if (weLeftSomethingOnTheStack) {
const deps = ObservationRecorder.stop();
weLeftSomethingOnTheStack = false;

if (!deps.ylem) {
throw new Error(
'If you see this error with another error, clearing that should solve this. If you see ' +
'this error alone, please open an issue at https://github.com/canjs/react-to-can-webcomponent/issues'
);
}
}

this.oldDependencies = this.newDependencies;
this.nextDependencies = ObservationRecorder.start();
this.nextDependencies.ylem = true;
weLeftSomethingOnTheStack = true;

if (this.order !== undefined) {
ORDER = this.order;
} else if (ORDER !== undefined) {
this.order = ORDER;
ORDER += 1;
} else {
// the root component
ORDER = 0;
this.order = ORDER;
}
}

stopRecording() {
if (weLeftSomethingOnTheStack) {
const deps = ObservationRecorder.stop();
weLeftSomethingOnTheStack = false;

if (!deps.ylem) {
throw new Error(
'If you see this error with another error, clearing that should solve this. If you see ' +
'this error alone, please open an issue at https://github.com/canjs/react-to-can-webcomponent/issues'
);
}
}

this.newDependencies = this.nextDependencies;
recorderHelpers.updateObservations(this);
}

dependencyChange() {
queues.deriveQueue.enqueue(this.onUpdate, this, [], {
priority: this.order,
});
}

teardown() {
recorderHelpers.stopObserving(this.newDependencies, this.onDependencyChange);
queues.deriveQueue.dequeue(this.onUpdate);
}

// eslint-disable-next-line class-methods-use-this
ignore(fn) {
return ObservationRecorder.ignore(fn)();
}
}

if (process.env.NODE_ENV !== 'production') {
canReflect.assignSymbols(Observer.prototype, {
'can.getName': function getName() {
return `${canReflect.getName(this.constructor)}<${canReflect.getName(this.onUpdate)}>`;
},
});
}
25 changes: 25 additions & 0 deletions lib/use-observer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import Observer from './can-observer';

// Unlike the ylem version, this hook is passed a
// React instance so it can be used seamlessly
// by Preact et al.
export default function useObserver(React) {
const { useEffect, useLayoutEffect, useState, useRef } = React;

const [, update] = useState();

const observer = useRef(new Observer(() => update({})));

observer.current.startRecording();
useLayoutEffect(() => {
observer.current.stopRecording();
});

// eslint-disable-next-line arrow-body-style
useEffect(() => {
return () => {
observer.current.teardown();
observer.current = null;
};
}, []);
}
50 changes: 32 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,34 +1,48 @@
{
"name": "react-to-webcomponent",
"version": "1.4.0",
"description": "Convert react components to native Web Components.",
"main": "react-to-webcomponent",
"dependencies": {},
"name": "react-to-can-webcomponent",
"version": "1.5.0",
"description": "Convert react components to native Web Components that work with CanJS.",
"main": "react-to-can-webcomponent",
"dependencies": {
"can-observation": "^4.2.0",
"can-observation-recorder": "^1.3.1",
"can-queues": "^1.3.1",
"can-reflect": "^1.18.0"
},
"devDependencies": {
"@webcomponents/custom-elements": "^1.2.4",
"can-stache": "^4.17.20",
"can-stache-bindings": "^4.10.9",
"preact": "^8.5.2",
"preact-compat": "^3.19.0",
"can-observable-array": "^1.0.6",
"can-observable-object": "^1.0.1",
"can-stache": "^5.1.1",
"can-stache-bindings": "^5.0.4",
"preact": "^10.2.1",
"prop-types": "^15.7.2",
"react": "^16.9.0",
"react-dom": "^16.9.0",
"steal": "^2.2.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"steal": "^2.2.4",
"steal-qunit": "^2.0.0",
"steal-tools": "^2.2.2"
"steal-tools": "^2.2.2",
"testee": "^0.9.1"
},
"scripts": {
"build": "node build.js",
"preversion": "npm test && npm run build",
"version": "git commit -am \"Update version number\" && git checkout -b release && git add -f dist/",
"postpublish": "git push --tags && git checkout master && git branch -D release && git push origin master",
"postpublish": "git push --tags && git checkout main && 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"
},
"steal": {
"map": {
"preact/compat": "preact/compat/dist/compat",
"preact/hooks": "preact/hooks/dist/hooks"
}
},
"repository": {
"type": "git",
"url": "git+https://github.com/bitovi/react-to-webcomponent.git"
"url": "git+https://github.com/canjs/react-to-can-webcomponent.git"
},
"keywords": [
"React",
@@ -38,7 +52,7 @@
"author": "Bitovi",
"license": "MIT",
"bugs": {
"url": "https://github.com/bitovi/react-to-webcomponent/issues"
"url": "https://github.com/canjs/react-to-can-webcomponent/issues"
},
"homepage": "https://github.com/bitovi/react-to-webcomponent#readme"
"homepage": "https://github.com/canjs/react-to-can-webcomponent#readme"
}
Loading