3

I have multiple component with similar piece code in lifecycle methods and some similarity in state variables. Is there a way to unify them, by inheriting from one parent or something like that?

constructor(props) {
    super(props);
    this.state = {
        //state properties similar in all components, getting from redux
        //state properties specific for this component
    }
    // same code in many components
}

componentWillMount() {
    // same code in many components
    // code specific for this component
}

Can I use children methods and props in parent "wrapper" ? Can I change component state from parent ?

Anna
  • 2,911
  • 6
  • 29
  • 42

2 Answers2

3

You can create Higher Order Component (HOC) for that, basically, you just write component with your same lifecycle method which is repeating, and then in render() function, call this.props.children function with any HOC internal state arguments you want, you can pass the whole state and a setState function as well, so you can change the HOC's state inside the underlying component.

For example:

  class HOC extends React.Component {
   constructor(props) {
     super(props);
     state = {
       someState: 'foo',
     };
   }

   componentWillMount() {
     console.log('i mounted!')
   }
   render() {
     return (
       <div>
         {this.props.children({ state: this.state, setState: this.setState })}
       </div>
     )
   }
 }

 const SomeComponent = () =>
   <HOC>
     {({ state, setState }) => (
       <div>
         <span>someState value: </span>
         <input 
           value={state.someState} 
           onChange={e => setState({ someState: e.target.value})} 
         />
       </div>
     )}
   </HOC>

You can also do really cool and interesting things with it, like connecting a slice of your redux state whenever you need it:

import { connect } from 'react-redux';

const ProfileState = connect(
  state => ({ profile: state.profile }),
  null,
)(({ 
  profile, 
  children
  }) => ( 
  <div>
    {children({ profile })}
  </div>
));

const ProfilePage = () => (
  <div>
    Your name is:
    <ProfileState>
     {({ profile }) => (
       <span>{profile.name}</span>
     )}
    </ProfileState>
  </div>
);

Here is the full documentation on this technique.

Ilya Lopukhin
  • 692
  • 8
  • 22
  • This is not really a HOC, this technique is called `render props`. – Hinrich Jun 07 '18 at 11:06
  • Thank you, and how SomeComponent can be look like if this is component with constructor, methods, render function... ? – Anna Jun 07 '18 at 12:57
  • 1
    @Jeremy you can convert functional component to stateful easily, just insert {({ state, setState}) => 'anything'} component in the returns of render function and you're done – Ilya Lopukhin Jun 07 '18 at 17:11
  • ok..so this way I can't use setState from HOC inside lifecircle or other methods of SomeComponent, only directly in render, right ? – Anna Jun 08 '18 at 09:12
  • @Jeremy, oh you can easily route this setState to SomeComponent, in render function just call something like this.registerHOC({ state, setState }) which would be registerHOC = (({ state, setState }) => if (!this.HOCRegistered) { this.HOCSetState = setState; this.HOCState = state; this.HOCRegistered = true } – Ilya Lopukhin Jun 08 '18 at 14:48
2

You could create HOCs (Higher Order Components) in that case. It can look like this:

/*
   A Higher Order Component is a function,
   that takes a Component as Input and returns another Component.

   Every Component that gets wrapped by this HOC
   will receive `exampleProp`,`handleEvent`, 
   plus all other props that get passed in.
*/

function WithCommonLogic(WrappedComponent) {
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        example: ''
      }
    }
    componentWillMount() {
      ...
      // Same code in many components.
    }
    callback = () => {
      /* Enhanced components can access this callback 
         via a prop called `handleEvent`
         and thereby alter the state of their wrapper. */
      this.setState({example: 'some val'})
    }
    render() {
      return <WrappedComponent 
          exampleProp={this.state.example}
          handleEvent={this.callback}
          {...this.props}
      />
  }
}


// You use it like this:

const EnhancedComponent1 = WithCommonLogic(SomeComponent);
const EnhancedComponent2 = WithCommonLogic(SomeOtherComponent);

Now all the shared logic goes into that HOC, which then wrap all your different components you want to share it with.

See the React Docs for further reading.

Hinrich
  • 13,485
  • 7
  • 43
  • 66
  • Can I use props from SomeComponent in WithCommonLogic wrapper? – Anna Jun 07 '18 at 13:07
  • 1
    No, but you can pass a callback function to the component as prop, and call whenever needed in a child to update something in the parent. I will update my answer to reflect that. – Hinrich Jun 07 '18 at 14:45