How to make component instances in Reactjs - javascript

When I call ReactDom.render in init() only one g tag is created and all circles are created inside this g tag. but I want that on every ReactDom.render call a separate g tag must be created. My knowledge of components in React is that we can instantiate as many components as we want using React.createClass. How can I do this?
var Rect = React.createClass({
render: function() {
return (
React.createElement('circle', {
cx: this.props.cx,
cy: this.props.cy,
r: this.props.r,
fill: '#00ff00'
});
);
}
});
var globalArray = [];
var someFunc = function(cx, cy, r) {
var Factory = React.createClass({
render: function() {
var localArray = [];
for(var i = 0; i < 1; i++) {
localArray[i] = React.createElement(Rect, {
key: globalArray.length,
cx: cx,
cy: cy,
r: r
})
}
globalArray[globalArray.length] = localArray[0];
return (
React.createElement('g', {}, globalArray)
)
}
});
return Factory;
}
var randomNumber = function (x,y) {
return ~~(Math.floor(((Math.random()*x) + y )));
}
var obj = {
init: function() {
var element;
for(var i = 0; i < 100; i++) {
var cx = randomNumber(1200,40);
var cy = randomNumber(600,40);
var r = randomNumber(30,20);
element = someFunc(cx,cy,r);
ReactDOM.render(
React.createElement(element,{}),
document.getElementById('svg')
);
}
}
}

You'll probably find that you benefit from letting the structure of your code be defined by React components, rather than your own functions and global variables.
Generally, if you find yourself calling ReactDOM.render lots of times, then something has probably gone wrong.
Top Level Component
Rather than rendering 100 components into one element, define a top level component which is made up of 100 subcomponent instances, then render that once.
var Graphics = React.createClass({
// ...
});
ReactDOM.render(
React.createElement(Graphics, null),
document.getElementById('svg')
);
We'll create a Graphics component that we can use as a parent for the rest of the components. We only need to render this once.
State
Rather than storing your list of circles in a global array, use the state property on your new top level component. This way, whenever you update it, the component will re-render to reflect the changes.
getInitialState: function() {
// by default there are no circles (empty array)
return { circles: [] };
},
componentWillMount: function() {
var circles = [];
for(var i = 0; i < 100; i++) {
// rather than storing actual circles, we just store
// the props that are needed to create them
circles.push({
cx: randomNumber(1200, 40),
cy: randomNumber(600, 40),
r: randomNumber(30,20)
});
}
this.setState({ circles: circles });
}
getInitialState allows us to define default values for the properties on this.state and componentWillMount allows to run code just before the component is rendered in the DOM.
Render
Now that the Graphics component knows about the circles list, we have to describe the way it should be rendered.
render: function() {
var circles = this.state.circles.map(function(props) {
return React.createElement(Circle, props);
});
return React.createElement('g', null, circles);
}
This function uses map to create a Circle component using the props that we stored in this.state.circles.
Conclusion
React is most effective when you structure your components to live within one top level container component, like we've created here. Rather than ever doing any imperative actions (like looping and rendering components each time) you should look for declarative alternatives.
React wants you to tell it what you want, not how it should be done.
In this case, what you wanted was a component with a number of randomly sized and positioned circles inside it, but what you tried to do was explain to React how it should go about making that happen.
var Circle = React.createClass({
render: function() {
// ...
}
});
var Graphics = React.createClass({
getInitialState: function() {
// ...
},
componentWillMount: function() {
// ...
},
render: function() {
// ...
}
});
The resulting code should not only be shorter and easier to follow, but also easy to refactor. Without much work you could move configuration details — like the number of circles — out to being a prop of Graphics.

Related

Best practice for using scoped variables between imported functions

I am trying to create an easing function on scroll but my main function is growing rather large and I want to be able to split it up. I am creating a requestAnimationFrame function that will ease the page scroll. The big issue I am having is that the render function with the animation frame will ease the Y value and then calls the update function to update the elements. But if I split this up and import them individually I am having trouble figuring out how to pass the updated values between functions. Many other functions also rely on these updated values.
I could take an object oriented approach and create a class or a constructor function and bind this to the function but it seems like bad practice to me:
import render from './render'
import update from './update'
const controller = () => {
this.items = [];
this.event = {
delta: 0,
y: 0
}
this.render = render.bind(this)
this.update = update.bind(this)
//ect
}
I could also break it up into classes that extend each other but I would like to take a more functional approach to the situation but I am having trouble figuring out how to achieve this.
Here is a very condensed version of what I am trying to do. Codesandbox.
const controller = (container) => {
const items = [];
let aF = null;
const event = {
delta: 0,
y: 0
};
const render = () => {
const diff = event.delta - event.y;
if (Math.abs(diff) > 0.1) {
event.y = lerp(event.y, event.delta, 0.06);
aF = requestAnimationFrame(render);
} else {
stop();
}
update();
};
const start = () => {
if (!aF) aF = requestAnimationFrame(render);
};
const stop = () => {
event.y = event.delta;
cancelAnimationFrame(aF);
aF = null;
};
const update = () => {
const y = event.y;
container.style.transform = `translateY(-${y}px)`;
items.forEach((item) => {
item.style.transform = `translate(${y}px, ${y}px)`;
});
};
const addItem = (item) => items.push(item);
const removeItem = (item) => {
const idx = items.indexOf(item);
if (idx > -1) items.splice(idx, 1);
};
const onScroll = () => {
event.delta = window.scrollY;
start();
};
// and a bunch more stuff
window.addEventListener("scroll", onScroll);
return {
addItem,
removeItem
};
};
export default controller;
I would like to be able to split this up and create a more functional approach with pure functions where I can import the update and render functions. The problem is that these functions are reliant on the updated global variables. Everywhere I look it says that global variables are a sin but I don't see a way to avoid them here. I have tried to look at the source code for some large frameworks to get some insight on how they structure their projects but it is too much for me to take in at the moment.
Any Help would be appreciated. Thanks.
What you wrote in the first example is completely valid, you basically did what classes are doing under the hood (classes are just syntactic sugar almost), but there are some problems with your code:
You use an arrow function, so this becomes window actually in that context.
You should use function expression instead, and initialize your instance using new, only in this case this will work how you want.
However, there are many solutions for Dependency Injection / Plugin systems which is what you're basically looking for, I'd advise you to look around this area.
In case you want a more functional approach, you need to avoid the this keyword and you need to use pure functions. I'd advice you not to share values in a context, but simply pass the necessary dependencies to your functions.
import render from './render'
const controller = () => {
const items = []
window.addEventListener('scroll', (event) => {
start({ y: event.y })
})
const start = ({ y }) => {
// Pass what render needs
requestAnimationFrame(() => render({ container, items, y }))
}
}
Using the above:
You don't need the update function in this file, render will import it on its own.
Your functions are pure, easily testable on their own.
You don't need to create an instance by using new (it's actually more expensive).
No need for shared state across functions.
No DI/Plugin solution need to implemented/used.
In case you do want to have shared state, you won't do that without classes and/or extra tooling around.
The way I'd do this is by creating a seperate .js file, then send the information you need into that js file right at the start of your main files code. Then just use the seperate file to store all your functions, then just call them with utils.myFunction(). At least that's how I do it in node.

react - accessing DOM without breaking encapsulation

Is there a cannonical way of going about doing something like the following without breaking encapsulation?
import React, { Component, PropTypes } from 'react';
class Dashboard extends Component {
constructor(props, context) {
super(props, context);
this.setRef = ::this.setRef;
}
componentDidMount() {
const node = ReactDOM.findDOMNode(this.someRef);
const newHeight = window.innerHeight - node.offsetTop - 30;
node.style.maxHeight = `${newHeight}px`;
}
render() {
return (
<div id="some-element-id" ref={this.setRef}>
</div>
);
}
setRef(ref) {
this.someRef= ref;
}
}
ReactDOM.findDOMNode seems to be the suggested way of going about this, but this still breaks encapsulation and the documentation has a big red flag to this extent.
You should use the component "state" to set the style property of the react element, so you only access the "real" DOM node to calculate the height and then you update the state, re-rendering the component. The react component now has the same information as the real DOM node, so it shouldn't be breaking encapsulation.
Using vanilla JS to provide an example:
var Component = React.createClass({
getInitialState: function(){
return {
style: {},
}
},
componentDidMount: function(){
var node = ReactDOM.findDOMNode(this);
var newHeight = window.innerHeight - node.offsetTop - 30;
this.setState({style: {backgroundColor: '#bbb', height: newHeight.toString() + 'px' }});
},
render: function(){
return React.createElement('div', {style: this.state.style}, 'Height: ' + this.state.style.height);
},
});
You can see it running in this fiddle.
While this technically "breaks encapsulation" in the general sense, if there is no other way to do it (and in this case there is not), then using findDOMNode is your only choice and it is the correct one.
If you find yourself using it repeatedly, you should create a wrapper component to encapsulate that behavior.

Binding to getInitialState?

Is it possible to bind to the getInitialState()?
BidLists = React.createClass({
getInitialState: function(s) {
return{
filteredID: this.props.data,
currentData: s
}
},
renderBidContent: function(s) {
return(
<div>{s.support_title}</div>
);
},
render: function() {
var cards = this.props.data.slice(0,25).map( (s, i) => {
return (
<div>
{this.getInitialState.bind(this,s)} // is this even possible?
</div>
);
});
var currentData = this.state.currentData;
return(
<div>{this.renderBidContent(currentData)}</div>
);
}
});
module.exports = BidLists;
Im getting:
cannot read property 'support_title' of undefined
I seem to not binding s properly. Any help? I'm using React v0.14
Reason behind this:
In the renderBidContent() there will be another function loop. That loop will test against s.id. I can achieve this in the BidList's render() but the output is twice and I dont want that.
I'm trying to do something like this but without a click method.
You can't do this, this isn't how bind works.
maybe you're looking for something like this
BidLists = React.createClass({
getInitialState: function(s) {
return{
filteredID: this.props.data
}
},
renderBidContent: function(s) {
return(
<div>{s.support_title}</div>
);
},
render: function() {
return this.props.data.slice(0,25).map( (s, i) => {
return <div>{this.renderBidContent(s)}</div>
});
}
});
How Bind Works
Bind doesn't actually bind the old function. It creates a new function and binds context or variables to it. Bind works similar to currying.
consider this function
function multiply(x, y){
return x * y
}
and now say I want to create a multiplyTwo function using the above function (we could also create our own but lets pretend multiply is an extremely complicated function).
Method 1, currying/closure.
function multiplyAny(num) {
return function(y){
return multiply(num, y);
}
}
var multiplyTwo = multiplyAny(2);
multiplyTwo(5); //this should give you 10
Method 2 bind
var multiplyTwo = multiply.bind(null, 2);
multiplyTwo(5); //this should give you 10
bind is like a special kind of curry and creates a new function with some of the variables already set. The first variable is the context (a.k.a this keyword)

How to append multiple DOM elements to an element created with reactjs?

I've just started work with reactjs and have not done much hands-on work with it. So far I'm able to create DOM elements through reactjs using JSXTransformer.js. The problem I'm getting is, when I try to create multiple elements within a DOM element, it replaces the old elements with the new ones.
That is, if I want to create div_B, div_C and div_D in mainDiv, it just adds div_D in the mainDiv because it is create last. But I want to append all three divs in the mainDiv.
The code I'm using is following:
var props = [];
function getEle(id) {
return document.getElementById(id);
}
function setProps(ele, Css, inner, id) {
props.element = ele;
props.CssClass = Css;
props.innerText = inner;
props.id = id;
return props;
}
function createElement(properties , element){
var CreateDiv = React.createClass({
render : function(){
return <div className = {this.props.elementProps.CssClass} id={this.props.elementProps.id}>{this.props.innerText}</div>;
}
});
React.render(<CreateDiv elementProps = {properties} />, element);
}
setProps("div", "divBClass", "", "div_B");
createElement(props, getEle("mainDiv"));
setProps("div", "divCClass", "", "div_C");
createElement(props, getEle("mainDiv"));
setProps("div", "divDClass", "", "div_D");
createElement(props, getEle("mainDiv"));
Is there anything wrong with that code?
You are still thinking about your code in an imperative manner. React is based on a declarative programming paradigm.
First, think about your whole application as a React component.
var App = React.createClass({
render: function() {
return (
<div>foo</div>
);
}
})
React.render(<App />, document.body);
Now, let's first render some paragraphs:
var App = React.createClass({
render: function() {
// construct array [0, 1, 2]
var values = [];
for (var i=0; i<this.props.noDivs; i++) {
values.push(i);
}
// return <p>0</p> <p>1</p> ...
return (
<div>
{values.map(function (value) {
return <p key={value}>Value {value}</p>;
})}
</div>
);
}
})
React.render(<App noDivs={3} />, document.body);
If JSX is too much, try to compile it to Javascript. Here is a live example. I'm passing the number of paragraphs as a prop.

Handling Complex Dependencies Between Object Properties (Auto Update Dependent Properties)

I have a tree structure of objects, and their properties have very complicated dependencies on surrounding objects determined by where they are in the tree. I have hard coded a lot of these dependencies, and tried to create some sort of update loop (where if a property gets updated, based on the design, all of the properties that depend on it get updated, and in the correct order), but I want to handle it in a more generic/abstract way, instead of hard coding a bunch of update calls to different objects.
Let's say, for example, I have 1 superclass, and 3 subclasses, and then a separate container object.
Shape
properties: parentContainer, index, left, top, width, height
methods: updateLeft(), updateTop(), updateWidth(), updateHeight()
Square inherits from Shape
Triangle inherits from Shape
Circle inherits from Shape
ShapeContainer
properties: shapes
methods: addShape(shape, index), removeShape(index)
I'll give a pseudocode example update method to illustrate how these dependencies crop up:
Square.updateTop() {
var prevShape = null;
if (this.index != 0) {
prevShape = this.parentContainer.shapes[this.index - 1];
}
var nextSquareInContainer = null;
for (var i = this.index; i < this.parentContainer.shapes.length; i++) {
var shape = this.parentContainer.shapes[i];
if(shape instanceof Square) {
nextSquareInContainer = shape;
break;
}
}
var top = 0;
if (prevShape != null && nextSquareInContainer != null) {
top = prevShape.top + nextSquareInContainer.width;
} else {
top = 22;
}
this.top = top;
}
So, any square objects added to the shapeConatiner will depend on the previous shape's top value and the next square found in the container's width value for its top value.
Here is some code to set up an example shape container:
var shapeContainer = new ShapeContainer();
var triangle = new Triangle();
var circle = new Circle();
var square1 = new Square();
var square2 = new Square();
shapeContainer.addShape(triangle, 0);
shapeContainer.addShape(circle, 1);
shapeContainer.addShape(square1, 2);
shapeContainer.addShape(square2, 3);
So, I guess the crux of the issue is, if I update the above circle's top value, I want the top value of square1 to be automatically updated (because there is a one way dependency between square1's top value, and circle's top value). So one way I can do this (the way I've been doing it, in combination with some other specific knowledge of my problem domain to simplify the calls), is to add the code similar to the following to Circle's updateTop method (really it would have to be added to each shape's updateTop method):
Circle.updateTop() {
// Code to actually calculate and update Circle's top value, note this
// may depend on its own set of dependencies
var nextShape = this.parentContainer.shapes[this.index + 1];
if (nextShape instanceof Square) {
nextShape.updateTop();
}
}
This type of design is fine for a few simple dependencies between objects, but my project has dozens of types of objects with probably hundreds of dependencies between their properties. I've coded it this way, but it is very difficult to reason about when trying to add new features, or troubleshoot a bug.
Is there some sort of design pattern out there to set up dependencies between object properties, and then when one property is updated, it updates all of the properties on other objects that depend on it (which may then trigger further updating of properties that depend on the now newly updated properties)? Some sort of declarative syntax for specifying these dependencies would probably be best for readability/maintainability.
Another issue is, a property may have several dependencies, that ALL must be updated before I want that property to update itself.
I've been looking into a pub/sub type of solution, but I thought this was a complicated enough problem to reach out for help. As a side note, I'm working in javascript.
Here is the hackish solution I came up with. I create a wrapper class, that you pass in anonymous functions for getter/setter/updaters. Then you make a call of prop1.dependsOn(prop2) to declaratively set up dependencies. It involves setting up a directed acyclic graph of the dependencies between object properties, and then when a property value is updated, explicitly making a call to resolve the related dependencies using a topological sort. I didn't put much thought into efficiency, and I bet somebody could come up with a much more robust/performant solution, but I think this will do for now. Sorry for the code dump, but I thought it could be of some help to somebody trying to solve a similar problem down the road. If somebody wants to make this syntactically cleaner, be my guest.
// This is a class that will act as a wrapper for all properties
// that we want to tie to our dependency graph.
function Property(initialValue, ctx) {
// Each property will get a unique id.
this.id = (++Property.id).toString();
this.value = initialValue;
this.isUpdated = false;
this.context = ctx;
Property.dependsOn[this.id] = [];
Property.isDependedOnBy[this.id] = [];
Property.idMapping[this.id] = this;
}
// Static properties on Property function.
Property.id = 0;
Property.dependsOn = {};
Property.isDependedOnBy = {};
Property.idMapping = {};
// Calling this updates all dependencies from the node outward.
Property.resolveDependencies = function (node) {
node = node.id;
var visible = [];
// Using Depth First Search to mark visibility (only want to update dependencies that are visible).
var depthFirst = function (node) {
visible.push(node);
for (var i = 0; i < Property.isDependedOnBy[node].length; i++) {
depthFirst(Property.isDependedOnBy[node][i]);
}
};
depthFirst(node);
// Topological sort to make sure updates are done in the correct order.
var generateOrder = function (inbound) {
var noIncomingEdges = [];
for (var key in inbound) {
if (inbound.hasOwnProperty(key)) {
if (inbound[key].length === 0) {
// Only call update if visible.
if (_.indexOf(visible, key) !== -1) {
Property.idMapping[key].computeValue();
}
noIncomingEdges.push(key);
delete inbound[key];
}
}
}
for (var key in inbound) {
if (inbound.hasOwnProperty(key)) {
for (var i = 0; i < noIncomingEdges.length; i++) {
inbound[key] = _.without(inbound[key], noIncomingEdges[i]);
}
}
}
// Check if the object has anymore nodes.
for (var prop in inbound) {
if (Object.prototype.hasOwnProperty.call(inbound, prop)) {
generateOrder(inbound);
}
}
};
generateOrder(_.clone(Property.dependsOn));
};
Property.prototype.get = function () {
return this.value;
}
Property.prototype.set = function (value) {
this.value = value;
}
Property.prototype.computeValue = function () {
// Call code that updates this.value.
};
Property.prototype.dependsOn = function (prop) {
Property.dependsOn[this.id].push(prop.id);
Property.isDependedOnBy[prop.id].push(this.id);
}
function PropertyFactory(methodObject) {
var self = this;
var PropType = function (initialValue) {
Property.call(this, initialValue, self);
}
PropType.prototype = Object.create(Property.prototype);
PropType.prototype.constructor = PropType;
if (methodObject.get !== null) {
PropType.prototype.get = methodObject.get;
}
if (methodObject.set !== null) {
PropType.prototype.set = methodObject.set;
}
if (methodObject.computeValue !== null) {
PropType.prototype.computeValue = methodObject.computeValue;
}
return new PropType(methodObject.initialValue);
}
And here is an example of what setting up a property looks like:
function MyClassContainer() {
this.children = [];
this.prop = PropertyFactory.call(this, {
initialValue: 0,
get: null,
set: null,
computeValue: function () {
var self = this.context;
var updatedVal = self.children[0].prop.get() + self.children[1].prop.get();
this.set(updatedVal);
}
});
}
MyClassContainer.prototype.addChildren = function (child) {
if (this.children.length === 0 || this.children.length === 1) {
// Here is the key line. This line is setting up the dependency between
// object properties.
this.prop.dependsOn(child.prop);
}
this.children.push(child);
}
function MyClass() {
this.prop = PropertyFactory.call(this, {
initialValue: 5,
get: null,
set: null,
computeValue: null
});
}
var c = new MyClassContainer();
var c1 = new MyClass();
var c2 = new MyClass();
c.addChildren(c1);
c.addChildren(c2);
And here is an example of actually updating a property once all of this infrastructure is set up:
c1.prop.set(3);
Property.resolveDependencies(c1.prop);
I feel like this is a pretty powerful pattern for programs that require really complicated dependencies. Knockout JS has something similar, with computedObservables (and they use a wrapper in a similar fashion), but you can only tie the computed property to other properties on the same object from what I can tell. The above pattern allows you to arbitrarily associate object properties as dependencies.

Categories

Resources