This.setState не является функцией

У меня есть следующий компонент, который поддерживает состояние, которое обновляется, когда событие запускается в конкретном элементе, и когда состояние обновляется, оно передается в качестве опоры другому компоненту. В настоящее время я пытаюсь понять, почему возникает следующая ошибка: "this.setState не является функцией", скорее всего, не привязана к правильному контексту. Но я не уверен в этом, правильно ли я это делаю?

export default class SearchBox extends Component{

    constructor(){
        super()
        console.log("search box imported");
        this.state = {
            results:[]
        };
    }

    //this.setState({result: arrayExample})

    searchGif(event) {
        if(event.keyCode == 13){
            let inputVal = this.refs.query.value;
            let xhr = new XMLHttpRequest();
            xhr.open('GET', 'http://api.giphy.com/v1/gifs/search?q='+inputVal+'&api_key=dc6zaTOxFJmzC', true);
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4 && xhr.status == 200){
                    // this.response = JSON.parse(xhr.responseText);
                    let returnedObj = JSON.parse(xhr.responseText);
                    //console.log(returnedObj.data);
                    let response = returnedObj.data.map(function(record){
                        let reformattedArray =  {   key: record.id,
                                                    id: record.id,
                                                    thumbnailUrl: record.images.fixed_height_small_still.url
                                                };
                                                return reformattedArray;
                    });
                    console.log(response);
                    this.setState({results: response});
                }
            }
            xhr.send();
        }
    }


    render(){

        return(
            <div>   
                <input className="search-input" ref="query" onKeyDown={this.searchGif.bind(this)} name="search" placeholder="Search"/>
                <GifSwatch data={this.state.results} />
            </div>
        );
    }
}

EDIT: Я просто понял, что контекст изменяется, когда функция "onreadyStateChange", поэтому я сделал следующее в searchGif

searchGif(){
   //other logic
   var self = this;
   xhr.onreadystatechange = function(){
    //ajax logic
    self.setState({results: repsonse});
   }
}

Ответ 1

Вы теряете класс React в this контексте. Привязать его, а также связать его в функции асинхронного обратного вызова тоже.

constructor(props){
    super(props);
    console.log("search box imported");
    this.state = {
        results:[]
    };
    this.searchGif = this.searchGif.bind(this);
}

searchGif(event) {
    // ... code here
    xhr.onreadystatechange = () => {
    // ... code here
        this.setState();
    }
}

удивительная вещь о функциях стрелок, они связывают ваш контекст для вас, и синтаксис также удивительный. Недостатком является поддержка браузера. Убедитесь, что у вас есть полифайл или процесс компиляции, чтобы скомпилировать его в синтаксис ES5 для кросс-браузерной производительности.

Если вы не можете сделать ни то, ни другое, просто сделайте теневую переменную вашего контекста this вне функции async onreadystatechange и используйте ее вместо this.


редактировать

Большинство компиляторов в наши дни обрабатывают методы привязки к классу стрелками (без указания преобразования babel... и т.д.), Вы также можете назначать состояние таким образом без конструктора

export default class SearchBox extends Component {
  state = { 
    results: []
  }

  searchGif = (event) => {
    // ... code here
    xhr.onreadystatechange = () => {
    // ... code here
        this.setState();
    }
  }
  render() {
    // ...
  }
}