I am working on a project where I have to attach a few utility functions to Javascript Object object as follows:
function isEmpty(a, b) {...}
Object.prototype.isEmpty = isEmpty;
Now the problem i am facing is, since I am working with react, I am guessing the above code is also attaching the isEmpty function to the constructed Components. And this works fine as long as I don't use the native html tags i.e. div, span inside my components which is not possible. I get the following warning
Warning: Unknown prop `isEmpty` on <div> tag.
Remove these props from the element. For details, see
https://facebook.github.io/react/warnings/unknown-prop.html
when I use the native html tags. Is there any way to augment the Object object without getting this error in react?
The problem is that an object extension like this is enumerable. You need to use defineProperty
BTW: this is still a bad idea
When you write jsx tags in react it gets transpiled to objects (React elements).
So
<div id="test">
</div>
is transformed into following object -
var divElement = React.createElement("div", { id: "test" });
Now since you are attaching
function sum(a, b) {...}
Object.prototype.sum = sum;
It gets attached to every objects present.
May be you should consider providing a Util.js which will contain all utility methods and do not attach to Object prototype. Because It can cause undesired side effects.
You can import Util.js where ever you need and use those methods.
e.g. -
module.exports = {
sum(a, b) {...}
};
Problem
All JSX elements are first created as objects (WitVault explains how JSX is transpiled to plain JS that can run in the browser). React takes those objects and their properties that React supports and maps them to DOM elements. If there are properties that React does not know about, it will show a warning because it's likely a case of either "you don't know what you are doing" or "you made a typo", and therefore you won't get the behavior that you expect.
Since you edited the prototype of Object, all objects, also those created by React, get the property sum, and for primitive html elements React does not know how to map a sum property.
As Juan Mendes pointed out, extending native objects is bad practice. If you extend Object.prototype in a React project you really cant avoid the problem you are experiencing.
Solution 1: Export/Import util function
Since React comes with browserify you can instead import utility methods. This has two advantages:
You don't need to extend native objects
You clearly express where the methods used come from, be cause they have an import statement in the file where it's used.
In ES6
// util/objectSum.js
export default function objectSum(object) { ... };
// anotherFile.js
import objectSum from 'utils/objectSum.js'; // assuming utils/ is directly under the root path of your project (root path can be configured in webpack.config if you use webpack)
const object = ?; // the object you want to sum
const sum = objectSum(object);
In ES5
// util/objectSum.js
module.exports = function(object) { ... };
// anotherFile.js
var objectSum = require('utils/objectSum.js'); // assuming utils/ is directly under the root path of your project (root path can be configured in webpack.config if you use webpack)
const object = ?; // the object you want to sum
const sum = objectSum(object);
Solution 2: Using ES6 classes
In ES6 you can also create a class with the sum method. Here's an example:
class NumberList {
constructor(arrayOfNumbers) {
this.numbers = arrayOfNumbers;
this.sum = this.sum.bind(this);
}
sum() {
return this.numbers.reduce((sum, next) => sum + next, 0);
}
}
const numberList = new NumberList([1, 2, 3]);
numberList.sum(); // -> 6
Related
Here's a JavaScript object I'm working with:
const tree = {
birdhouse: {
inspectNest: () => 'eggs'
},
...
}
I want the world to have access to the inspectNest function tucked into the middle there, but for hand-wavy reasons I do not want to give my consumers the entire tree object. I was hoping I could do something like this as a named export:
export { tree.birdhouse.inspectNest as inspectNest };
However, including the dot notation to access those properties seems to be invalid syntax.
I suspect a destructuring statement like const { birdhouse: { inspectNest }} = tree; right before exporting may work, but there's another hitch - other parts of my program may redefine birdhouse or inspectNest as the app runs. When that happens, I don't know if the changed item will continue to be exported with the new value.
Any advice, or is destructuring the best approach?
(P.S. I know export const inspectNest = tree.birdhouse.inspectNest; will do the trick, but in my real app I'd need many of these assignment exports. If at all possible, I want to use the named export object to keep things syntactically cleaner.)
I've got a MyFirstPage.js file which is something like this:
renderCards() {
let icon = this.state.icon;
....
....
....
}
Which i write to render my cards inside my main render function! The point is, because it's getting too long (about 350 lines), i want to create another file and import this renderCards() AS TEXT so i can i have it in the MyFirstPage.js main render!
Please note that let icon = this.state.icon; which refer to the MyFirstPage.js state - so i have to import it AS TEXT.
I tried:
export default {
renderCards() {
....
....
....
}
}
but not working!
Any idea?
Thanks in advance!
You can define code in a string and execute it in Javascript. We can use eval() to evaluate a String as a piece of code. This approach has a security, reliability, and performance concerns because eval can essentially inject code into your script, and that code is simply determined by what String is passed to it. That said, most Babel transpiling presets use strict mode, and this limits eval to some extent by ultimately not allowing it to instantiate new variables in the surrounding scope. What it can still do, is define a function and evaluate that function and assign it to a variable in the external scope like so:
var a = 5
var b = 10
var c = eval('function abc() { return a + b }; abc;')
assert(c() === 15)
With this ability in hand, we can wrap your entire 'renderCards' function definition in a String (by way of template literal):
export default `renderCards() {
let icon = this.state.icon;
....
....
....
}; renderCards;`
Import this string (import renderCardsImport from {relative path to file} in the react component MyFirstPage.js, and in the constructor you need to do a few things:
constructor(props){
super(props)
this.state = {
icon: // whatever this is
}
let renderCards = eval(renderCardsImport)
// so you have access to the parent class object this
this.renderCards = renderCards.bind(this)
}
And now you should be able to call renderCards() in the render() method or anyplace else in the component. Phewww. That was a doozy, but learned so much about eval, strict mode, etc. Hope it helps.
EDIT: You can also use the Function constructor it appears, as in var myFunct = Function('body of function with a return statement') but you will need to remember to bind it to this as well. If you use Function, you avoid performance and security hit of eval. Strict mode takes away most of the reliability problems.
Is it a good practice to do this in ReactJS?
var Component = React.createClass({
render: function () {
return (<div></div>);
},
field: 'value', // is this safe?
method: function () {
// do something with field
}
})
Before starting to suggest that I should use this.props or this.state, for me it's not the case, because those are fields that do not affect rendering in any way directly, they just work together to control the rendering.
I would like to use the React class as I do with regular javascript 'classes'.
My main concern here is how those fields and methods are handled inside React, and if the fields are set on the instance itself or directly on the prototype, which would not be suitable at all for what I need.
I ran a quick test and it seems that the fields are set on the instance, and the methods on the prototype, which is ideal. But is this the expected and documented behavior? And is this safe for future versions?
I think it can work the way you are doing and that it's safe. However if I understand well you are proceeding data calculation/transformation directly in the view.
So I would advise that you remove this logic from the view and treat it in the model part of a mvc or mv*, in your backbone models, or in your flux store for example.
This way you won't be mixing data transformation logic and pure rendering.
I would say so, I have been using things like this for a while and have not seen any issues. For example, let's say you want a handler of some sort that you want to pass to nested components, you would create the function in this component and pass it as a prop to a child. I believe they have examples that use similar concept in the ReactJS Facebook site.
Under the hood React is just looping through the properties of the object you pass to createClass and copying them to the prototype of the Component. Primitive values like strings or numbers obviously cannot be copied by reference, so don't get shared across all instances, whereas objects, functions, arrays and so on will.
If you want to work with values that are just local to the component instance you need to use the state API. I'm not sure what you mean by "[state and props] do not affect rendering in any way directly, they just work together to control the rendering". The whole point of props and state is that they work together to generate values to be used when (re)rendering.
https://facebook.github.io/react/docs/component-api.html
A React component should only render in response to either changing props or changing state. You cannot/shouldn't trigger a re-render by mutating other fields directly.
You need to think of your component as something closer to a pure function. State and props go in at the top, and static VDOM/HTML comes out.
I would re-write your example as,
var Component = React.createClass({
getInitialState: function () {
return {field: 'value'};
},
render: function () {
var field = this.state.field;
return (<div>{field}</div>);
},
method: function () {
var field = this.state.field;
// do something with field
this.setState({field: 'anotherValue'});
}
})
Intro: I'm a bit confused with React. I've seen articles saying that React components are just functions that receive the props and render to the virtual DOM. What I see, however, is that they are full-blown stateful monsters and I have found no way to treat them like functions.
The question: Why is each usage of a React component wrapped in React.createElement? Why can't I use new MyComponent() instead? It looks pretty similar when I do it in DevTools. Why is React.createElement needed at all, given that components are created using React.createClass? It looks like redundant to me.
Edit: this looks relevant: https://gist.github.com/sebmarkbage/ae327f2eda03bf165261
Edit #2: This is related, but not a duplicate of React.Component vs React.createClass, that question asks about creating classes. I'm not asking about creating new component classes, I'm asking about creating instances (elements) of that classes.
I think I found the answer here:
In React 0.12, we're making a core change to how React.createClass(...) and JSX works.
(...)
Currently var Button = React.createClass(...) does two things. It
creates a class and a helper function to create ReactElements. It is
essentially equivalent to this:
class ButtonClass { }
function ButtonFactory(...args) { return
React.createElement(ButtonClass, ...args); }
module.exports = ButtonFactory; ```
Then you access this in the consuming component by invoking the
ButtonFactory.
var Button = require('Button');
class App { render() {
return Button({ prop: 'foo '}); // ReactElement
} }
Conceptually this is the wrong model. The source component should not
be responsible for the output of App.
There are a few problems with this:
ES6 classes can't be directly exported, they need to be wrapped.
There's no convenient way to access the actual class and it's confusing which one you're using.
Static methods are wrapped in helpers that are not real function. As a convenience.
Auto-mocking destroys the factory so there is no way to test the result of render without disabling mocking.
Factories can be wrapped by other factories that returns something different than ReactElements. Making testing and optimizations
impossible.
Languages with specialized features for object management have to defer to React instead of using the built-in features.
For debugging reasons I'd like to add following line into general render() method, so it would be executed in all components.
console.log('render' + this.constructor.displayName, this.state);
I assume you want to do this without changing any of the existing code. I played around with this and found a way to do so if you're using something like webpack or browserify to build your application and you're using React v0.13.
It's important to note that this uses private methods, reaching into React's internals, and could break at any time. That said, it might be useful for your debugging purposes.
[Update to the Update]
If you use Babel, I highly recommend checking out the React Transform plugin. This will let you do all sorts of nifty stuff to React, including wrapping (or overwriting!) render methods.
[Update]
I've found a way to do this without hacking into React.addons.Perf; the key was the module name of ReactCompositeComponent and the function name of _renderValidatedComponent—just wrap that method to inject your custom behavior.
Note you'll need to place this code before you require("react").
var ReactCompositeComponent = require("react/lib/ReactCompositeComponent");
var oldRenderValidatedComponent = ReactCompositeComponent.Mixin._renderValidatedComponent;
ReactCompositeComponent.Mixin._renderValidatedComponent = function() {
var name = this.getName();
if (name && !name.match(/^ReactDOM/)) {
console.log("render: ", this.getName(), {props: this._instance.props, state: this._instance.state});
}
return oldRenderValidatedComponent.apply(this, arguments);
}
You'll then end up with a very similar result as the old answer, below. I've added better logging of props and state, and filter out any of the built in ReactDOM* components.
[Old Answer]
I've overridden the default measure function of the performance tools, which React calls through its codebase to measure performance when using React.addons.Perf. By doing so, we're able to get the information that the default measurement strategy would normally get. Note that this breaks the normal behavior React.addons.Perf.
Add this code to the entry-point of your application (after you require React):
var ReactInjection = require("react/lib/ReactInjection");
var ReactDefaultPerf = require("react/lib/ReactDefaultPerf");
ReactDefaultPerf.start();
ReactInjection.Perf.injectMeasure(function measure(moduleName, fnName, fn) {
return function() {
if (moduleName === 'ReactCompositeComponent' && fnName === '_renderValidatedComponent') {
var name = this.getName();
if (name) {
console.log("render: ", name);
}
}
return fn.apply(this, arguments);
}
});
And you'll get the following in your console logs:
ReactElements with no names (that is, components that make up regular HTML elements like span and div) are not shown. One notable set of exceptions is button and other input elements, as React provides composite components that wrap those to help manage state. They show up as ReactDOMButton and ReactDOMInput.
React supports Mixins for such cross-cutting concerns:
https://facebook.github.io/react/docs/reusable-components.html#mixins
However, it's not permitted to define a render method in a mixin. The restrictions on each of the React lifecycle methods are in the following source:
https://github.com/facebook/react/blob/0c6bee049efb63585fb88c995de788cefc18b789/src/core/ReactCompositeComponent.js#L189
If you could assign this behaviour to one of the other steps in the component lifecycle, mixins might work for you.