3

I am relatively new to ReactJS and a novice with JavaScript and have been following some online tutorials to learn ReactJS. I am coming across a mixture of tutorials and information from different versions so I am sure I either completely don't understand a topic or am mixing things up here.

I have a function updateActiveFlag in my top level object App and I am passing it to ItemGroup as a prop and then Item that I want called when a checkbox is selected. Eventually when I understand what I am doing wrong I will modify other aspects of the state in this same manner as well.

When I select the checkbox, I get in the console:

undefined
undefined
4 // Assuming I selected the 5th checkbox, this aspect is working correct

Can someone help explain the mistake I am making. It seems that by the time I get back to the top level handler, the concept of 'this' has been lost. At this time I am not interested in using some library to solve this Unless that is the only way. I am hoping it can be solved in this example so that I can first better understand the low level aspects, then I can consider a more proper way if one exists.

EDIT:

This is now working

Several responses to this post highlighted a typo I made in my example. I copy and pasted the code form a larger prototype and mixed up the signatures in ItemGroup and Item, so that is reflected in the sample code.

I also changed how I passed the event handler in the App control again based on feedback in this thread and now it is working as I need.

Finally the duplicate topic that was referenced had more information to help me understand this better. I am not sure how to credit an answer in this case.

const Item = (props) => {
return (
      <li className="row">
         <input className="col-sm-1" type="checkbox" checked={props.item.Active} onChange={() => props.activeFlagHandler(props.index)} />
         <div className="col-sm-2">{props.index}</div>
         <div className="col-sm-3">{props.item.Name}</div>
         <div className="col-sm-3">{props.item.Desc}</div>
         <div className="col-sm-3">{props.item.Amount}</div>
      </li>
   );
};


const ItemGroup = (props) => {
   return (
      <div className="container">
         <h4>{props.name}</h4>
      <ul>
            {props.items.map((item, index) => <Item item={item} index={index} activeFlagHandler={props.activeFlagHandler}/>)}
      </ul>
      </div>
   );
};


class App extends React.Component {

   constructor(props) {
      super(props);
      this.state = {
         Name: "",

         "Accounts": [],
         "Expenses": []
      };
   };


   loadModel( model ) {
      $.getJSON( 'api/UnitTest' ).then();

      // Using arrow function otherwise would get an error saying setState is not a valid function
      // because of binding. Arrow function passes ensures correct execution context is passed along
      $.getJSON( '/Model/0123456789' ).then( (results) => { this.processModel( results ) } );
   };


   processModel( results ) {
      console.log( results );

      this.setState( results );
   };


   updateActiveFlag( index ) {
         //const newState = Object.assign( {}, this.state );
         console.log( this.state );
         console.log( this.prevState );

         console.log( index );
   };


   componentDidMount() {
      this.loadModel( '0123456789' );
   }


   render() {
      return (
         <div>
            <h2>Hello World  {this.state.Name} </h2>
            <ItemGroup name="Expenses" items={this.state.Expenses} activeFlagHandler={(index)=>{this.updateActiveFlag(index)} />
         </div>
      );
   }
}

ReactDOM.render(<App />, document.getElementById("app"));

Thanks for any help strong text

J Cox
  • 311
  • 3
  • 9
  • I'm unsure what you're trying to do in the updateActiveFlag method. Are you trying to get information from the checkbox itself? I will say that prevState doesn't exist in that context. – Jay Jordan Apr 16 '18 at 16:02
  • @JayJordan At this time I am trying to understand why I cant access this.state or this.prevState in the handler. Once I can access the state, I will do actual work using the supplied index. Thanks. – J Cox Apr 16 '18 at 16:06
  • basically change all your functions to arrow function so *this* does not get lost within callbacks. See if that works – iceveda06 Apr 16 '18 at 16:11
  • 1
    Apart from following the duplicate to solve the binding issue, you need to change `` to ` props.activeFlagHandler(props.index)} />`, notice the onChange being changed to arrow function, otherwise the onChange function will be called on each render – Shubham Khatri Apr 16 '18 at 16:44
  • @ShubhamKhatri I had tried some variations similar to the duplicate you showed me, but can into an error related to not being able to define arrow functions as class properties. https://stackoverflow.com/questions/41398645/unable-to-use-arrow-functions-inside-react-component-class. You answer also made me realise I had a typo in my example code that I copy and pasted from the bigger application. I will edit this question with my new findings. Thanks. – J Cox Apr 16 '18 at 17:04
  • What issue do you face now – Shubham Khatri Apr 16 '18 at 17:09
  • @ShubhamKhatri I actually have it working now thanks to the link you provided and another response in here. I am going to edit the question shortly to reflect that. Thanks a bunch. – J Cox Apr 16 '18 at 17:11
  • Glad it worked out, consider upvoting the answers that helped you – Shubham Khatri Apr 16 '18 at 17:16

1 Answers1

1

Your assumption about this being change is right. In JavaScript this depends on from where the function is called (so called execution context). There are few ways to overcome this one is using the bind method like this

 constructor(){
   //...
   this.updateActiveFlag = this.updateActiveFlag.bind(this)
 }

That way this will stay the same (App) no matter where the function is called from (artificially change to declaration context). When passing bound method use the direct form.

 activeFlagHandler={props.activeFlagHandler}

passing the function itself not the arrow function form of

 activeFlagHandler={() => props.activeFlagHandler(props.index)}  

Which passes a new anonymous function.
Another problem in your code is what you actually pass to Item is an anonymous function which receives no arguments (() => ...) but inside Item you try to pass an argument to it onChange={props.activeFlagHandler(props.index)} which is unnecessary.
You have the right idea in my opinion in focusing on one lib at a time a great resource i strongly recommend is react official docs specifically thinking in react is very instructive.

Moti Korets
  • 3,738
  • 2
  • 26
  • 35