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;
1 Answer 1
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")} />
}
}
5 Comments
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.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
.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
.Explore related questions
See similar questions with these tags.