Similar React components with different logic - javascript

Desired outcome
I want to have a reusable component, consisting of a title and a number.
The title is simple enough, as it is just a string, which I can send as a prop using
<ReactDOM.render(<MyComponent title="floorp"/>, docu...);
But the number will be calculated in a different way for each instance of the component.
Question
Is there a way to cleanly write such a component?
My thoughts
I'm thinking that inside the component, I would have a
calculate: function () {
switch(title) {
case 'floorp':
//...
case 'not_floorp':
//...
}
}
I will be needing at least six different cases, possibly (actually, probably) more later, so scalability is an important factor.
This is the way to do it, or am I overlooking something?

I would seperate the calculator logic from your component and just let the component worry about rendering views. Just make the component dependent on a calculator so it scales and is reusable.
Here is an example that has all your calculators in one file, but you certainly don't have to do this way, each calculator could be in its own file if you wanted (hence this approach gives you flexibility).
calculators.js
var CALCULATORS = {
add: function(a,b){ return a + b; },
subtract: function(a,b){ return a - b; },
suprise: function(){ return Math.random(); }
};
Then you would just pass a calculator to your component like so:
<ReactDOM.render(<MyComponent title="floorp" calculator={CALCULATORS.add}/>, docu...);
Inside your component:
calculate: function () {
//This will equate to '30' since we passed it the 'add' calculator above
return this.props.calculator.call(this, 10, 20);
}

I think I disagree slightly with #jennas's approach. I think the component should only receive simple strings/integers for title and number, and the calculation logic should be done in a parent component or container somewhere above in the hierarchy.
Consider this, you're writing a different function for every type of calculation anyway, why bother passing it into the component? Why not just make the calculation outside of the component and pass in a plain value?
This achieves two goals:
You have one more dumb component, which is good because:
Dumb components are simpler and easier to test and share
Consumers of the component don't need to know how it works, they just need to know what it needs. For example, what values will the function need to make calculations on?
You congregate your app logic in more central places, where it can be more easily managed
However, to adequately answer the question I think it would be helpful to know:
What is this component?
What context is it in?
Is it a todo item with a "due in 2 days" notice?
Is it a draggable whose x and y coordinates are calculated on mouse move?
Who else will be using it? Just you? Other people?

Related

Why is ReactJS not performant like raw JS for simple, stupid proof of concept?

Years ago, I heard about a nice 404 page and implemented a copy.
In working with ReactJS, the same idea is intended to be implemented, but it is slow and jerky in its motion, and after a while Chrome gives it an "unresponsive script" warning, pinpointed to line 1226, "var position = index % repeated_tokens.length;", with a few hundred milliseconds' delay between successive calls. The script consistently goes beyond an unresponsive page to bringing a computer to its knees.
Obviously, they're not the same implementation, although the ReactJS version is derived from the "I am not using jQuery yet" version. But beyond that, why is it bogging? Am I making a deep stack of closures? Why is the ReactJS port slower than the bare JavaScript original?
In both cases the work is driven by minor arithmetic and there is nothing particularly interesting about the code or what it is doing.
--UPDATE--
I see I've gotten a downvote and three close votes...
This appears to have gotten response from people who are (a) saying something sensible and (b) contradicting what Pete Hunt and other people have said.
What is claimed, among other things, by Hunt and Facebook's ReactJS video, is that the synthetic DOM is lightning-fast, enough to pull 60 frames per second on a non-JIT iPhone. And they've left an optimization hook to say "Ignore this portion of the DOM in your fast comparison," which I've used elsewhere to disclaim jurisdiction of a non-ReactJS widget.
#EdBallot's suggestion that it's "an extreme (and unnecessary) amount of work to create and render an element, and do a single document.getElementById. Now I'm factoring out that last bit; DOM manipulation is slow. But the responses here are hard to reconcile with what Facebook has been saying about performant ReactJS. There is a "Crunch all you want; we'll make more" attitude about (theoretically) throwing away the DOM and making a new one, which is lightning-fast because it's done in memory without talking to the real DOM.
In many cases I want something more surgical and can attempt to change the smallest area possible, but the letter and spirit of ReactJS videos I've seen is squarely in the spirit of "Crunch all you want; we'll make more."
Off to trying suggested edits to see what they will do...
I didn't look at all the code, but for starters, this is rather inefficient
var update = function() {
React.render(React.createElement(Pragmatometer, null),
document.getElementById('main'));
for(var instance in CKEDITOR.instances) {
CKEDITOR.instances[instance].updateElement();
}
save('Scratchpad', document.getElementById('scratchpad').value);
};
var update_interval = setInterval(update, 100);
It is doing an extreme (and unnecessary) amount of work and it is being done every 100ms. Among other things, it is calling:
React.createElement
React.render
document.getElementById
Probably with the amount of JS objects being created and released, your update function plus garbage collection is taking longer than 100ms, effectively taking the computer to its knees and lower.
At the very least, I'd recommend caching as much as you can outside of the interval callback. Also no need to call React.render multiple times. Once it is rendered into the dom, use setProps or forceUpdate to cause it to render changes.
Here's an example of what I mean:
var mainComponent = React.createElement(Pragmatometer, null);
React.render(mainComponent,
document.getElementById('main'));
var update = function() {
mainComponent.forceUpdate();
for(var instance in CKEDITOR.instances) {
CKEDITOR.instances[instance].updateElement();
}
save('Scratchpad', document.getElementById('scratchpad').value);
};
var update_interval = setInterval(update, 100);
Beyond that, I'd also recommend moving the setInterval code into whatever React component is rendering that stuff (the Scratchpad component?).
A final comment: one of the downsides of using setInterval is that it doesn't wait for the callback function to complete before queuing up the next callback. An alternative is to use setTimeout with the callback setting up the next setTimeout, like this
var update = function() {
// do some stuff
// update is done to setup the next timeout
setTimeout(update, 100);
};
setTimeout(update, 100);

How to avoid react rerendering whole tree on simple className changes?

I have some nested React components, the inner of which is a large svg graph with hundreds if not thousands line and rect elements. To enable some application-wide behaviour and appearance changes I thought to change the className of the uppermost component. The problem is, that the whole application re-renders if I do that.
I understand that this behaviour is somewhat intended, in the sense of a unidirectional render flow, but I thought React would be smarter about what it can reuse and keep DOM changes to a minimum.
Minimal example here: https://jsbin.com/rabofewawu/1/edit?html,js,output
As you can see, the line pattern in the SVG changes every time you press 'here' though I only want the background color to change.
A similar, but more extreme, example arises when I try to zoom and pan the svg by changing the transform property of an inner g element. Using d3, I simply change the attribute. With react, my render function gets called, the updated state results in an updated transform property, and the whole group is re-renderd from scratch instead of changing the DOM attribute.
Am I missing something? Whats the React way to achieve what I'm trying to do?
Take this simple harmless looking render function:
render(){
return <div>{Date.now()}</div>;
}
In React's mental model this would always display the current number every millisecond. React conceptually updates infinite times per second. The inputs of render here are everything in the world, but we happen to just use the clock. Given the same world, we get the same output from render and thus it's idempotent.
Well crap... we don't have infinitely fast computers so we need to compromise. Instead of render's input being everything we restrict it to state and props (and context).
In this restricted setting use of Math.random or Date.now breaks the rules. If you need to use the output of these, it must go through state or props first. How would this look? Well we can use a deterministic random number generator and store the seed in state. Here's a modified version of your component doing that:
var MyComponent = React.createClass({
displayName:"MyComponent",
getInitialState(){
return {
seed: Math.floor(Math.random()*0xffffff)
};
},
render: function() {
// make a random number generator with the given seed
var rng = new Chance(this.state.seed);
function random(x){
return rng.floating({min: 0, max: x, fixed: 7})
}
var s=100, lines = [];
for (var i=0; i<100; i++) {
var line = { x1: random(s), y1: random(s), x2: random(s), y2: random(s) };
lines.push(React.createElement("line", line));
}
return React.createElement("svg", { height: s, width: s}, lines);
}
});
Rendering less than infinite times per second, and rendering only certain components is an optimization. Optimizations should not affect the behavior of the program.
If you wanted different random numbers, you could set the seed to a different random number. Using real Math.random() here is okay because state is the result or i/o operations in your code, and you're invoking this setState in response to some other i/o (e.g. a click handler).
Time is similar; if you want the current time to be rendered, use a setTimeout and setState with the current time. You can then display it how you like in render, including passing it down to other components and doing any kind of math on it you like.
You can control this with the component's shouldComponentUpdate function. By default this always returns true (so the component will always re-render).
There's a little bit of documentation for this function here https://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate

Is there a way to count objects currently displayed on the stage using createJS / easelJS?

I'm currently creating a program using the createJS suite and have hit a roadblock. I'm "spawning" items on the stage however I wondered if there was a way to count how many currently exist on the stage.
So, for example:
if (spawnedItemCount <= 1) {
spawnItem();
}
spawnedItemCount would return the amount of a particular object that is currently being displayed on the stage. If there is only 1 (or less) of these objects then run the spawnItem function. Is this possible at all?
Thank you.
You, are looking for getNumChildren()
http://createjs.com/Docs/EaselJS/classes/Container.html#method_getNumChildren
Every container has this method, but it will only return the Number of direct children, no children of children, for that you will have to create a recursive call.

Unittesting dependant tests

I've searched the site and some literature and couldn't get to a clear answer. I'm trying to learn unittesting while constructing a new webpage that simply works as a whiteboard to which you can add post-its.
I have a Canvas object which represents the whiteboard, and a ticket object to represent the post-its. I have (for now) global function to retrieve the one and only canvas, which i test like this:
this.testRetrieveCanvas = function()
{
var canvas = getCanvas();
this.assertTrue( canvas != null );
}
this.testCanvasType = function()
{
var canvas = getCanvas();
this.assertTrue( canvas instanceof Canvas );
}
this.testIfCanvasIsReused = function()
{
var canvas = getCanvas();
this.assertEquals( canvas, getCanvas() );
}
So, i test for three things:
Does the method return a canvas?
Is it an acutal canvas?
Does the method give me the same canvas always?
No problems so far. But a little later, i'm testing "adding the ticket to the canvas":
this.testAddTicketToCanvas = function()
{
var ticket = factory.createTicket("yellow");
var canvas = getCanvas();
canvas.addTicket( ticket );
this.assertTrue( canvas.contains( ticket ) );
};
As you can see, i'm using the getCanvas() function inside my test. Is this a dependent test now? I mean, the first three tests have to pass, if i want this test to be able to run without doubts. If it is dependent, how would i fix this?
Strictly speaking, you should override getCanvas() to return a preconstructed (i.e. the original constructor isn't called) partial mock of canvas. With that said, if canvas' constructor is an empty function and the getCanvas method has no business logic involved, you shouldn't have a problem.
I'd be more wary of the last two statements used together. canvas.addTicket( ticket ); is ok, since it's the function being tested. but then you're asserting that you have added the ticket using a method from the same object. What if this method isn't implemented yet, or returns false, or worse true? What if your addTicket method has secondary effects that could make it add the ticket to the list but change a flag that makes contains() throw an exception, or return false, or true? What if contains has certain business logic that makes it return false for the ticket your sending it, but true for the same ticket in production environment (i.e. your test ticket hasn't been properly initialized, is missing a state, has been marked as excluded from the environment's business flow), what if it has no logic now, but two months into the project the logic changes so that your test fails but all else works (a new state is added, and objects without this state are deemed non existant, except for clients a, b and c). I could go on.
My point is without the code we can't specifically answer your question, only give you pointers and general answers like the one above. If you really don't want to post code, then take into consideration all of these scenarios, and all the other scenarios you can think of, and if under these scenarios testing your code this way won't break code nor tests now and for the foreseeable future, then you're ok.
The short answer to your question is, no.
If your code does not violate any unit testing
principles as long as you adhere to these rules.
Usually, with those questions there is a huge discussion about mocking certain parts of your tests e.g. your getCanvas() function. I agree that there is reason behind this discussion and that if you want to proceed with testing or TDD in general you should dive deeper into this topic. (Please refer to this excellent article from Martin Fowler).
However, for the question if this is a valid unit test, I think it is not relevant as long as you adhere to the unit test rules.

Which is better for massive amounts of Variables

So after reading massively into JS the past few days (I'm just starting with it) I have come upon 3 viable options for my project, which is a game btw. There will be about 200 items in my game, with a total of about 30 stats/properties. But since the program has to know that a Sword doesn't give Spell damage than I have to give each item all those 30 properties even if 90% will be 0. So here is what I came up with, any advice is greatly appreciated:
var prop, obj = { Item1: "Boots", Item2: "Gloves", Item3: "Sword", Item4: "Cape" };
// Pretty sure I'd have to remake this somehow to put properties in there, this might not be as useable
function Item(hp, mp, dmg, spd, luk) {
this.hp = hp;
this.mp = mp;
this.dmg = dmg;
this.spd = spd;
this.luk = luk;
}
var BigSword = new Item(0, 0, 50, 20, 10)
//I think this might be my best option so there are as few variables as possible? or does that not matter as much? I figured with this option whenever I update totalHp and totalMp and totalDmg etc in the updating function I can create a local variable called theItem = BigSword and then just do theItem.hp, theItem.mp and that way it doesn't have to search much for the variable and should run relatively faster? Sorry for the newbie question but does JS even have local variables in functions? As in if I do var theItem = whatever, then theItem will be null outside the function?
itemNames = [ "Boots", "Gloves", "Sword", "Cape" ];
itemHp = [ 0, 0, 0, 50 ];
itemMp = [ 0, 30, 0, 0 ];
itemDmg = [ 0, 0, 50, 0 ];
itemSpd = [ 50, 0, 0, 0];
//This is the other option that I came up with, a little uglier but if it's faster or better to just use arrays I don't mind that much.
But please keep in mind, like I said, 200~ish total items, each item will have around 30 properties. What do you think?
Thanks a lot for reading and for any information provided, it's greatly appreciated! Thanks in advance
Your best bet is to have a constructor, as you have in your first example. However, I'd change a few things. First of all, for the constructor, you're simply passing in a lot of numbers. This is bad, because it's very easy to forget the order in which your numbers go. A better way to go would be to pass in an object to your constructor, so you can label each property:
function Item(props) {
this.hp = props.hp;
this.mp = props.mp;
this.dmg = props.dmg;
this.spd = props.spd;
this.luk = props.luk;
}
var BigSword = new Item({
hp: 0,
mp: 0, 5
dmg: 0,
spd: 20,
luk: 10
});
This is nice, but still a pain, because you have all sorts of different items, and as you said, you'd have to define 30 properties for each item, which gets both messy and time-consuming. At this point you get into the realm of more complex object oriented stuff. From here you have basically two options: inheritance and construction.
Inheritance
Using object inheritance to properly define objects is one probably the more common of the two, and is fairly straightforward. In essence, you have a "class" of object, and then sub-classes of each class. So you might have Magical Items and Non-Magical Items as two classes. If you had a magic belt, perhaps, you'd want to make it inherit the properties of the Magical Item class. On the other hand, if you had a basic un-charmed sword, you'd want to make it inherit from the Non-Magical Item class.
But say you have magical scrolls and magical potions. Those are both magical items, right? So you'd want to make a class called Potions and a class called Scrolls, which both inherit from the Magical Item class. Then you might have certain types of potions. Healing potions, maybe--"Magical Potion of Heal Allies" and "Magical Potion Of Heal Self". So you'd need different classes for those--and so on and so forth.
Inheritance in Javascript can be done a number of different ways and it's possible to get very in-depth, so I'm not going to discuss it in my post. Here are a few useful links to get you started with inheritance, though, if you want to go that route:
David Crockford on Inheritance
MDN on Inheritance and MDN Inheritance Revisted
Alex Sexton on Inheritance
Composition
Composition is a concept that borrows heavily from other loosely-typed languages, such as Python, which employs it quite extensively. The basic idea behind composition is that you have a base object class, and whenever you need a certain specific type of object, you add behaviors, or members, to that base class. This isn't quite as well-known as inheritance, but it's definitely the one I prefer.
In Javascript, it works by having a base constructor, Item, and then having other constructors which add the necessary behaviors to your Item. Let's take a look at the example I used for composition--creating a Magical Potion of Heal Allies. You have several distinct properties here: Magical Item, Potion, Healing. With inheritance you'd do something like Item -> Magical -> Potion -> Healing Potions -> Allies. However, with composition, you can add several behaviors to a base Item class, without having to set up a huge prototype chain: Affect Allies, Potion, Healing.
Since composition is a bit simpler than inheritance (and, I'll admit it, I'm biased) I'll set up a quick example in psuedo-code:
// base item class
function Item(props) {
this.someProperty = props.someProperty;
this.someOtherProperty = props.someOtherProperty;
}
// potion behavior
function Potion(props) {
this.amount = props.amount; // IDK what amount means, just some random potion property
this.color = "green";
}
// healing behavior
function Healing(props) {
this.amountToHeal = props.amountToHeal;
this.cooldown = props.cooldown;
}
// behavior to affect allies
function AffectAllies(props) {
this.allies = props.allies;
for(ally in this.allies) {
this.allies[ally].affect(props.affact);
}
}
// at this point we have the various behaviors set up and we can create the Magical Potion of Heal Allies constructor:
function MassHealingPotion(props) {
var self = this;
this.amount = props.amount;
this.potion = new Potion(props);
this.effect = new Healing(props);
this.AffectAllies = new AffectAllies({
affect: function(ally) {
self.potion.affect;
},
allies: player.allies;
});
}
// you can now create a mass healing potion:
var potion = new MassHealingPotion({
weight: 50,
someProp: "someValue",
someOtherProp: "someOtherValue"
// and so on and so forth with whatever properties you want the potion to have
});
Well, that turned out a bit longer than I expected and probably contains proportionally more errors than I'd like, but I hope that conveys the basic idea of composition.
Now, simpler object creation isn't even the best part of composition, which is that you can re-use different behaviors. Consider you have a magical sword that whenever you make an attack it heals your allies. All you have to do is give it the Healing member and the AffectAllies member and boom--magical sword of heal buddies. With inheritance, you'd have to create a Swords class in the Magical Items class (and then you'd have two different Swords classes-one for Non Magical Swords and the other for Magical Swords! Ew!), then extend that with Healing Swords, and so on and so forth.
You can go either way, inheritance or object composition. Both are equally effective for what you want to achieve.
But what about performance?
200 items with 30 properties a piece? Not a problem. Stick them all in one giant array/object, boil 'em, mash 'em, stick 'em in a stew. The browser don't care. Your best bet is probably an object, though:
var itemList = {
bigSword: new BigSword(...);
smallSword: new SmallSword(...);
}
I would prefer Objects. I would start with a simple, generic "Item" and then build specific items by "inherit" from the generic one with prototyping.
That probably may reduce code redundancy and increase maintainability dramatically.
Speedwise I don't have a clue but I think iterating through 200*30 array values isn't also that fast.
You need to do a serious design effort first. If you just start coding (as many developers do), you will only get so far and realise your design has a fundamental flaw and the application needs to be rewritten. That doesn't matter in the early stages, but once you've written a few hundred lines of code it gets quite tedious.
By all means write code to prototype things, but all you should expect to get from it are design hints, the code should be thrown away.
Your items list (or catalogue) should be an object with methods to add, remove and update items. Each player (character, avatar, whatever) then just needs a list of their items and their state. There should be a global update damage or health or whatever function that gets items of a specific type and applies them to players of a specific type (e.g. weapons damage enemies, food revives the player and friends, etc.).
Only once you know all the players and items and their attributes and actions, you can start to design the interfaces and methods, and finally write some code.
Keep your eye on the objective, which should be to develop an engaging game, not to just write a few thousand lines of code.

Categories