React.js: modify render() method for all components? - javascript

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.

Related

What is the common way to encapsulate data inside Pinia stores when using the composition API?

I'm using Vue 3 with Pinia and want to use the composition API inside my Pinia stores. Given the following example store
export const useMyStore = defineStore("my", () => {
const currentState = ref(0);
return { currentState };
}
);
I can directly modify the value of currentState. I'm not sure if this is the way to go or if I should prevent this and provide some get and set functions like
export const useMyStore = defineStore("my", () => {
const currentState = ref(false);
const readonlyCurrentState = computed((): Readonly<boolean> => currentState.value);
function changeState(newState: boolean) {
currentState.value = newState;
}
return { readonlyCurrentState, changeState };
}
);
The options API solves by problem by providing getters and actions, so I thought if I should achieve the same here.
This might be an opinion based question but I would like to know if I should encapsulate the state currentState to prevent corruption by accident.
I can directly modify the value of currentState
Yes, this is intentional. As described in comparison with Vuex
mutations no longer exist. They were very often perceived as extremely verbose. They initially brought devtools integration but that is no longer an issue.
This is more technical reason but you can read more from author himself (and people who doesn't like this new "freedom") in this discussion: Add an option to disallow direct state modification from component
Generally the code in your question demonstrates the point perfectly - lot of unnecessary code just to change simple boolean value. Of course there are cases where data is more complex, you want to enforce some constraints etc. where unrestricted direct mutation can be considered dangerous. I think the pattern in your question is perfectly viable and you can find more ideas (and experimental "strict mode" support commit waiting for feedback) in the linked discussion
Personally I think a lot of the safety checks can be done with linting and especially TypeScript definitions. You get the additional safety during development without paying the price at runtime (in terms of additional code executing and being bundled&served). Great thing about Pinia is that it gives us the choice of how to approach the problem...

Dealing with class method name conflicts in JavaScript

tl;dr:
My class' method name conflicts with the same name on the class it extends from. I could just rename my class, but I'd prefer a solution which doesn't require a breaking change.
Background
I publish a library called lit-apollo, which exports a class that extends from LitElement. Users are meant to define their own customElements with my class, those elements then can use apollo graphql for data, and render using lit-html. See README for a simple example.
The Problem
LitElement's latest version exposes an instance method called update, which implementations can optionally override to control how and when the element renders. My library also has an update property, which corresponds the the update option of Apollo mutation constructors.
import gql from 'graphql-tag'
import { ApolloMutation, html } from 'lit-apollo/apollo-mutation'
const mutation = gql`
mutation($id: ID!) {
MyMutation(id: $id) {
myResponse
}
}
`
const updateFunc = (cache, response) =>
cache.writeData(doSomethingWith(cache, response))
class MutatingElement extends ApolloMutation {
constructor() {
this.mutation = mutation;
this.variables = {id: "foo"};
// here's where we break the LitElement contract
this.update = updateFunc;
}
render() {
return html`<div>${this.data.myResponse}</div>`
}
}
customElements.define('mutating-element', MutatingElement)
These two methods, clearly are conflicting.
The Question
I know I could just issue a breaking change to lit-apollo which renames it's own update method to onUpdate or some similar, but how might I address this problem without breaking my classes' APIs and thus requiring a major version?
I've though of checking the first argument to see if it's an instance of ApolloCache, then routing the args to super.update as needed, but I think that would break the LitElement contract, preventing users from implementing their own version of LitElement's update
How would you deal with this situation?
I know I could just issue a breaking change to lit-apollo, but how might I address this problem without breaking my classes' APIs?
You cannot, really. But it's not your fault:
LitElement's latest version exposes an instance method called update
That was the breaking change. So if you update your lit-element dependency to the latest version, you have to make a major version as well. Renaming your update method for that is natural. Alternatively, keep your API and continue using the old lit-element version.

Bulk binding class methods in react

I was looking for a way to bind react class methods to this in the constructor all at once, because I got tired of typing this._anotherFunction = this._anotherFunction.bind(this) 10 times for each component.
I haven't seen anyone else posting solutions for this and thought it would be useful to share my answer.
Interested to see if anyone else has similar implementations, or if there are any issues with the way I implemented.
Given the following React class functions:
_showModal() {}
_hideModal() {}
// etc.
In the constructor I added:
// bind all of the class's methods to the class
bindClassMethods.bind(this)([
'_showModal',
'_hideModal',
// etc.
]);
Here's the re-usable util function I wrote to pull this off:
export function bindClassMethods(classMethods = []) {
if (!_.isArray(classMethods)) {
console.error(`Need to pass an array to bindClassMethods().`);
return;
}
classMethods.map(fnc => {
if (!this[fnc]) {
console.error(
`Warning: func ${fnc} is not defined! It probably has been removed from this class' methods.`
);
} else {
this[fnc] = this[fnc].bind(this);
}
});
}
I found the console logs useful for reminding myself when I forgot to remove or update a function binding.
Revisiting this issue - the previous suggestion to use arrow functions as class properties have some drawbacks, including performance implications. (Albeit an arguably negligible performance implication, but one worth noting).
New Proposed Solution:
create a new class that extends Component, e.g. ComponentAutoBind
any sub-component that extends ComponentAutoBind will automatically have its own methods bound to the class instance
exclude React lifecycle methods from binding
See this gist for proposed solution
Working codepen example
I realize that extending Component may not be best practice but my implementation is only affecting the constructor - all other Component class properties are untouched.
Here are some links for the unconvinced:
https://medium.com/#forsakenharmony/you-should-maybe-mention-that-the-arrow-function-in-the-class-body-is-just-syntactic-sugar-c7bfb3383bef
https://blog.usejournal.com/arrow-functions-are-disrupting-react-components-63662d35f97b
https://medium.freecodecamp.org/why-arrow-functions-and-bind-in-reacts-render-are-problematic-f1c08b060e36 - only discusses Render method but same principle applies to class properties

Setting a field directly on the class definition in ReactJS

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'});
}
})

What's the difference between `React.createElement(...)` and `new MyComponent()`?

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.

Categories