简体   繁体   中英

How can I pass attributes from a stateful component to an event handler used in an HOC wrapping a child component?

I am using a framework where I must pass an event handler into an HOC that is wrapping one of the children of my stateful Page component.

<Page>
    <HOC onClick={fn}>
        <PageColumn>
        </PageColumn>
    </HOC>
</Page>

I have a function in my stateful Page component that relies on the state and props of page. I must use a HOC that wraps a child component of the page. Within this HOC I must call an onClick method that relies on the state of the Page component.

So far I have tried passing down a reference to this.state in order to use the Page state in the HOC, and I have tried passing down props that are assigned the values that I needed in the parent state.In the onClick fn, whether I reference the necessary properties by using this.state or this.props, I get the error:

 cannot read property '<attribute>' of undefined

How can I successfully implement this pattern?

There is not a lot to investigate from you code. I noted that you use HOC as a component, but usually hoc are functions that add something to a component.

Usually hoc works like this:

EnrichedComponent = hoc(options)(BaseComponent);

example: react-redux connect hoc function

This approach should work:

// render Page somewhere
const App = () => (
  <Page/>
)

// let Page component render the PageColumn component
class Page extends React.Component {
  handleClick() {
    // I can access page state and props
    const {
      state: { foo },
      props: { bar },
    } = this;

    console.log(foo, bar);
  }

  render() {
    return (
      <PageColumn onClick={this.handleClick} />
    )
  }
}

// wrap component with HOC
const PageColumn = hoc()(() => {
  return (
    <div>...</div>
  )
});

Take a look at the code below. Basically, you want to create the function in your top level component and bind it to this in the constructor. Then pass it down as a property to your child component (ie no parameter list, no function syntax, just `this.myFunc').

In the child component, in the onClick event, call the function and pass in your parameters. Use the name of the attribute of the parent function to call the function. So for example, here it's still this.props.myFunc , but if <Page /> had this expression instead: mySpecialFunc={this.myFunc} you would call like this instead <HOC onClick={(prm1,prm2,prm3) => this.props.mySpecialFunc(prm1,prm2,prm3)} />

In <Page /> :

constructor(){
   //regular constructor code

   this.myFunc = this.myFunc.bind(this);
}

myFunc(prm1, prm2, prm3){\
   //function code
   //
   this.setState({
      myFuncState: "newValue",
      //additional code
   });
}

render(){
    return (
        <Page>
            <HOC myFunc={this.myFunc}>
                <PageColumn />
            </HOC>
        </Page>
    )
}

In <HOC /> :

render(){
    prm1 = "someValue1";
    prm2 = "someValue2";
    prm3 = "someValue3";
    return (
        <div className="HOC" onClick={(prm1,prm2,prm3) => this.props.myFunc(prm1,prm2,prm3)}>
    )
}

A Note on Parameters: The above code is for the situation where all of your parameters are in one place (in this case, the direct child component). Sometimes they are spread out amongst parent, child, grandchild components, etc. In that case, you need to declare the parameters generically in each component, passing in the relevant parameters as you move down the chain of component ancestry . For example:

class Parent extends React.Component{
    constructor(){
        //normal render code
        this.passedFunc = this.passedFunc.bind(this);
    }

    passedFunc(prm1,prm2){
        //function code
        this.setState({
            a: val1,
            b: val2,
            //etc.
        })
    }

    render(){
        return (
            <Child passedFunc={this.passedFunc}
        )
    }  
}

class Child extends React.Component{
    //other relevant code

    render(){
        let val1 = "foo";

        return (
            //we're passing prm1 into the func as val1. <Child /> doesn't know what prm2 is so we're keeping that generic and letting <GrandChild /> handle it   
            <GrandChild passedFunc={(prm1, prm2) => this.props.passedFunc(val1, prm2)}
        )
    }
}

class GrandChild extends React.Component{
    //other relevant code

    render(){
        let val2 = "bar";

        return (
            //<GrandChild /> doesn't know what prm1 is, but it knows that prm2 is val2, so it's passing in prm2 as val2 and leaving prm1 generic so <Child /> can handle it
            <div passedFunc={(prm1, prm2) => this.props.passedFunc(prm1, val2)}
        )
}

As you can see, each component passes parameters up to their parent component and uses the generic parameter name for params they don't know. Finally, the parent component receives all of the parameters and is able to use them in the function. If there was a middle man component in the chain that didn't need to set any params, for example a component named between <Child /> and '' named <InBetween /> , you could simply pass the function name through that component as a simple property, like this:

<InBetween passedFunc={this.props.passedFunc} />

Any parameters set in a component above or below it in the chain will remain intact.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM