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?
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)
When clicking the button, you will see n is getting bigger but the UI remains 1
<script>
let n = 1
function add() {
console.log(n)
return ++n
}
export default {
functional: true,
render(h, ctx) {
return (<div>
<h1>{n}</h1>
<button onClick={add}>click</button>
</div>)
}
}
</script>
Intended Behavior
This is the intended behavior of functional components and the thing that makes them functional. From the docs:
...we can mark components as functional, which means that they’re stateless (no reactive data)
Since functional components are just functions, they’re much cheaper to render.
Explanation
What this means-- and the reason n is not reactive-- is that n is not observable and has no dependency management/watching. This lack of dependency overhead is also the reason for the performance increase of functional components. You get speed at the cost of observability, which is a nice tradeoff if you don't need it. If you do, there would be no reason to use a functional component.
How to proceed
So, you could proceed to simply use a non-functional component, or, reason about whether it's possible to subdivide your functional component further and encapsulate only the reactive portions into a non-functional subcomponent.
Other thoughts
If you manually added observability to your functional component, you would get the behavior you wanted, though there's no reason to do this over using a non-functional component. Notice the use of observable:
import Vue from 'vue';
let state = Vue.observable({n: 1});
function add() {
console.log(state.n)
return ++state.n
}
export default {
functional: true,
render(h, ctx) {
return (<div>
<h1>{state.n}</h1>
<button onClick={add}>click</button>
</div>)
}
}
(Note: You can use render functions in normal components as well. I say this just in case you had a misunderstanding that a functional component was required for render functions.)
I have a parent component which is a flat list which contains a header HeaderComponent. This HeaderComponent is a custom component that I have created which contains 2 child components of its own. Whenever i refresh the list, I am passing a boolean to the HeaderComponent as props which get passed onto its own children, I am doing this so I can check if each component needs to fetch new data or not. The problem is that whenever the parent refreshes and sets a new state the constructors of the child components get called everytime. Shouldn't the constructor be called only the first time the parent initializes and then all further calls involve calling the shouldComponentUpdate method of the children in order to see if it needs an update or not.
Parent component
_renderHeader = () => {
return <HeaderComponent Items={this.state.Data} refresh={this.state.refresh}/>;
};
render() {
console.log("TAG_RENDER render called " + this.state.refresh);
return (
<FlatList
refreshing={this.state.refresh}
onRefresh={() => {
console.log("onRefresh");
this.setState({
refresh: true
}, () => {
this._fetchData();
});
}}
......
ListHeaderComponent={() => this._renderHeader()}
.......
/>
);
}
Header Component
export default class HeaderComponent extends React.Component {
constructor(props) {
super(props);
console.debug("HeaderComponent");
}
render() {
return (
<MainHeader Items={this.props.Items}/>
<SubHeader refresh={this.props.refresh}/>
);
}
}
The constructor of MainHeader and Subheader gets called whenever the parent component refreshes. Does this mean that it is creating new child components each time it refreshes because I can see the render of the children also being called multiple times.
Control your index.js file. If you see <React.StrictMode>, you should change to <>. This is solved my problem.
It should be like:
ReactDOM.render(
<>
<App/>
</>,
document.getElementById('root')
);
As correctly stated in the one of the answers , removing the strict mode fixes the issue. Coming to why it does that, its because the strict mode intentionally calls the 'render' method twice in order to detect potential problems.
React works in two phases:render and commit. Render phase checks and determines the new changes to be applied. And commit phase applies it.
Render phase lifecycle includes methods like : constructor, UNSAFE_componentWillMount,UNSAFE_componentWillReceiveProps, ...,render and few more.
The render phase is time consuming and is often broken into pieces to free up the browser. Render phase might be called multiple times before the commit phase(usually very fast).
Since the render phase methods are called more than once, its important that none of those method have any problems or side effects.
Thus just in order to highlight the possible side effects to make them easy to spot, react explicitly double invoke the render phase methods.
You can read more about this on :https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects :)
Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:
Class component constructor, render, and shouldComponentUpdate
methods
Class component static getDerivedStateFromProps method
Function component bodies
State updater functions (the first argument to setState)
Functions passed to useState, useMemo, or useReducer
https://reactjs.org/docs/strict-mode.html
As stated in the site,
Note:
This only applies to development mode. Lifecycles will not be double-invoked in production mode.
I'm learning React. It seems to me that HOC like the following example from React's official docs:
function withSubscription(WrappedComponent, selectData) {
// ...and returns another component...
return class extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
data: selectData(DataSource, props)
};
}
componentDidMount() {
// ... that takes care of the subscription...
DataSource.addChangeListener(this.handleChange);
}
componentWillUnmount() {
DataSource.removeChangeListener(this.handleChange);
}
handleChange() {
this.setState({
data: selectData(DataSource, this.props)
});
}
render() {
// ... and renders the wrapped component with the fresh data!
// Notice that we pass through any additional props
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
}
can be rewritten in this way:
class WithSubscription extends React.Component {
constructor({ component, selectData, ...props }) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
data: selectData(DataSource, props)
};
}
componentDidMount() {
DataSource.addChangeListener(this.handleChange);
}
componentWillUnmount() {
DataSource.removeChangeListener(this.handleChange);
}
handleChange() {
this.setState({
data: selectData(DataSource, this.props)
});
}
render() {
return <component data={this.state.data} {...this.props} />;
}
}
Then use it like this:
<WithSubscription component={BlogPost} selectData={(DataSource) => DataSource.getComments()} />
Are they both HOC? When is one style preferred than the other?
I was struggling with HOC too at first. Another way of looking at this is at wrappers of components that you could use to isolate the functionality from one component.
For example, I have multiple HOC. I have many components that are only defined by props, and they are immutable once they are created.
Then I have a Loader HOC Component, which handles all the network connectivity and then just passes the props to whatever component is wrapping (This would be the component you pass to the HOC).
The loader does not really care which component it is rendering, it only needs to fetch data, and pass it to the wrapped component.
In your example, you can actually accomplish the same, however it will become much more complex once you need to chain multiple HOC.
For example I have this chain of HOCs:
PermissionsHOC -> LoaderHOC -> BorderLayoutHOC -> Component
First one can check your permissions, second one is loading the data, third one is giving a generic layout and the forth one is the actual component.
It is much easier to detect HOCs if you realize that some components would benefit from having a generic logic on the parent. You could do the same in your example, however you would need to modify the HOC every time you add a child component, to add the logic for that one. Not very effective. This way, you can add new components easily. I do have a Base component which every component extends, but I use it to handle the helper functions like analytics, logger, handling errors, etc.
What they call an "HOC" is basically a function (just a regular function, not React specific) that behaves like component factory. Meaning it outputs wrapped components that are the result of wrapping any inside-component of your choice. And your choice is specified with the "WrappedComponent" parameter. (Notice how their so-called "HOC" actually returns a class).
So I don't know why they called it an "HOC" tbh. It's just a function that spits out components. If anyone knows why I'd be interested in hearing the reason.
In essence their example is doing exactly what you're doing, but it's more flexible because WrappedComponent is being taken in as a parameter. So you can specify whatever you want. Your code, on the other hand, has your inside component hard coded into it.
To see the power of their example, let's say you have a file called insideComp.js containing:
import withSubscription from './withSubscription';
class InsideComp extends React.Component{
// define it
}
export default withSubscription(InsideComp);
And when you use insideComp in another file:
import myComp from './insideComp.js';
You're not actually importing insideComp, but rather the wrapped version that "withSubscription" had already spit out. Because remember your last line of insideComp.js is
export default withSubscription(InsideComp);
So your InsideComp was modified before it was exported
The second one is not a HOC.
They coin the word HOC from higher order functions. One example of a higher order function is a function that takes a function as an argument and returns another function.
Similarly, a HOC is a function that takes an component as argument and returns another component.
This does sound weird to me because a higher order component is not a react component; it is a function instead. I guess the reason they call it HOC is because:
A react component is a class, which is indeed a constructor function in JavaScript (except that functional components are simply functions). A HOC actually takes a function (a constructor function) and returns another function (another constructor function), so it is actually a higher order function if you think about it. Probably because it is in the react context and this is a pattern to transform components, they call it HOC.
As to the difference between the two styles you mentioned:
First one: you would use the first one to generate a class like MyComponnet = withSubscription(AnotherComponent, ...), and whenever you need it in a render call just write <MyComponent><MyComponent>
Second one: this is less common. Every time you need it in a render call, you would need to include the WithSubscription component as you mentioned in the description <WithSubscription component={BlogPost} selectData={(DataSource) => DataSource.getComments()} />
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.