Когда использовать обратный вызов React setState

191

При изменении состояния реагирующего компонента вызывается метод рендеринга. Следовательно, для любого изменения состояния действие может быть выполнено в теле методов рендеринга. Есть ли конкретный вариант использования для обратного вызова setState?

Сахил Джайн
источник
4
В настоящее время неясно, о чем вы спрашиваете. Можете ли вы включить код?
Дэвин Трайон
2
Обратный вызов setState предназначен для всего, что вы хотите сделать после того, как состояние ОПРЕДЕЛЕНО было изменено. Поскольку setState является асинхронным, если вы хотите вызвать fx и быть УВЕРЕНЫ, что новое состояние загружено, то для этого
нужен
3
Вариант использования для обратного вызова setState вполне понятен. Вы используете его, когда хотите, чтобы функция запускалась после того, как состояние SPECIFIC было обновлено. Если render()вместо этого вы включите эту функцию , она будет запускаться каждый раз при обновлении ЛЮБОГО состояния, что, вероятно, не то, что вам нужно. Это также сделает ваш код менее читабельным и логичным.
M3RS

Ответы:

225

Да, так как setStateработает таким asynchronousобразом. Это означает , что после вызова setStateв this.stateпеременной не сразу изменилось. так что если вы хотите выполнить действие сразу после установки состояния для переменной состояния, а затем вернуть результат, обратный вызов будет полезен

Рассмотрим пример ниже

....
changeTitle: function changeTitle (event) {
  this.setState({ title: event.target.value });
  this.validateTitle();
},
validateTitle: function validateTitle () {
  if (this.state.title.length === 0) {
    this.setState({ titleError: "Title can't be blank" });
  }
},
....

Приведенный выше код может работать не так, как ожидалось, поскольку titleпеременная может не мутировать до того, как будет выполнена проверка. Теперь вы можете задаться вопросом, что мы можем выполнить проверку в самой render()функции, но было бы лучше и чище, если бы мы могли обрабатывать это в самой функции changeTitle, поскольку это сделало бы ваш код более организованным и понятным

В этом случае обратный вызов полезен

....
changeTitle: function changeTitle (event) {
  this.setState({ title: event.target.value }, function() {
    this.validateTitle();
  });

},
validateTitle: function validateTitle () {
  if (this.state.title.length === 0) {
    this.setState({ titleError: "Title can't be blank" });
  }
},
....

Другим примером будет, когда вы хотите, dispatchи действия, когда состояние изменилось. вы захотите сделать это в render()обратном вызове, а не в том, как он будет вызываться при каждом повторном рендеринге, и, следовательно, возможны многие такие сценарии, когда вам потребуется обратный вызов.

Еще один случай API Call

Может возникнуть ситуация, когда вам нужно сделать вызов API на основе определенного изменения состояния, если вы сделаете это в методе рендеринга, он будет вызываться при каждом onStateизменении рендеринга или потому, что какой-то пропущенный элемент передается Child Componentизмененному.

В этом случае вы захотите использовать a setState callbackдля передачи обновленного значения состояния в вызов API

....
changeTitle: function (event) {
  this.setState({ title: event.target.value }, () => this.APICallFunction());
},
APICallFunction: function () {
  // Call API with the updated value
}
....
Шубхам Хатри
источник
3
Я понимаю, что это асинхронный характер. Мой вопрос был о том, есть ли что-то конкретное, что только обратный вызов setState может быть использован для этого, возможно, тело методов рендеринга может не поддерживать (что-то кроме, скажем, лучшей читабельности кода).
Sahil Jain
@SahilJain Validation - правильный пример, вы не захотите обрабатывать его в функции render (), потому что тогда он будет вызываться каждый раз, когда вы вносите какие-либо изменения в render (), вы хотите вызывать его только тогда, когда изменяется только вход, и, следовательно, в самой функции
Шубхам Хатри
React запрещает изменять состояние во время рендеринга. Так что его право помещать проверку в обратный вызов.
webdeb
if (this.title.length === 0) {должно быть this.state.title.length, верно?
Дмитрий Минковский
4
Первый вариант использования, вероятно, не очень хорошая идея. Обратные вызовы setState срабатывают после повторного рендеринга, поэтому вы вызываете двойной рендеринг без веской причины. Это как раз и есть цель аргумента функции (апдейтер). Вы можете просто запустить, setState(state => state.title.length ? { titleError: "Title can't be blank" } : null)и изменения будут складываться. Двойной рендеринг не требуется.
Р Эсмонд
47
this.setState({
    name:'value' 
},() => {
    console.log(this.state.name);
});
Араз Бабаев
источник
14
Спасибо за этот фрагмент кода, который может предоставить некоторую ограниченную, немедленную помощь. Надлежащее объяснение было бы значительно улучшить свою долгосрочную ценность , показывая , почему это хорошее решение проблемы, и сделает его более полезным для читателей будущих с другими подобными вопросами. Пожалуйста, измените свой ответ, чтобы добавить некоторые объяснения, в том числе предположения, которые вы сделали.
Мачавити
1
Когда вы хотите вызвать функцию после изменения состояния, вы можете использовать метод.
Араз Бабаев
Что делать, если вы хотите установить несколько свойств штата, таких как имя, имя и т. д.?
Сумант Варада
44

1. Случай использования, который приходит мне в голову, - это apiвызов, который не должен входить в рендер, потому что он будет работать для eachизменения состояния. И вызов API должен выполняться только при специальном изменении состояния, а не при каждом рендеринге.

changeSearchParams = (params) => {
  this.setState({ params }, this.performSearch)
} 

performSearch = () => {
  API.search(this.state.params, (result) => {
    this.setState({ result })
  });
}

Следовательно, для любого изменения состояния действие может быть выполнено в теле методов рендеринга.

Очень плохая практика , потому что render-метод должен быть чистым, это означает, что никакие действия, изменения состояния, вызовы API не должны выполняться, просто составьте свое представление и верните его. Действия должны выполняться только для некоторых событий. Рендер это не событие, а componentDidMountнапример.

webdeb
источник
25

Рассмотрим вызов setState

this.setState({ counter: this.state.counter + 1 })

IDEA

setState может быть вызван в асинхронной функции

Таким образом, вы не можете положиться на this. Если вышеупомянутый вызов был сделан внутри асинхронной функции, это thisбудет ссылаться на состояние компонента в этот момент времени, но мы ожидали, что это будет ссылаться на свойство внутри состояния во время вызова setState или начала асинхронной задачи. И поскольку заданием был асинхронный вызов, то это свойство могло со временем измениться. Таким образом, это ненадежно использоватьthis ключевое слово для ссылки на какое-либо свойство состояния, поэтому мы используем функцию обратного вызова, аргументы которой - previousState и props, что означает, что когда была выполнена асинхронная задача и пришло время обновить состояние с помощью вызова setState, prevState теперь будет ссылаться на состояние, когда setState еще не началось. Обеспечение надежности, что nextState не будет поврежден.

Неверный код: приведет к повреждению данных

this.setState(
   {counter:this.state.counter+1}
 );

Правильный код с setState с функцией обратного вызова:

 this.setState(
       (prevState,props)=>{
           return {counter:prevState.counter+1};
        }
    );

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

Я попытался объяснить это в codepen здесь CODE PEN

Аникет Джа
источник