简体   繁体   中英

How do I keep context in react without stringing .bind(this)?

I'm using react to retrieve data from parse, manipulate it in my own function, and then update a component in the render.

The problem is that I can't update the state within my own, convoluted function unless I attach a string of bind(this). The entire component looks like this:

React.Component({

getInitialState: function () {
 return{ 
  isloading:true
 }
},

componentDidMount: function(){
 this.myStupidFunction()
}, 

myStupidFunction : function(){
(
 (
  (nested parse queries that eventually ... 
 return an object and set isloading:false).bind(this))
.bind(this))
.bind(this)
},

render: function (){
  if (this.state.isloading) {
   return(
    <Text "...isloading"/>
   )
  } else {
   return(
    ...actually return important stuff...
   )
  }
 }
})

What is the smarter way to do this? Do I need to really .bind(this) for every nested function?

There are a few ways to maintain the context of your component.

Use ES6 Arrows

If you use ES6 arrows to define your functions. Arrow functions force the inner context of this to be the same as the outer context, regardless of how the function is called.

parse.find({
  success: results => {
    // this is correct
    console.log(this);
  }
});

I think this is the most elegant solution, but not all browsers support arrow functions yet .

Use Component Methods

React automatically binds this into each of the top level methods on your component. They are always guaranteed to have the correct context.

onSuccess: function() {
  // this is correct
  console.log(this);
},
componentWillMount: function() {
  parse.find({
    success: this.onSuccess
  });
}

This is also fairly elegant, in my opinion. It lets React deal with the messiness of context whilst you just write code. However, it can mean that you end up with far too many methods at the top level of your component, so use it sparingly.

As an Argument

Some functions, such as map allow you to optionally pass a context to use as this as a final argument. This allows you to maintain the correct context without .bind(this) .

data.map(function() {
  console.log(this);
  // this is correct
}, this);

This only works for some methods, so it's not really a universal solution.

Alias this

Create a reference to this and use that instead.

var __this__ = this;

parse.find({
  success: results => {
    // __this__ is correct
    console.log(__this__);
  }
});

This hack has been around forever in Javascript, but I don't think it's a great way to solve the problem.

Use ES7 Function Bind

For those who like to Javascript on the edge, you could also achieve this using the ES7 function bind syntax proposal — currently implemented in Babel .

parse.find({
  success: this::function(results) {
    // this is correct
    console.log(this);
  }
});

This requires using experimental proposal stage features of ES7. You may not want to start using it yet, but it's definitely interesting to be aware of. The value on the left hand side will be bound into the function on the right, as this .

Use a closure at the beginning of the function to capture this . It will be usable in any nested structure. The conventional names for such a closure are self _this and that . I prefer self .

myStupidFunction : function(){
  var self = this;
  someAsyncCall(1,2, function(result) {
     //some nested stuff
     anotherAsyncCall(1,2 function(innerResult) {
       self.setState(innerResult);
     });
  });
}

one solution could be using local variable

myStupidFunction:function(){
    var that=this
    ParseReact.Mutation.Create('Place', {
                    name: 'New Place',
                    user: Parse.User.current()
                })
                .dispatch()
                .then(function() {
                    that.refreshQueries();
                });
}

Using ES7 Property Initalizer Syntax, currently implemented in Babel. The key is the methodName = () => { //method return } You can read more here .

 import React from 'react'; export default class Note extends React.Component { constructor(props) { super(props); this.state = { editing : false } } render() { const editing = this.state.editing; return ( <div>{ editing ? this.renderEdit() : this.renderTask() }</div> ) } renderEdit = () => { return ( <input type="text" className="edit-input" autoFocus={true} defaultValue={this.props.task} onBlur={this.finishEdit} onKeyPress={this.checkEnter} /> ) } renderTask = () => { const onDelete = this.props.onDelete; return ( <div onClick={this.edit}> <span className="task-body">{this.props.task}</span> { onDelete ? this.renderDelete() : null } </div> ) } renderDelete = () => { return ( <button className="delete-btn" onClick={this.props.onDelete}>x</button> ) } edit = () => { this.setState({ editing : true }) } checkEnter = (e) => { if(e.key === "Enter") { this.finishEdit(e); } } finishEdit = (e) => { this.props.onEdit(e.target.value); this.setState({ editing : false }) } } 

// Note: Sample class from project above.

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