0

I have finally gotten into using react and ES6 and it's going well but I am finally stumped and could use some direction.

I have got my head around binding this to a method to reference the class, but I am trying to go a bit deeper. Take this for example...which works as expected:

class App extends Component {
 state = {
 myFirstState: false,
 };
 handleMyFirstState = () => {
 this.setState( { myFirstState : true } );
 };
 render() {
 return (
 <MyComponent handleMySate={ this.handleMyState } />
 );
 }
}
export default App;

As the amount of methods increased I decided NOT to pass each method individually as props and to group them in an object first, and to just pass the object as a whole, as a prop. Like So...

class App extends Component {
 state = {
 myFirstState: false,
 mySecondState: false
 };
 handleMyFirstState = () => {
 this.setState( { myFirstState : true } );
 };
 handleMySecondSate = () => {
 this.setState( { mySecondState : true } );
 };
 render() {
 const handleStates = {
 first : this.handleMyFirstState,
 second : this.handleMySecondState
 }
 return (
 <MyComponent handleStates={ handleStates } />
 );
 }
}
export default App;

Now, I am trying to avoid redundant code and just build the methods as one object with functions as properties before the render begins. Pretty much like this...

class App extends Component {
 state = {
 myFirstState: false,
 mySecondState: false
 };
 handleStates = {
 // Here is where 'this' does not reference the App class
 // I get results from the console log but setstate doesn't pass correctly
 first : () => { console.log("First Triggered"); this.setState( { myFirstState : true } ); },
 second : () => { console.log("Second Triggered"); this.setState( { mySecondState : true } ); }
 };
 render() {
 return (
 <MyComponent handleStates={this.handleStates} />
 );
 }
}
export default App;
// I trigger the function like this within MyComponent and I get the console log, but `this.setState` breaks.
<Button onClick={ this.props.handleState.first } >Handle First</button>

I have successfully triggered the functions from the child component ,<MyComponent/>, using the latter code, but this no longer refers to the class and I can't figure out how to bind this to handleStates since it's not a function.

Is this just not possible or is there another way to handle what I am trying to achieve?

Thank you in advance!

ADDITIONAL

If I move the handleStates into the render() it works just fine...how could that be?

class App extends Component {
 state = {
 myFirstState: false,
 mySecondState: false
 };
 render() {
 const handleStates = {
 first : () => { this.setState( { myFirstState : true } ); },
 second : () => { this.setState( { mySecondState : true } ); }
 };
 return (
 <MyComponent handleStates={this.handleStates} />
 );
 }
}
export default App;
asked Sep 5, 2018 at 2:04

1 Answer 1

2

First, in the second example, you pass this.handleStates as the value for the prop handleStates, but it's undefined. You built handleStates as a local variable, and thus you want your props to reference that local variable:

<MyComponent handleStates={handleStates} />

For your third (last) example, your issue is even simpler: you defined handleStates as an attribute on this which is assigned an object, itself with two attributes, first and second, each of which have a function as their value.

When you ultimately pass this.handleStates to MyComponent, you're passing an object, not a function. If you want to call one of first or second from MyComponent, you can do so like this:

this.props.handleStates.first()

Which has the desired result of updating the state in App.

For what it's worth, there's a more common pattern for this: simply pass a single updater function as the prop, named according to what it does:

class Sandwich extends React.Component {
 this.state = {
 bread: "",
 meat: "",
 veggie: "",
 }
 updateSandwich = (component, selection) => {
 this.setState({ [component]: selection })
 }
 render() {
 return(<IngredientSelector updateSandwich={this.updateSandwich} />)
 }
}
class IngredientSelector extends React.Component {
 return(){
 <button value="Rye" onClick={() => this.updateSandwich("bread", "rye")} />
 <button value="Wheat" onClick={() => this.updateSandwich("bread", "wheat")} />
 <button value="Ham" onClick={() => this.updateSandwich("meat", "ham")} />
 <button value="Turkey" onClick={() => this.updateSandwich("meat", "turkey")} />
 }
}
answered Sep 5, 2018 at 2:20

5 Comments

Thanks for the response, the second example was a typo as I was creating an example to show for the question. I will fix that. I know how to call the functions within the components in example three. I set a console log within each function which triggers, it's just the "this" reference which has got me confused.
I like the example approach, but, again, my example is pretty dumbed down. It's not always just setting the state but I will take your example to heart. The crux of the issue is the this reference.
this depends on the context. In the third example, those functions are defined as closures. The value for this is already bound to your App component. It doesn't matter where they're called, this won't change as long as you don't re-bind them.
Can you expound on that? I updated my third example to point to the issue a bit better. I assumed I had to bind this within the object property function since it's never bound up until that point. Without the binding, as in example one or two, this does not reference the app.
Arrow functions do not have their own this value. The value of this inside an arrow function is always inherited from the enclosing scope. So in your example, this referred to the instance of your component. With normal closures you don't get this behavior and instead this is going to be window or undefined.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.