One thing I like about recompose is that it enables me to create complex stateful components out of functions that don't depend on context. Two examples are withState and withStateHandlers. state and stateUpdater are passed as props instead of bound to this. This is nice because I can write more generic functions that consume these values. The same is not true for lifecycle. The handlers passed to lifecycle are given to their respective hooks without any context abstraction. So for these handlers, I either have to write that wrapper myself or write functions tied to this. Is there any interest in updating lifecycle to pass the context into the handler like withState and withStateHandlers?
I think that the original idea was to give you a real part of a real class but inside of a HOC interface, with all the powerful things you can do with this like this.setState(). But for me it feels more like a low-level hacks and I personally totally agree with your points about context abstraction. That's why I created with-lifecycle as an alternative "trade off" which is acceptable for me.
@istarkov actually I heard such questions from my colleagues few times, like "why lifecycle is not following the same approach as other Recompose HOCs?" Then I found how lifecycle hooks are done in Reassemble and was really impressed with the idea.
lifecycle is somewhere at the top place in possible deprecation list as in it's current implementation there is not a big difference to write HOC as is
compose(
...
Base => class MyLifecycle extends Component {
// Do here anything you want much more that can be done in lifecycle
render() {
return <Base {...this.props} />
}
}
....
)
There are issues with lifecycle with flowtype, and also it gives much more freedom than acceptable.
Also IMO if you really need lifecycle - may be better to write a component as is without enhancers at all.
Here https://github.com/acdlite/recompose/issues/193 author wrote:
Recompose is a functional utility library for React components — not a state management library, or a full replacement for class components, or an entire application architecture.
So I think we will deprecate lifecycle in nearest release.
Also lifecycle is a big source of issues here and this gives us +1 for deprecation ;-)
Thanks for the responses, @deepsweet and @istarkov! I'm sorry I've taken so long to respond myself. I unexpectedly found myself without internet access.
@deepsweet thank you for recommending with-lifecycle and reassemble. I like both a lot.
@istarkov, I don't completely understand your reasons for deprecating the lifecycle utility but if it's going to be deprecated, I won't open a PR. I definitely appreciate the work you and others do on this library. Overall, I find it to be a very elegant way of thinking about React components. If lifecycle is beyond the scope of that way of thinking, I'd like to hear more about why.
FWIW, before opening this issue, I looked for existing issues related to lifecycle. The two I remember reading were both confusions about the lexical this binding of arrow functions. Deprecating lifecycle is definitely one way to reduce that confusion but to me, doing so seems like throwing the baby out with the bathwater.
Thanks again for your attention to this!
@istarkov, I'd like to echo @maxhallinan's call for more info about why lifecycle is a bad idea.
In the absence of lifecycle, what would you recommend as a preferred approach to something like triggering an async fetchData action in componentDidMount?
@istarkov, I'd like to echo @jrnail23's echo. I use lifecycle all the time. It's a common need to have a HOC that must use lifecycle hooks, so why make us write one from scratch when a lib can centralize with great test coverage and all the benefits of open source contributions for bugs/etc?
Here is an example of when using the lifecycle HOC is better than using the inline class method. Because you can create reusable patterns. Inline class methods are not shareable or reusable.
import React from 'react';
import { connect } from 'react-redux';
import { compose, lifecycle } from 'recompose';
import { fetchToggles } from '../actions/resources';
import { getToggles } from '../reducers/toggles';
const loadStatus = {
loaded: false
};
const fetchTogglesOnMount = lifecycle({
componentDidMount() {
if (!loadStatus.loaded) {
loadStatus.loaded = true;
this.props.fetchToggles();
}
}
});
const connectToToggleState = connect(
(state) => ({
toggles: getToggles(state)
}),
{ fetchToggles }
);
export const withToggles = compose(
fetchTogglesOnMount,
connectToToggleState
);
FWIW, here's a context-abstraction utility that enables you to compose context-free functions and then lift them into a context such as the context created by the lifecycle HOC.
Because you can create reusable patterns.
Maybe it's just me, but the number of times I've literally ever reused a given set of lifecycle handlers, even across a huge project, can be counted on one hand. Having to make a separate HOC out of them just adds more boilerplate and makes it harder to understand the code for a component.
@icopp
can be counted on one hand
Maybe so, but I've also only used other recompose functions a handful of times too.
just adds more boilerplate
Hmmm, I actually consider it more boilerplate if we have to manually create our own class plus render method plus rendering BaseComponent with spread props.
Most helpful comment
Here is an example of when using the lifecycle HOC is better than using the inline class method. Because you can create reusable patterns. Inline class methods are not shareable or reusable.