acdlite / recompose

A React utility belt for function components and higher-order components.
MIT License
14.76k stars 1.26k forks source link

add mount option to lifecycle #68

Closed hartzis closed 8 years ago

hartzis commented 8 years ago
lifecycle(
  setup: (component: ReactComponent) => void,
  mount: (component: ReactComponent) => void,
  teardown: (component: ReactComponent) => void,
  BaseComponent: ReactElementType
): ReactElementType

Add mount option to lifecycle that calls your function during componentDidMount. This will allow use for isomorphic/universal apps when you only want things ran on client and not on server.

Let me know what you think. I could probably get a PR in this week.

jeron-diovis commented 8 years ago

+1 for this issue. It looks inconsistent for now. I.e., we already dealing with DOM lifecycle hooks, in willUnmount - so why there is no other DOM hooks?

acdlite commented 8 years ago

I've been thinking lately about removing lifecycle all together. The lifecycle methods are essentially useless without access to the component instance, in which case you should just use a class.

acdlite commented 8 years ago

Does this make sense to anyone else? Is lifecycle really all that useful?

jeron-diovis commented 8 years ago

The lifecycle methods are essentially useless without access to the component instance

Well, not exactly. For example, you can start some timers when component is mounted - note, not in constructor, as it will cause problems with server-side rendering. Or, my own case, with which I came to this issue - a print page. It looks like this:

class PrintPage extends React.Component {
  render() {
    return (
      <Content {...this.props} />
    );
  }

  componentDidMount() {
    window.print();
  }
}

With didMount hook it could be just lifecycle(window.print, Content).

Sure, it's all a very edge cases. But they exist, and it's always nicely when there is a handy function which saves you from manually writing another boilerplate class =)

istarkov commented 8 years ago

Have a question about:

What the idea to pass this as an argument to lifecycle methods, isn't it better to pass just props and some internal object to hold data across lifecycle events (like window event handlers or some instance methods which depends on lifecycle).

To get state access user can use lifecycle in combination with withState.

Does something like this will not be enough?

 class Lifecycle extends React.Component {
   internalObj_ = null

   componentWillMount() {
       this.internalObj_ = setup(props)
    }

    ...otherLifeCycleEventsIfNeeded

    componentWillUnmount() {
       teardown(this.props, this.internalObj_)
    }

    render() {
      return createElement(BaseComponent, {...this.props, ...this.internalObj_})
    }
  }
istarkov commented 8 years ago

Without this, props could be changed at level above, so setup internals will have a ref on an old props version

hartzis commented 8 years ago

My use case is just to load an SDK(braintree) during componentWillMount only on client side for later use. I don't need access to the wrapped component instance.

dariocravero commented 8 years ago

I've been using createSink or doOnReceiveProps when I need to deal with componentWillMount. Wouldn't that do for those use cases?

The downside is that it triggers on componentWillReceiveProps and sometimes you don't want that, e.g.: you may just want to trigger something once off. So perhaps something can be done there too (either once off components or a switch on those two).

What are your thoughts on this?

dralletje commented 8 years ago

@dariocravero I use lifecycle for things like initial caches as well, where they aren't really depending on the props at all. Just a setState most of the time

RafalFilipek commented 8 years ago

Useful:

dlebedynskyi commented 8 years ago

voted for keeping doOnReceiveProps in another thread. since it removed. at least provide a way to do something on Mount/NewProps since that helper was very handy really.

agree with @RafalFilipek

gregmuellegger commented 8 years ago

Here is simple way to intercept livecycle method calls:

import { compose, toClass } from 'recompose';

const enhance = compose(
  component => class extends toClass(component) {
    componentWillReceiveProps(nextProps) {
      if (nextProps.value !== this.props.value) {
        // Do something, value has changed.
      }
    }
  },
);

Simply extend the given component in the compose chain, and do whatever you need there.

acdlite commented 8 years ago

Closed by https://github.com/acdlite/recompose/issues/162.