I am looking at extending a component in React. I have read this in the docs that it is recommended to use composition instead of inheritance to reuse code between components.
Based on this I implemented the following and believed that this is the correct approach. (It does work). See code below.
class ComponentExtension extends Component {
//*** extras to extend the original component here ***//
//*** omitted for the purposes of this question ***//
render() {
return <OriginalComponent />;
}
}
However, then upon further research I came across Higher Order Components in the docs. What is the difference between the two approaches? Should you use one approach in one instance and another approach in a separate instance? Are they even solving an identical problem?
If you want to extend the component, you should do something like this:
class ComponentExtension extends OriginalComponent {
//*** extras ***//
}
And if you use HOC, you just wrap one component into the other.
Related
This is how the components are arranged
class A extends React.Component {
state = {mode: 'create'}
abstract getRows() {
}
abstract getTitle(){
}
}
class B extends A {
getTitle(){
if(this.state.mode === 'create'){ return 'New';}
else {return 'Existing'};
}
}
And in test case:
jest.mock('componentAFile');
component = shallow(<B />);
component.setState({mode: 'create'});
expect(component.instance().getTitle()).toBe('New');
component.setState({mode: 'edit'});
expect(component.instance().getTitle()).toBe('Existing');
But state mode says undefined. What is the correct way to do this? How do we test such classes inheriting from base classes?
First, better don't use that inheritance approach. Composition is so many way better that I even cannot recall any cons of inheritance.
Second, using .setState you are literally accessing internal aka private data. Or in other words "implementation details". There are so many cases when it breaks suddenly(renamed property in this.state, refactored class component into function, decompose one component into several etc etc), that there are no reason to use it.
Third, the same is with testing internal methods. Again, it's implementation details, for both name, arguments ordering, result returned. And moreover, let's assume you never use that function in what component renders in render(). So test will pass, but component might be broken.
What can you do instead?
Provide prop(shallow(<B prop1={mockedValue} prop2={anotherValue} />);)
change prop(.setProps)
call callback(component.find('button').simulate('click'))
validate against rendering results: expect(component.find('.title').text()).toBe('New')
also we may want to validate whether some mocked external service/utility has been called(but your case probably does not need that; just want to have complete list)
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
We're trying to customise the arguments of a HOC (specifically react-graphql). Due to how this particular library HOC works, we have no ability to influence the component state (queries and options) after the HOC is called.
In a traditional HOC factory like connect() in Redux, all contained logic is applied immediately - which is too early for us. Instead, I'm deferring application of the original HOC (applyOriginalHOC()) until the construction of the first instance from this component.
export function applyLazyHOC(args) {
return function wrap(WrappedComponent) {
let ComponentWithLazyHOC; // Closed-over value is scoped to component
class LazyHOC extends Component {
constructor() {
super();
// Lazy initialising of HOC to give customisations an opportunity to be registered
if (!ComponentWithLazyHOC) {
ComponentWithLazyHOC = applyOriginalHOC(
// Adds customisations which have been registered by third party code
myCustomization(args)
)(WrappedComponent);
}
}
render() {
return <ComponentWithLazyHOC {...this.props} />;
}
}
return LazyHOC;
};
}
It seems like a slightly unconventional approach, so I'm looking for feedback:
Can you see any side effects of changing the initialisation order of HOCs? I'm assuming HOCs shouldn't rely on the presence of other HOCs.
Static analysis (e.g. in IDEs) isn't possible with HOCs anyway, so a lazy evaluation doesn't make this any better or worse, right?
HOCs can provide context to React components, which accumulates across nested components - the order shouldn't matter, right?
A component definition never gets unloaded, and ComponentWithLazyHOC is only created once per component (not component instance). Can you see any potential for memory leaks?
I'm trying to understand stateless components and what the difference is between these examples:
class App {
render() {
return (
<div>
{this.renderAFunction('hello')}
</div>
);
}
renderAFunction(text) {
return (
<p>{text}</p>
);
}
}
and this:
class App {
render() {
return(
<div>
<RenderAFunction text='hello'/>
</div>
);
}
}
const RenderAFunction = ({text}) => (
<p>{text}</p>
);
Or if there is any difference at all?
Functionally, there is absolutely no difference. Both end up rendering a paragraph element, but there are other aspects to consider. There are three points to make (in my opinion) when examining both methods:
Reusability: You have to understand to separate components when you need to. If renderAFunction is just meant to generate some JSX based on, for example, an API request, then it's fine being in a method. But if you want to reuse it somewhere else, then separate it into it's own component. A huge part of React is component reusability and getting rid of code duplication. Separating the method into it's own component would be imperative to accomplish this.
Purpose: There are reason to use stateless function components and reasons not to. The whole point of stateless functional components is to not have state and be presentational. If you need to do something that involves the React lifecycle or internal state, keep it as a method, or new class depending on if you want it reusable.
Performance: Using a stateless functional component would be less efficient. This is because it's a component, not just some JSX returned from a method. As of right now, the React team plans on making some optimizations for stateless functional components because they do not contain state and are merely presentational, but this probably won't happen until after React Fiber is done and thus your stateless functional component has no optimizations versus a regular full-fledged class component. That makes it incredibly inefficient versus a method returning some JSX, especially if it's just used once in another component.
A good rule of thumb is to ask yourself, do I need it anywhere else? If not, then keep it in a method. If you don't need it anywhere else, separating the JSX into a separate component would have worse performance and wouldn't follow React's core principles.
If you are going to need it somewhere else, then separate the component so you follow React's concept of reusability.
Your App's render function will be translated into following JS code for your first example:
render() {
return React.createElement(
'div',
null,
this.renderAFunction('hello')
);
}
And the following one for the second one:
render() {
return React.createElement(
'div',
null,
React.createElement(RenderAFunction, { text: 'hello' })
);
}
While they both looks almost the same, there is one significant difference: laziness. React will execute RenderAFunction body only in case it gonna be mounted to the DOM hierarchy.
It is insignificant is your case, but imaging following example:
const LazyApp = () => {
const heavy = <div><HeavyStuff /></div>
// ...
return <div>done it</div>
}
const HardWorkingApp = () => {
const heavy = <div>{HeavyStuff()}</div>
// ...
return <div>done it</div>
}
const HeavyStuff = () => {
// ... do heavy rendering here
}
Lazy app will not perform any heavy stuff at all. Maybe it is not very clear from that synthetic example why one would do anything like that at all. But there are real-world examples out there, when something conceptually similar (creating Components without rendering them) is happening.
Basically, both will serve the same purpose. But the real advantage of creating a component is its reusability.
If you have to render that particular piece of jsx just for this component. Then, there is no need to create a separate component.
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.