简体   繁体   中英

Elegant ES6 way to update state in React

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 ..

I would store the update function in a variable outside the class, eg

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)}

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> ) } } 

I think a popular and good way to do this is to

  1. Use "functional setState" by providing a callback to this.setState to avoid some weird cases in batch state updates

  2. "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>
    )
  }
}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM