React state from HOC and component

790 views Asked by At

I have a form with controlled inputs managed via a Higher Order Component. The structure is like this:

Field Higher Order Component

function BaseField(WrappedComponent) {
  class WrappedField extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        value: '',
        active: false,
      }
    }

    setValue = (e) => {
      this.setState({ value: e.target.value })
    }
    ...
    <WrappedComponent
      {...this.props}
      value={this.state.value}
      set={this.setValue}
      active={this.state.active}
    />
    ....

Field

import React from 'react';
import BaseField from './BaseField';

const TextField = (props) => {
  return <input
    value={props.value}
    onChange={props.set}
    name={props.name}
    type={props.type}
  />
}

export default BaseField(TextField);

When using TextField this works well - however, I want to use this with more flexibility - for example, I'd like to be able to enhance the onChange functionality in some cases, always having it set state but also have it do other things based on state or functions in the component the TextField is used in.

Am I misunderstanding how HOCs work?

1

There are 1 answers

0
Jack On

You can use something like createChainedFunction from react-bootstrap:

function createChainedFunction(...funcs) {
  return funcs
    .filter(f => f != null)
    .reduce((acc, f) => {
      if (typeof f !== 'function') {
        throw new Error('Invalid Argument Type, must only provide functions, undefined, or null.');
      }

      if (acc === null) {
        return f;
      }

      return function chainedFunction(...args) {
        acc.apply(this, args);
        f.apply(this, args);
      };
    }, null);
}

and something from my react utils:

export function copyPropsWithout(props, without) {
  const propKeys = Object.keys(props);
  const passProps = propKeys.reduce((obj, propKey) => {
    if (without.indexOf(propKey) === -1) {
      obj[propKey] = props[propKey];
    }

    return obj;
  }, {});

  return passProps;
}

I'd add these to your utils and then use them like:

 ...
    <WrappedComponent
      {...copyPropsWithout(this.props, ['onChange'])}
      value={this.state.value}
      set={createChainedFunction(this.setValue, this.props.onChange}}
      active={this.state.active}
    />
    ....