Reputation: 9678
The syntax to update state
in React has change a lot. I'm trying to find the most simple and elegant way to initiate and update it.
Got this RN code:
const { quotes } = require('./quotes.json')
class QuoteScreen extends Component {
state = {
QuoteIndex: 0
}
render() {
return (
<Image ...>
<View ...>
...
<ButtonNextQuote
onPress={() => {
this.setState((prevState, props) => {
return {
QuoteIndex: (prevState.QuoteIndex + 1) % (quotes.length - 1)
}
})
}}
/>
</View>
</Image>
)
}
}
Would it be possible to reduce the updating of state
in the onPress
?
Would like to avoid calling an anonymous function twice but don't want to reference and bind a handler. Would also like to avoid using the return
..
Upvotes: 6
Views: 1345
Reputation: 26085
You'd want to avoid redefining the event handler each time render
runs, like others have said. I prefer to pass an object to setState
instead of a callback function:
_handlePress = () => {
this.setState({
QuoteIndex: (this.state.QuoteIndex + 1) % (quotes.length - 1)
});
};
onPress={this._handlePress}
This is easier to read because it's explicit where the state is coming from. Also, you don't have to keep track of extra callback functions.
Upvotes: 1
Reputation: 12709
I think a popular and good way to do this is to
Use "functional setState" by providing a callback to this.setState
to avoid some weird cases in batch state updates
"declare state changes separately from the component classes" so that the functions that get the new state can be reused and tested separately.
check this article for a great explanation of this approach featuring tweets by Dan Abramov
Example:
const { quotes } = require('./quotes.json')
// keep pure function outside component to be reusable, testable
const getQuoteIndex = ({ QuoteIndex }) => ({
QuoteIndex: (QuoteIndex + 1) % (quotes.length - 1)
})
class QuoteScreen extends Component {
state = {
QuoteIndex: 0
}
// arrow function will take care of this binding
handlePress = () => { this.setState(getQuoteIndex) }
render() {
return (
<Image ...>
<View ...>
...
<ButtonNextQuote
onPress={this.handlePress}
/>
</View>
</Image>
)
}
}
Upvotes: 2
Reputation: 1139
Here's how I would do that. I have used object destructuring in the first argument of setState's callback (prevState), and I have used a separate function instead of an anonymous one for performance reasons. Also, please note that I didn't need to manually bind the function to this
, because I have used an arrow function for it.
const { quotes } = require('./quotes.json')
class QuoteScreen extends Component {
state = {
QuoteIndex: 0
}
handleUpdateState = () => {
this.setState(({ QuoteIndex }) => ({
QuoteIndex: (QuoteIndex + 1) % (quotes.length - 1)
}));
}
render() {
return (
<Image ...>
<View ...>
...
<ButtonNextQuote
onPress={this.handleUpdateState}
/>
</View>
</Image>
)
}
}
Upvotes: 4
Reputation: 816384
I would store the update function in a variable outside the class, e.g.
const newState = ({QuoteIndex: i}) => ({QuoteIndex: (i + 1) % nQuotes});
(of course you can chose to define the function in any way you like, maybe "terseness" isn't as important to you anymore if it isn't inlined)
And then you can just call this.setState(newState)
:
onPress={() => this.setState(newState)}
Upvotes: 9