Roy Prins
Roy Prins

Reputation: 3080

useEffect called in higher order component

After an update, React no longer compiles code with useEffect hooks in Higher Order Components (HOC). I have a HOC that connects to the Redux store and dispatches an action to fetch data if needed.

import React, {useEffect} from 'react'
import PropTypes from 'prop-types' 
import { connect } from 'react-redux'

import SpinCenter from '../components/SpinCenter'
import { fetchObject } from '../actions/objects'
import { fetchIfNeeded } from '../utils'


const withObject = ({element}) => WrappedComponent => ({id, ...rest}) => {
  const hoc = ({status, object, fetchObject}) => {
    useEffect(() => {
      fetchIfNeeded(
        status,
        ()=>fetchObject(element, id),
      )
    })

    // Initial loading and error
    if (status === undefined || object === undefined) return <SpinCenter/>
    if (status.error) return <>error loading: {status.error.message}</>

    // Pass through the id for immediate access
    return <WrappedComponent {...{object, status, id}} {...rest} />
  }

  hoc.propTypes = {
    object: PropTypes.object,
    status: PropTypes.object,
    fetchObject: PropTypes.func.isRequired,
  }

  const mapStateToProps = state => ({
    object: state.data[element][id],
    status: state.objects[element][id]
  })

  const mapDispatchToProps = {
    fetchObject
  }

  const WithConnect = connect(mapStateToProps, mapDispatchToProps)(hoc)
  return <WithConnect/>
}

export default withObject

I hope this makes sense. I guess the best approach would be to somehow place the useEffect into a functional component, but it is getting a bit complicated where this all should happen. Can anyone help me make sense of this?

This is the error I am getting.

React Hook "useEffect" is called in function "hoc" which is neither a React function component or a custom React Hook function

Upvotes: 2

Views: 4317

Answers (1)

Shubham Khatri
Shubham Khatri

Reputation: 281874

Your component name need to start with an upper case character which is why you get this issue. Also you can optimise the connect and hoc code to return an instance of hoc once instead of doing it again and again

const withObject = ({element}) => WrappedComponent => {
  const Hoc = ({id, status, object, fetchObject,...rest}) => {
    useEffect(() => {
      fetchIfNeeded(
        status,
        ()=>fetchObject(element, id),
      )
    })

    // Initial loading and error
    if (status === undefined || object === undefined) return <SpinCenter/>
    if (status.error) return <>error loading: {status.error.message}</>

    // Pass through the id for immediate access
    return <WrappedComponent {...{object, status, id}} {...rest} />
  }

  Hoc.propTypes = {
    object: PropTypes.object,
    status: PropTypes.object,
    fetchObject: PropTypes.func.isRequired,
  }

  const mapStateToProps = state => ({
    object: state.data[element][id],
    status: state.objects[element][id]
  })

  const mapDispatchToProps = {
    fetchObject
  }

  return connect(mapStateToProps, mapDispatchToProps)(Hoc)
}

export default withObject

Upvotes: 2

Related Questions