Code Monkey home page Code Monkey logo

reactive-elements's Introduction

Reactive Elements

Note! The docs here are for the v1.0.0 alpha. This is not ready for production use yet.

You should use this README, which refers to 0.10.0, the latest stable version on npm: https://github.com/PixelsCommander/ReactiveElements/blob/7cce3d7b472989878ac1433cec0e8168fd4136aa/README.md

Convert React.js components into Web Components

npm install reactive-elements
yarn add reactive-elements

How to use?

Directly in a browser

Placing component in a pure HTML

<body>
	<my-react-component items="{window.someArray}"></my-react-component>
</body>

React class definition

/* @jsx React.DOM */
MyComponent = React.createClass({
  render: function() {
    console.log(this.props.items); // passed as HTML tag`s argument
    console.log(this.props.children); // original tag children
    return (
      <ul>
        <li>React content</li>
      </ul>
    );
  },
});

ReactiveElements('my-react-component', MyComponent);

With Bundler

import React, { Component } from 'react';
import ReactiveElements from 'reactive-elements';

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

ReactiveElements('welcome-component', Welcome);

Nesting

Original children of a custom element is injected to component as this.props.children.

<my-react-component>Hello world</my-react-component>

In this case this.props.children is equal to "Hello world".

Container node of the element is passed as this.props.container. Both props.container and props.children have type of documentFragment.

Boolean attribute transforms (added in version 0.7.0)

An attribute that has the value "true" or "false" will be converted into the boolean true or false when given to the React component:

<my-react-component is-logged-in="true">Hello world</my-react-component>

Here, this.props.isLoggedIn === true within the React component.

If you don't want this behaviour you can disable it with a special attribute:

<my-react-component is-logged-in="true" reactive-elements-no-boolean-transform>Hello world</my-react-component>

Exposing components methods on custom element

If you want to expose React component methods on custom element - assign them to component as following:

componentDidMount: function() {
    this.props.container.setTextContent = this.setTextContent.bind(this);
}

Handling attributes change

You may add attributeChanged callback to component in order to handle / modify / filter incoming values.

attributeChanged: function(attributeName, oldValue, newValue) {
    console.log('Attribute ' + attributeName + ' was changed from ' + oldValue + ' to ' + newValue);
    this.props[attributeName] = parseInt(newValue);
}

Communicate via DOM events

You may trigger DOM event from React component by using following snippet:

var event = new CustomEvent('change', {
  bubbles: true,
});
React.findDOMNode(this).dispatchEvent(event);

Subscribing to DOM events is similar:

React.findDOMNode(this).addEventListener('change', function(e){...});

Options

You can also specify options to the ReactiveElements call, e.g.

ReactiveElements('welcome-component', Welcome, options);

options.useShadowDom (default false)

By default, your React element is rendered directly into the web-component root. However, by setting this option - your React element will instead be rendered in a Shadow DOM inside the web-component instead.

Dependencies

  • React.js
  • React DOM
  • Custom elements support or polyfill
  • Support or polyfills for:
    • regexp.match
    • regexp.replace
    • object.define-setter
    • object.define-getter
    • object.define-property
    • function.name
    • web.dom.iterable
    • array.iterator
    • object.keys
    • object.set-prototype-of
    • reflect.construct
    • function.bind

License

MIT: http://mit-license.org/

Copyright 2014 Denis Radin aka PixelsCommander

Inspired by Christopher Chedeau`s react-xtags

reactive-elements's People

Contributors

addyosmani avatar christianmurphy avatar codejet avatar gitter-badger avatar gusnips avatar jackfranklin avatar jakobo avatar jayphelps avatar kof avatar kulor avatar pixelscommander avatar stephencookdev avatar timdp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

reactive-elements's Issues

Pass JSON as attribute value

Since we are parsing attribute value for {{}} sequence it is impossible to pass JSON - it is being interpreted as a pointer to value.

We need a heuristic to determine is it a JSON or pointer.

HTML5 attributes aren't case sensitive

I tried converting this react timer component using this library, and I noticed that there is a prop initialTimeRemaining so I used it something like this.

<react-timer initialTimeRemaining="60000"> </react-timer>

There were two problems with this.

  1. HTML5 doesn't support casing like this in attributes
  2. The prop is required to be a number

I would love it if reactive elements could handle the attribute mapping. Thoughts? I would love it if we could get to a point of having transparency between [react-native] and [react as web-components].

Releases not in sync with `npm`.

It seems that the latest release here (GitHub) was 0.6.5, but npm has 0.6.8. I find this a bit confusing. Is there a reason the release tags are not showing up on GitHub? Is the repo being hosted somewhere else?

Support Universal Module Definition

Right now, it looks like this lib only supports CommonJS (i.e. Node-style) module definitions. Unfortunately, I don't get to make use of that on my current project. Instead, I'm using requireJS which uses AMD style definitions. It would be nice if you supported UMD, thus supporting both styles.

The fix would likely look something like this (untested):

(function (root, factory) {
    if (typeof define === "function" && define.amd) {
        define(["react", "documentRegisterElement"], factory);
    } else if (typeof exports === "object") {
        module.exports = factory(require("react"), require("documentRegisterElement"));
    } else {
        root.ReactiveElements= factory(root.React, root.document.registerElement || root.document.register);
    }
}(this, function (React, documentRegisterElement) {
     var ReactiveElements = { 
         ...
    };
    return ReactiveElements;
}));

In the meantime, for anyone else in my predicament, in order to get ReactiveElements to work in a requireJS setup, wrap the source of the lib in the following:
define(function(require, exports, module) { <source> }).

attributeNameToPropertyName() breaks attributes containing 'data-' anywhere in name

        attributeNameToPropertyName: function (attributeName) {
            var result = attributeName.replace('x-', '').replace('data-', '');
            var delimiterIndex = -1;

            while ((delimiterIndex = utils.getNextDelimiterIndex(result)) !== -1) {
                result = result.slice(0, delimiterIndex) + result.charAt(delimiterIndex + 1).toUpperCase() + result.slice(delimiterIndex + 2, result.length);
            }

            return result;
        },

This replaces x- and data- ANYWHERE in the attribute name so it will break attributes with names like on-data-arrived.

I'd propose to simplify the function like this:

    var attributeNameToPropertyName = function(attributeName) {
        return attributeName.
            replace(/^(?:(?:x-)|(?:data-))/, ''). // Removing prefixes
            replace(/\W+(.)/g, function(x, chr) { // Camelising
                return chr.toUpperCase();
            });
    };

With this implementation getNextDelimiterIndex is no longer needed.

New WebComponent spec requires class syntax

Hello,
The longstanding means of using Object.create(HTMLElement.prototype) to create Web Components was dropped in v1.0, and Chrome now throws an Uncaught TypeError if you try to use the current ReactiveElements code.

For confirmation of this change, read:

It's not possible to use custom elements without ES6 classes.

This change is also impacting the WebComponenets demo in React itself. See facebook/react#8656.

CustomElements and React rendering compatibility issue

I stumbled over this prob recently:

In DOM changing props is sync, if you change it, you have it rendered immediately.
In react there is an update cycle.

If I set new props and then try to execute something that expects props are already handled inside - it might fail if something inside of react component depends on the rendering loop.

Which means that we either need a method instead of .props setter which will have a callback (same as React.render) method. Or we need something else.

registerReact() should return references to created react objects

It would be nice if I could interact with registered react object and element created by registerReact().

Could registerReact() return something like example below?

return {
  'reactiveElementId':  { element: documentRegisterElementRef, reactObject: reactObjectRef}
}

or Promise with this data?

Update to support recent versions of React

This breaks in recent versions of React since render now is located in ReactDOM, not React.

function create(parent, props) {
    var element = React.createElement(ReactComponent, props);
    return React.render(element, parent, props.onRender);
}

animate container height?

Is there a way to animate the height of the container when items are dynamically added/removed such that the height changes? At the moment, afaict, the change in height happens suddenly.

Suggestion: Optionally define what HTMLElement to extend

I would like to be able to specify what kind of HTMLElement my custom react elements extends. Now we always get a basic HTMLElement.

Would be a simple fix to add another optional parameter to document.registerReact and use that instead of HTMLElement.prototype if provided.

Remove UMD build in `dist`

I'd like to remove the UMD build in dist. I think it just adds some noise and I'd be surprised if anyone actually needs it.

Please let me know if you do need it and we can leave it be :)

Release latest version to npm.

The latest version available on npm is 0.4.9, which is 6 months old.
Could you please release the latest version from master to npm.

Setup Travis/CircleCI

So our tests run on PRs.

@PixelsCommander I think this needs some of your help because you own the repo. I would be happy to take ownership, or we could create an organisation so we can all share it, as such?

Don't populate `document.registerReact`

I'd rather have it be imported:

import registerReact from ...

Than pollute the global namespace. Would be a breaking change but a useful one I think. We could release with both supported but make use of document.registerReact log a deprecation warning.

Issue with _content property

Not sure if this is a documentation issue, a code issue, or something I'm doing wrong.

According to the documentation, you should be able to access your custom element's content using:
{ this.props._content }

However, _content appears to be returning the whole element object rather than it's contents, so to get the content, you have to use:
{ this.props._content.innerHTML }

So either:
A.) Documentation needs updated to state _content returns an object
B.) _content needs to return the innerHTML directly
C.) I'm doing something really really wrong. haha.

JSON in attribute, why single quotes are replaced by double quotes?

Are there any good reasons why single quotes are replaced by double quotes in

value = JSON.parse(jsonMatches[0].replace(/^{|}$/g, '').replace(/'/g, '"'));

My felling is that this is for the case when object field name is quoted using single quotes like:

{'obj': 'value'}

right? If this is the case then it is not a valid json according to http://rfc7159.net/rfc7159 and http://www.json.org/, only double quotes are allowed.

Anyway I think that it is not a very good idea to silently replace quotes because it can break data where single quotes are relevant. What do you think?

Update for React 0.12

React 0.12 changed the API for rendering component, leads to some errors when using this module.

render() does not accept DocumentFragment

I'm using React 0.14.3 and Chrome 47.0.2526.106.

I'm trying to create a component that accepts child elements but it's coming in as a DocumentFragment which React will not accept (as JSX {this.props.children}), and still looking for a way to convert to something that it does accept.

Maybe a new issue with React 14? Any solution or guidance is appreciated.

"Handling attributes change" section of README requires another example or may be removal

Since 0.13 version of ReactJS (http://facebook.github.io/react/blog/2015/02/24/react-v0.13-rc1.html) props are immutable. So, I think that example needs to be rewritten so as not to promote bad practices.

I am not also sure with this change changing attributes of a component is a good idea (since attributes are imported into a component as props, which are immutable). There should be other ways used to communicate to React component via Flux-like technics or something like that. May be, it will be better to mark this section as "not recommended to be used".

transcluding children

how do we transclude the children elements? I tried the code below and gotten an "Objects are not valid as a React child" error...

`


{this.props.children}

`

Consider collaboration

I'm the author of Maple, which is very similar in its philosophy to ReactiveElements, although with a different approach to achieving it.

Would be great to see if we could collaborate and take this forwards into a larger project ๐Ÿ‘

Thoughts?

Add CONTRIBUTING.md guide

To make it easier for people to contribute :)

It should cover:

  • how to run locally
  • how to run tests
  • how to format with Prettier

We should also setup a .editorconfig file and mention it in the guide.

Allow for a global configuration

Once 0.9.0 is out with #81 in it, we will now have two options that you can pass in on a per-registration basis:

document.registerReact('some-component', SomeComponent, {
  ignoreAttributeChanged: true,
  useAttachedCallback: true,
})

Do we want a way for a user to be able to define the defaults?

import reactiveElements from '...'

reactiveElements.setDefaultOptions({ ... })

reactiveElements.register(...)

Typecasting for attributes

At the moment attribute always reflect in this.porps as a string. But in order to use React components without changes we need to configure typecasting for attributes.

There are few propositions from Thomas Reggi on how to make this work:

This would solve the issue of [attribute casing] issue and the [required integer type] issue and would validate for the timer element and fulfill it's propTypes / initialTimeRemaining: React.PropTypes.number.isRequired.

The only problem with this would be if there was a conflict with a library that had a prop with the keyword reactive.

Would love your thoughts on this!!

<react-timer initial-time-remaining="600000">
</react-timer>

Besides the attribute json above. thinking

<react-timer initial-time-remaining="600000" initial-time-remaining-type="number">
</react-timer>

or

<react-timer initial-time-remaining="num!600000">
</react-timer>

or

<react-timer initial-time-remaining="600000" initial-time-remaining-transform="parseInt(this, 10);">
</react-timer>

or keep this logic out of the tag and put it in when registering

registerReact('react-timer', timerClass, {
  "initialTimeRemaining": function(props){
    return parseInt(props.initialTimeRemaining, 10);
  }
});

The last one there would also let you return a function as a prop, which is better then in the attributes.

Upgrade to new custom elements spec

The one we use is deprecated - new one is here: developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements.

Babel 7 supports extending built-ins correctly, so if we fully upgrade to Babel 7, we should be able to do this + publish a compiled version, so others can use us without needing to be on Babel 7.

Add example of triggering DOM events out of react component.

As we will need to communicate out of react to the rest of the world, I think the most strict forward way would be to trigger DOM events. Here is how it can be done:

    var event = new CustomEvent('change', {
      bubbles: true,
      cancelable: true
    })
    this.getDOMNode().dispatchEvent(event)

bubbles: true is required as react node is a child of our custom element.

Explicite interface for custom element

Hi

I think merging react component with dom element is kinda dirty. It would be nicer to be able to define public interface explicitly.

I would think of custom element as a public interface to react component instead of merging this two and pretend they are the same.

  // Potentially API for this could look like:
   // The last argument is the array for properties which will be exposed on the element
    document.registerReact('smart-complete', Smartcomplete, ['setState', 'somethingElse']);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.