content: // @flow
  import { isEmpty, isEqual, mapValues } from 'lodash'
  import React from 'react'
  import { connect } from 'react-redux'
  import prefixName from './util/prefixName'
  import { withReduxForm } from './ReduxFormContext'
  import type { ComponentType } from 'react'
  import type { Structure } from './types'
  import type { FormValuesInterface } from './formValues.types'
  
  export default function createValues({ getIn }: Structure<any, any>): FormValuesInterface {
    return (firstArg: string | Object | Function, ...rest: string[]) => {
      // create a class that reads current form name and creates a selector
      // return
      return (Component: ComponentType<any>): ComponentType<any> => {
        class FormValues extends React.Component<Object> {
          Component: ComponentType<any>
          _valuesMap: Object
  
          constructor(props: Object) {
            super(props)
            if (!props._reduxForm) {
              throw new Error(
                'formValues() must be used inside a React tree decorated with reduxForm()'
              )
            }
            this.updateComponent(props)
          }
  
          UNSAFE_componentWillReceiveProps(props) {
            if (typeof firstArg === 'function') {
              this.updateComponent(props)
            }
          }
  
          render() {
            const { Component } = this
            return (
              <Component
                // so that the connected component updates props when sectionPrefix has changed
                sectionPrefix={this.props._reduxForm.sectionPrefix}
                {...this.props}
              />
            )
          }
  
          updateComponent(props) {
            let valuesMap: Object
            const resolvedFirstArg: string | Object =
              typeof firstArg === 'function' ? firstArg(props) : firstArg
            if (typeof resolvedFirstArg === 'string') {
              valuesMap = rest.reduce(
                (result, k) => {
                  result[k] = k
                  return result
                },
                { [resolvedFirstArg]: resolvedFirstArg }
              )
            } else {
              valuesMap = resolvedFirstArg
            }
            if (isEmpty(valuesMap)) {
              // maybe that empty valuesMap is ok if firstArg is a function?
              // if this is the case, we probably should set this.Component = Component
              throw new Error(
                'formValues(): You must specify values to get as formValues(name1, name2, ...) or formValues({propName1: propPath1, ...}) or formValues((props) => name) or formValues((props) => ({propName1: propPath1, ...}))'
              )
            }
            if (isEqual(valuesMap, this._valuesMap)) {
              // no change in valuesMap
              return
            }
            this._valuesMap = valuesMap
            this.setComponent()
          }
  
          setComponent() {
            const formValuesSelector = (_, { sectionPrefix }) => {
              // Yes, we're only using connect() for listening to updates.
              // The second argument needs to be there so that connect calls
              // the selector when props change
              const { getValues } = this.props._reduxForm
              const values = getValues()
              return mapValues(this._valuesMap, (path: string) =>
                getIn(values, prefixName(this.props, path))
              )
            }
            this.Component = connect(
              formValuesSelector,
              () => ({}) // ignore dispatch
            )(({ sectionPrefix, ...otherProps }) => <Component {...otherProps} />)
          }
        }
  
        return withReduxForm(FormValues)
      }
    }
  }
  