OriEng
OriEng

Reputation: 1573

Pass dynamic value to HOC in react

I write some HOC and I need to pass to this HOC a dynamic object that I create on some life cycle level and I did not get him as a prop. If I try to pass some static value ( for example initialize myObj from start) it works as expected and I get the correct value.

Let's say this is my component class :

 let myObj = {};

  class Test extends React.Component
   {
      constructor(props) {
        super(props);
        .....
        }

         render() {
            myObj = {test:'test'};
            return ( ... )
        }       
    }
   export default withHOC(Test, myObj);

And this is my HOC:

const withHOC = (Component, test) => {
    class Hoc extends React.Component
    {
        constructor(props)
        {
            super(props);
            const s = test; // ---->test is empty object always !!
            ...
        }

     } 
     return Hoc;
 }

My 'Dynamic' object that I create on my 'test' class is always empty on my HOC class. It's happend also when I try to pass some value from my props directly, in this case the page is stuck(without errors in console).

Does someone have any idea how to resolve that? Thanks!

Upvotes: 8

Views: 10176

Answers (5)

Palina
Palina

Reputation: 40

You can use react-redux and store your object in redux state. Change the object wherever you need (in your case it's in Test) and access it in component inside your HOC from redux state, it'll be always up to date.

Upvotes: 0

Raba
Raba

Reputation: 250

When you compose a component that way, composition only happens at compile time (static composition). This means that withHOC runs only once and is receiving an empty myObj argument, as it is using the one defined on declaration.

export default withHOC(Test, myObj); //myObj = {}

If you want that value to be dynamic, the withHOC composition should be runned when that value changes.

You can't send data up from the WrappedComponent (Test) to the HOC (withHOC), so even if you change myObj value in Test.render, the HOC would never know.

What you could do, if you really need it, is do the composition on the Test.render

render(){
    const Hoc = withHOC(this.state.myObj, WrappedComponent);//WrappedComponent can be anything
    return(
        <Hoc/>
    )
}

This way, every time the component renders, Hoc is composed using as myObj a value from the component state, wich is not the preferable way to do it, because this.state.myObj might have the same value as it did at the previous render, and you would be re-composing with the same data as before.

A better way to do it is checking for changes in myObj at Test.componentDidUpdate, and if it did change, then compose Hoc again.

Upvotes: 6

leto
leto

Reputation: 599

I can't say with confidence this is optimal but I solved a similar problem by having a function within the HOC that updates state that you can then invoke with any data in the wrapped component.

HOC:

func = (a, b) => {
  this.setState({
    stateA: a,
    stateB: b
  )}
}

return ({ <WrappedComponent func={this.func} /> })

Wrapped Component:

this.props.func(anythingA, anythingB);

You can then access the data through state in the HOC.

To elaborate:

const withHOC = (WrappedComponent) => {
  class withHOC extends React.Component {
    constructor(props) {
      super(props)

      this.state = {
        stateA: 1,
        stateB: 2
      }
      *use state however you want in this HOC, including pass it through to another component*

      *the following is just a function*
      *when it's invoked in the wrapped component state will update here in the 
 HOC*
      changeState = (a, b) => {
        this.setState({
          stateA: a,
          stateB: b
        )}
      }

      render() {
        return (
          <div>
            <p>this.state.stateA</p>
            <p>this.state.stateB</p>
            <WrappedComponent changeState={this.changeState} />
          </div>
        )
      }
    }
  }
}

In wrappedComponent, after importing:

class aComponent extends Component {
  constructor(props) {
    super(props)

    this.state = {
    }

    *you can now invoke the function from the HOC in this wrapped component*
  }
}




Upvotes: 0

Edan Chetrit
Edan Chetrit

Reputation: 5071

Some explanation about what's happening here, by order:

  1. import Comp from '.../Test.js'
  2. the withHOC function is triggered, with the params of Test (which is defined above the call) and myObj (which is defined above the call but is empty)
  3. Test component is returned, and nobody used the logic of myObj = {test:'test'}

Suggested solution: Make the HOC get the logic from the props with another hoc:

const withProps = newProps => BaseComponent => props => {
  const propsToAdd = typeof newProps === 'function' ? newProps(props) : newProps
  return <BaseComponent {...props} {...propsToAdd} />
}

Usage:

class Test extends React.Component
   {
      constructor(props) {
        super(props);
        .....
        }

         render() {
            return ( ... )
        }       
    }
export default withProps({test:'test'})(withHOC(Test));
// or: export default withProps(props => {test:'test'})(withHOC(Test));

const withHOC = (Component) => {
    class Hoc extends React.Component
    {
        constructor(props)
        {
            super(props);
            const s = this.props.test;
            ...
        }

     } 
     return Hoc;
 }

you can use recompose, a library which has many hocs and utils, and for better readability:

import { compose, withProps } from "recompose"

class Test extends React.Component {...}

const enhance = compose(
  withProps({test:'test'}),
  withHOC
)
export default enhance(Test);

Upvotes: 0

Prithwee Das
Prithwee Das

Reputation: 5226

You are passing an empty object to the withHOC function

let myObj = {}; // <- your myObj is empty

  class Test extends React.Component
   {
      constructor(props) {
        super(props);
        .....
        }

         render() {
            myObj = {test:'test'}; // <- You're doing this in the render method of your Test component, so until the component is rendered myObj is empty
            return ( ... )
        }       
    }
   export default withHOC(Test, myObj);

Upvotes: 0

Related Questions