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()} />
Related
I would like to be able to have a component whose rendering function is in another file in order to have a separation between the logic of my component and the rendering.
Naively, I tried to do just one file containing my component and which rendered a functional component of the same name to which I passed the necessary props so that everything was displayed correctly.
Something like that :
// MyComponent.render.jsx
export default MyComponentRender = (props) => {
return {
<View>
// render all components of my view
</View>
}
}
// MyComponent.js
class MyComponent extends Component {
// some logic
render() {
return (
<MyComponentRender
aLotOfProps=....
/>
)
}
}
But I soon found myself having to send, sometimes, a fairly large amount of props and +, I have for example textInputs that need to be focus() or blur() in reaction to some logic in my view but as a result, I couldn't control that just by sending props. It quickly became a mess!
I was wondering if there was a simple way to separate the logic of a component and its rendering function? Maybe there is a way to pass the context of my component to my rendering function/component so that it has direct access to all states and can also store references, etc.?
Thanks you,
Viktor
Suppose that I had a React component such as
import { getEventListener } from 'controls';
class SomeComponent extends React.Component {
render() {
return (<div onClick={this.handleEvent}></div>)
}
handleEvent = (event) => {
getEventListener(event, this);
}
}
And I have another file such as
export function getEventListeners(event, component) {
component.setState({x: 1};
}
If getEventListeners calls set state from the component to change one of the properties would it cause an issue?
This causes the design issue, passing the whole component by reference and accessing it in a function breaks the principle of least privilege. Calling the function with a context like getEventListener.call(this, event) would have the same problem.
If getEventListener isn't supposed to be reused between components, it shouldn't be extracted from a component where it's used. It uses this.setState method and clearly belongs to a class. In case multiple inheritance is involved, a mix-in can be used.
A solution that is idiomatic to React is reusable state updater function. It's decoupled from a component and supposed to be used for pure synchronous functions:
export const getEventListeners = event => state => {
// return state object that derives from an event
};
...
this.setState(getEventListeners(event));
there! The App component is a container for three different components:
Map renders a map with visual marks on it representing addresses the user has provide.
List component contains all added addresses as list items.
Input allows a user to add a new address (in my terms that is called LocationPoint).
Right now, the App keeps locations array in state with all those addresses (LocationPoints) and passes that array into all child components.
Manipulations with LocationPoints (add/move/update/deleteLocationPoint) are taken out to separate function as they are quite generic and may be reused somewhere else later.
But because those functions do not know about state existence I have to create some kind of "provider" functions that calls those actions (addLocationPoint, deleteLocationPoint, etc). E.g. addLocationPoint func has to be called inside App.addLocationPoint.
The following example should explain what I was talking about better. Note: snippet doesn't work as it's not a real implementation.
// Adds a new location point
const addLocactionPoint = (locations: array, address: string) => {
// ...
return updatedLocations;
}
class App extends React.Component {
constructor() {
this.state = {
locations: [],
}
// bind addLocPoint, etc.
}
addLocPoint(address) {
this.setState(state => {
addLocactionPoint(state.locations, address);
});
}
// ...
render() {
return (
<Input onSubmit={ this.addLocPoint } />
<List
onDrag={ this.moveLocPoint }
onDelete={ this.deleteLocPoint }
/>
<Map data={ this.state.locations } />
);
}
}
Can my approach be considered as a good practice? Or there are other ways to reduce amount of logic in App component and to avoid creating those "providers" without using state management libs (MobX, Redux, etc). Maybe the case I consider is a right time to introduce a Redux or MobX?
I'll be really grateful for advice or recommendations or links to explore on this question.
It's already good enough, this is how global state is usually maintained in vanilla React with no state management libraries. Context API may additionally be used to pass the state to nested components.
The thing that can be changed is that functions that update the state can actually be extracted and used as setState higher-order updater functions:
const addLocationPoint = (address) => ({ location }) => {
// ...
return updatedLocations;
}
class App extends React.Component {
...
addLocPoint(address) {
this.setState(addLocationPoint(address));
}
...
A similar idea is used in Redux action creators.
In order for state updaters to provide real improvements, they have to be moved to another module. In this case they can be tested separately from a component that uses them and mocked with jest.mock in a component that uses them.
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.
TL;DR Given the following example code:
ReactDOM.render(<MyComponent prop1={someVar} />, someDomNode);
Is it possible to manually pass React context into the instance of MyComponent?
I know this sounds like a weird question given React's nature, but the use case is that I'm mixing React with Semantic UI (SUI) and this specific case is lazy-loading the contents of a SUI tooltip (the contents of the tooltip is a React component using the same code pattern as above) when the tooltip first displays. So it's not a React component being implicitly created by another React component, which seems to break context chain.
I'm wondering if I can manually keep the context chain going rather than having components that need to look for certain data in context AND props.
React version: 0.14.8
No. Before react 0.14 there was method React.withContext, but it was removed.
However you can do it by creating HoC component with context, it would be something like:
import React from 'react';
function createContextProvider(context){
class ContextProvider extends React.Component {
getChildContext() {
return context;
}
render() {
return this.props.children;
}
}
ContextProvider.childContextTypes = {};
Object.keys(context).forEach(key => {
ContextProvider.childContextTypes[key] = React.PropTypes.any.isRequired;
});
return ContextProvider;
}
And use it as following:
const ContextProvider = createContextProvider(context);
ReactDOM.render(
<ContextProvider>
<MyComponent prop1={someVar} />
</ContextProvider>,
someDomNode
);
In React 15 and earlier you can use ReactDOM.unstable_renderSubtreeIntoContainer instead of ReactDOM.render. The first argument is the component who's context you want to propagate (generally this)
In React 16 and later there's the "Portal" API: https://reactjs.org/docs/portals.html