Can I mount a React component automatically (like angularjs' direct)? - angularjs

Update 3
I have been using AngularJs for several years, and want to try ReactJs. With AngularJs I can define a directive and put the node inside the HTML DOM, like this:
<!DOCTYPE html>
<html>
<head>
....
</head>
<body>
<!--helloWorld is a directive-->
<hello-world></hello-world>
</body>
</html>
However, in React, to my knowledge, one needs to call
React.render(
<HelloWorld />,
targetElement);
to mount the component. Is there a way in React to mount the component automatically?
I have created a codepen here to show my idea as an experiment. The main piece of code is as below:
function R() {
var className = 'Hello';
if (window[className]) {
var elements = document.getElementsByTagName(className);
angular.forEach(elements, function (element) {
React.render(React.createElement(window[className]), element);
});
}
setTimeout(R, 50);
}
Every 50ms, we will check if the Class has been created. If it is I will render it under the element in the real DOM node <Hello> </Hello>.
*Warning: this code works only in trivial cases.
The JSX code to create react class:
<script type="text/jsx">
var Hello = React.createClass({
render: function() {
return <h1>Hello</h1>;
}
});
// React.render could be called here, like below:
React.render(
<Hello> </Hello>,
document.getElementById('example')
);
// but I want the <Hello> </Hello> stays in the html DOM. So that I don't
// repeat calling React.render. The first piece of code, will find the
// node and render the ReactClass there.
</script>
In html:
<Hello> </Hello>
I don't want to use setTimeout, but don't know if there are other approaches.

I don't really understand what you're trying to do but you could use EventEmitter
to bind a listener when you class is created.
var EventEmitter = require('events').EventEmitter;
var assign = require('object-assign');
var myclassfactory= assign({}, EventEmitter.prototype, {
addListener: function(myclassName,callback) {
this.on(myclass,callback);
},
createClass: function(myclassName) {
//create your class ===>>>>>> React.CreateClass !!
this.emit(myclassName);
}
});
Then add a listener in your view:
myclassfactory.addListener(myclassName, render(myclassName));
var render = function(myclassName) {
React.render(myclassName, myelement);
}

Related

How to pass Polymer object properties when rendering from React components (no JSX)

I've found this answer showing how to bind object properties to attributes specified in the HTML tag for a Polymer component.
What I want to do is to pass those same properties when rendering from a React component like so:
var myComponent = React.createClass({
componentDidMount: function(){
},
render: function() {
var activities = this.props.activities.map(function(activity) {
return React.createElement("activity-tracker", {variable: {"title": activity.title, "description": activity.description}});
});
return React.createElement("div", null, activities);
}
};
I'm trying to avoid using JSX altogether.
activity-tracker is the Polymer component
<dom-module id="activity-tracker">
<template>
<p>{{variable.title}}</p>
<p>{{variable.description}}</p>
</template>
<script>
Polymer({
is:"activity-tracker",
properties:{
variable:{
type:Object,
value: function(){return {};}
}
}
});
</script>
</dom-module>
Update
I’ve found a this post where they pass the values using a ref function but I’m not sure if that is the best way to do it.
...
return React.createElement("activity-tracker", {
ref: function (ref) {
if (!ref) return;
ref.variable = {title: activity.title, description: activity.description};
}
});
...
I don't think this is possible because of React's virtual DOM.

on updating props in ReactJS

There's a number of posts in SO related to not being able to update props in ReactJS. E.g. this one.
In the referenced post the explanation is given that this is part of the ReactJS philosophy, helps in debugging, etc. Also this answer (in the same thread) shows how it's done inside ReactJS (by using Object.freeze).
I am ReactJS newbie and so put together this small example to try to see what happens when one attempts to modify props (also in jsfiddle)
<!doctype html>
<html>
<body>
<div id='react-app'></div>
<script src="https://cdn.jsdelivr.net/react/0.14.0-rc1/react.js"></script>
<script src="https://cdn.jsdelivr.net/react/0.14.0-rc1/react-dom.js"></script>
<script type='text/javascript'>
var rce = React.createElement.bind(React);
var TodoList = React.createClass({
propTypes: {
todos: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
},
render: function() {
var props = this.props;
var todos = this.props.todos;
var self = this;
return rce('div', {}
,rce('ul', {}
, (function(){
return todos.map(function(x, i) {
return rce('li', {key: i}, x);
});})())
,rce('button', {onClick: function() {
// delete props.todos; // fails
// props.todos = []; // fails
todos.splice(0, todos.length); // succeeds
console.log(todos);
self.forceUpdate();
}}, 'clear all'));
}
});
var todoList = rce(TodoList, {todos: ['walk dog', 'feed cat', 'water flowers']});
ReactDOM.render(todoList, document.getElementById('react-app'));
</script>
</body>
</html>
I am sure the above code violates many good practices but it's apparently easy to modify props. I understand this is because Object.freeze is called on the props object and not on its properties as well and so as it is, it does not "freeze" against splicing (or changing a props' object's attribute values). My questions are:
how should the above code be written to respect ReactJS philosophy?
if not modifying props is such a core tenet of ReactJS's philosophy why isn't it enforced more strictly?
According to react philosophy, you are advised not to edit the props because it is not how react is design (Thinking in React).
You should use the state of your view which conforms to changes and you may pass some data from state to props.
see the correct example of your code:
var TodoList = React.createClass({
propTypes: {
todos: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
onSomeAction: React.PropTypes.func.isRequired
},
render: function() {
var todos = this.props.todos;
var self = this;
return rce('div', {}
,rce('ul', {}
, (function(){
return todos.map(function(x, i) {
return rce('li', {key: i}, x);
});})())
,rce('button', {onClick: this.props.onSomeAction});
}
});
The logic of what to do and how, when the button is clicked, should be place in the view's owner, unless you manage you own state in the current view.
Also,
There is a concept which defines dummy views and smart views.
in short, dummy views are such that they only get properties and shows some data on screen, but they do not interact with other objects or able to change the view's state.
However, smart views are such that they are interacts with others, responding to events and etc. as well as able to change their state and pass any other data to its children.
Hope its clearer for you now...

Async loading script within react component

I have a problem loading external script within react JSX
<script>
(function(d) {
var config = {
kitId: 1234567,
scriptTimeout: 3000,
async: true
},
h=d.documentElement,t=setTimeout(function(){h.className=h.className.replace(/\bwf-loading\b/g,"")+" wf-inactive";},config.scriptTimeout),tk=d.createElement("script"),f=false,s=d.getElementsByTagName("script")[0],a;h.className+=" wf-loading";tk.src='https://use.typekit.net/'+config.kitId+'.js';tk.async=true;tk.onload=tk.onreadystatechange=function(){a=this.readyState;if(f||a&&a!="complete"&&a!="loaded")return;f=true;clearTimeout(t);try{Typekit.load(config)}catch(e){}};s.parentNode.insertBefore(tk,s)
})(document);
</script>
and here is my render function where i would like to have the javascipt load asyn, it's super easy in a html file, however i am stunned within a react component on how to do acheive. (If i can avoid install another external module would be best). Many thanks
render() {
return (
<head>
//Script to load asyn
</head>
)
}
My server renders the initial HTML document, so I couldn't just insert it into the head as #mjhm suggested. Using #bluebill1049's answer I was able to make use of ES6 Template Strings and react's dangerouslySetInnerHTML
import React, {Component} from 'react';
export default class Html extends Component {
render() {
const loadTypeKit = `
(function(d) {
var config = {
kitId: 'YOUR_KITID',
scriptTimeout: 3000,
async: true
},
h=d.documentElement,
t=setTimeout(function() {
h.className=h.className.replace(/wf-loading/g,"")+" wf-inactive";
},config.scriptTimeout),
tk=d.createElement("script"),
f=false,
s=d.getElementsByTagName("script")[0],
a;
h.className+=" wf-loading";
tk.src='https://use.typekit.net/'+config.kitId+'.js';
tk.async=true;
tk.onload=tk.onreadystatechange=function() {
a=this.readyState;
if(f||a&&a!="complete"&&a!="loaded") return;
f=true;
clearTimeout(t);
try{
Typekit.load(config)
} catch(e){ }
};
s.parentNode.insertBefore(tk,s);
})(document);
`;
return (
<html>
<head>
<script dangerouslySetInnerHTML={{__html: loadTypeKit}} />
</head>
<body>
...
</body>
</html>
);
}
}
dangerouslySetInnerHTML is the solution i have found.
https://facebook.github.io/react/tips/dangerously-set-inner-html.html
This allow you to have script tag and insert JavaScript in side of jSX.

how to access methods of an instance of a React component?

Is there a way to call the methods that a React component defines internally?
I understand generally we want to be passing values around declaratively with props/data etc. However I am using some component libraries that have internal useful methods.
eg
var field = <AutoComplete/>;
field.setValue("ready"); // doesn't work
eg this method
https://github.com/callemall/material-ui/blob/master/src/auto-complete.jsx#L244-L248
in material-ui AutoComplete component.
You can not do this with virtual dom, since virtual dom is just a description of components to be created(actual component instance will be created or updated only when rendering).
But you can access component instances inside your react component after rendering using refs:
var Test = React.createClass({
getInitialState: function(){
return {value:0};
},
setValue: function(value){
this.setState({value:value});
},
render: function() {
return <div>Value {this.state.value}</div>;
}
});
var App = React.createClass({
render: function() {
return <div>
<Test ref="test"/>
<button onClick={()=>this.refs.test.setValue(1)}>1</button>
<button onClick={()=>this.refs.test.setValue(2)}>2</button>
<button onClick={()=>this.refs.test.setValue(3)}>3</button>
</div>;
}
});
var mountNode = document.getElementById('app');
ReactDOM.render(<App name="John" />, mountNode);
jsbin with code above: http://jsbin.com/kitehujaje/1/edit?js,output

Marionette layout view -- why is a template necessary

In spite of reading the marionette docs several times over, I am still not able to fully comprehend some aspects of it correctly.
I am creating a layout view 'AppLayout' as below:
var AppLayoutView = Marionette.LayoutView.extend({
regions: {
headerRegion: "#ecp_header",
bodyRegion: "#ecp_layout_region"
},
...
The html snippet for my app is having the two dom nodes for above defined regions:
<div id="ecp_header"></div>
<div class="container" id="ecp_layout_region">
<div class="row" id="ecp_body">
...
in app.js, my calling code is like this..
ECPApp.on('start', function() {
require(['controller_cp', 'header_view'], function(ControllerCP, HeaderView) {
console.log("On start event executing...");
// create a event aggregator vent object and attach to app.
ECPApp.vent = new Backbone.Wreqr.EventAggregator();
var appLayoutView = new AppLayoutView();
appLayoutView.render();
//appLayoutView.showLayout();
//$('div.toolbar > ul > li:first > a').tab('show');
if (Backbone.history) Backbone.history.start();
});
This gives me error Cannot render the template since it is null or undefined.
I thought that the default render() behavior of layout always looks for a template, so I rolled out my own version of render, as below:
render: function() {
var $self = this;
/* if no session exists, show welcome page */
var promise = ECPApp.request('entities:session');
promise.done(function(data) {
if (data.result==0) {
console.log('Valid session exists. Showing home page...!');
$self.showHome();
} else {
console.log('No session exists. Showing welcome page...!');
$self.showWelcome();
}
}).fail(function(status) {
console.log('No session exists. Showing welcome page...!');
$self.showWelcome();
});
return $self;
},
showWelcome: function() {
var self = this;
require(['header_view', 'welcome_view'],
function(HeaderView, WelcomeView) {
var headerView = new HeaderView();
var welcomeView = new WelcomeView();
self.bodyRegion.show(welcomeView);
});
}
This time, I get another error saying, An "el" #ecp_layout_region must exist in DOM. However I am sure that the element is existing in the DOM, as I can see it by checking in the debug console window. Running $('#ecp_layout_region') shows a valid element.
Marionette layout view is pretty confusing. Going forward I need multiple nested views. I am stuck here.
How is your template located? Is your template wrapped by <script type = “text/template”> tag?
It may look like this:
Inside your html, in head section:
<script type = “text/template” id="yourLayout">
<div id="ecp_header"></div>
<div class="container" id="ecp_layout_region">...</div>
</script>
And in Layout definition:
var AppLayoutView = Marionette.LayoutView.extend({
template: '#yourLayout'
...
});

Resources