acdlite / recompose

A React utility belt for function components and higher-order components.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

New LifeCycle in React v16.3

opened this issue · comments

Does static getDerivedStateFromProps(nextProps, prevState) work?

what you mean?

How can/will we use getDerivedStateFromProps in recompose?

I'm doing it here using setStatic #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.

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... />
   }
}

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. 😄

@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.

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

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

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.

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.

@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);