-2

I'm tinkering with a React application where I have a Class component and a Functional Component. My Class component has a function for updating a name in its state. I'm trying to pass this function to a Functional Component as a property, the invocation works fine and I can see the argument making it into the method while debugging the application in a browser, but things blow up when trying to invoke this.setState, because 'this' is 'undefined'. I know about React hooks but don't think they are able to deal with a parent components state. Is there a technique for handling this kind of situation?

Note: the below snippets are pseudo code so just assume there's a button and clicking it works

The Class Component

class MyClassComponent extends React.Component {

    constructor(props) {
        super(props);

        this.setName = this.setName.bind(this);

        this.state = {
            name: '',
        };
    }


    setName(name) {
    
        this.setState({
            name: name
        });
    }

    render() {

        return (
            <MyFunctionalComponent
                setName={this.setName}/>                
        );
    }
}

The Functional Component

export const MyFunctionalComponent = props => {

     const { setName } = props;

     return (
         <button onClick={setName('fred')}>
     );
};

The exception

Uncaught TypeError: Cannot read property 'setState' of undefined
Jason
  • 1,371
  • 4
  • 21
  • 44
  • 1
    But the included snippet ***IS*** incomplete and invalid. I added a closing tag for the button and correctly attached the callback and it works fine. Perhaps you should instead include the real code you are having an issue with? [Minimal, Complete, and Reproducible Code Example](https://stackoverflow.com/help/minimal-reproducible-example). [Working codesandbox demo](https://codesandbox.io/s/react-class-component-using-functional-component-cannot-update-state-with-passed-yt4qf) – Drew Reese Oct 14 '20 at 02:52
  • Nevermind I see what ya mean, thanks for the links! Haven't seen that before. – Jason Oct 14 '20 at 03:13
  • Does this answer your question? [How to access the correct \`this\` inside a callback?](https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-inside-a-callback) – Jared Smith Oct 14 '20 at 03:17

1 Answers1

0

The issue in your code is in MyFunctionalComponent component. you are calling the setName function on component render.

Try this.

export const MyFunctionalComponent = props => {
     const { setName } = props;
     return (
         <button onClick={() => setName('fred')}>
     );
};

Working Example:

function MyFunctionalComponent(props) {
  const { setName } = props;
  return <button onClick={() => setName("fred")}>Click me</button>;
}

class MyClassComponent extends React.Component {
  constructor(props) {
    super(props);
    this.setName = this.setName.bind(this);
    this.state = {
      name: ""
    };
  }
  setName(name) {
    this.setState({
      name: name
    });
  }
  render() {
    return (
      <div>
       <MyFunctionalComponent setName={this.setName} />
        <br />
        {this.state.name}
      </div>
    );
  }
}

function App() {
  return <MyClassComponent />;
}

 ReactDOM.render(<App />, document.getElementById('root'))
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

<div id="root"></div>
Sohail Ashraf
  • 10,078
  • 2
  • 26
  • 42
  • This would just fix the callback from being invoked immediately, not resolve the `this` being undefined issue. – Drew Reese Oct 14 '20 at 02:54
  • This is the issue, if update the state on render then it will go to an infinite loop and will not render the component. I have added the working code snippet. try that. – Sohail Ashraf Oct 14 '20 at 02:55
  • Right, which *isn't* the issue OP says they have with the admittedly attached "pseudo code so just assume there's a button and clicking it works". – Drew Reese Oct 14 '20 at 02:58