recompose icon indicating copy to clipboard operation
recompose copied to clipboard

New LifeCycle in React v16.3

Open ghost opened this issue 7 years ago • 11 comments

Does static getDerivedStateFromProps(nextProps, prevState) work?

ghost avatar Apr 23 '18 11:04 ghost

what you mean?

istarkov avatar Apr 23 '18 15:04 istarkov

How can/will we use getDerivedStateFromProps in recompose?

CharliePops avatar Apr 23 '18 16:04 CharliePops

I'm doing it here using setStatic https://github.com/acdlite/recompose/issues/648 , it is working like the old componentWillReceiveProps lifecycle, but get warning in console.

FYI, I am initializing state in my withReducer, reducer is imported from a separate reducer.js file and declared on the top, so I didn't pass a initialState as the 4th argument for withReducer HOC.

hh83917 avatar Apr 23 '18 16:04 hh83917

I wrote that lifecycle is somehow a recompose mistake, you don't need it at all as it's easier to write ordinary class. Just write your HOC if you need it, it's very simple

const MyLifecycle = Component => class Bla extends React.Component {
    ...any lifecycles you like
   render() {
       return <Component {...this.props} {...this.state} myHandler={this.handler} etc... />
   }
}

istarkov avatar Apr 23 '18 16:04 istarkov

I see what you meant about setStatic, and thank you very much for the example. Using a getDerivedStateFromProps HOC with recompose like this:

derivedStateFromProps HOC:

const derivedStateFromProps = (initialState, updateStateValue) => BaseComponent => {
    class DerivedStateFromProps extends React.Component {
        state = typeof initialState === 'string' ? this.props[initialState] : initialState;
        static getDerivedStateFromProps = updateStateValue;
        render() {
            return <BaseComponent {...this.props} {...this.state} />;
        };
    }
    return DerivedStateFromProps;
}

Usage:

const enhance = compose(
    withReducer('ComponentState', 'dispatch', reducer),
    withHandlers(handlers),
    derivedStateFromProps('ComponentState', (nextProps, prevState) => {
        if (nextProps !== prevState) return { ...etc };
        return null;
    }),
    lifecycle({
        componentDidMount() {
                ...etc.
        }
    })
);

export default enhance(SomeComponent);

No more Did not properly initialize state during construction. ...etc warnings too. 😄

hh83917 avatar Apr 23 '18 20:04 hh83917

@istarkov do you think recompose should have a getDerivedStateFromProps HOC since it's now a static instead of using the deprecating componentWillReceiveProps in the lifecycle HOC? Thanks.

hh83917 avatar Apr 24 '18 14:04 hh83917

I think to deprecate lifecycle and suggest solution above instead, as what the need to reproduce react api

istarkov avatar Apr 24 '18 14:04 istarkov

That make sense... then can probably deprecate setStatic too.

hh83917 avatar Apr 24 '18 15:04 hh83917

I don't think a derivedStateFromProps HOC will work. The problem is recompose effectively gets rid of state, as each line in compose results in a nested component, and the state gets passed into the child component as a prop.

The only solution I can think of is to add an extra optional function parameter to the withState HOC.

withState(
  ...
  derivedStateFromProps: (nextProps, preState) => newState
)

This also gets rid of the need to do lifecycling in many cases.

szoio avatar May 01 '18 21:05 szoio

Yes, state get pass through as props, so that's the first initialState argument which takes the state name in props as a string, or you can pass your own state in as an object.

In my case above, I use withReducer + withHandlers to dispatch state changes, similar to a local mini redux. I can pass the name of the state into the derivedStateFromProps HOC and it will watch and compare that state and return whatever new state.

If they are deprecating lifecycle and preferring class components, you can come up with what works for you best.

hh83917 avatar May 01 '18 21:05 hh83917

@istarkov you wrote

that lifecycle is somehow a recompose mistake, you don't need it at all as it's easier to write ordinary class.

But doesn't this apply to the most HOCs of recompose? I am using recompose to write more readable and less code.

I mean something like

compose(
    connect(null, mapDispatchToProps),
    lifecycle({
        componentDidMount() {
            this.props.loadUser();
        },
    }),
)(Login);

As I understand your point you want to write an extra HOC for every case? Something like

const triggerLoadUserOnMount = Component => (
    class TriggerLoadUserOnMount extends React.Component {
        componentDidMount() {
            this.props.loadUser();
        }
        render() {
               return <Component {...this.props} />
        }
    }
);

compose(
    connect(null, mapDispatchToProps),
    triggerLoadUserOnMount,
)(Login);

micha149 avatar Jul 20 '18 07:07 micha149