Чому setState в reactjs Async замість Sync?


126

Я щойно виявив, що this.setState()функція реагування в будь-якому компоненті є асинхронною або викликається після завершення функції, в яку вона була викликана.

Зараз я шукав і знайшов цей блог ( setState () Операція мутації стану може бути синхронною у ReactJS )

Тут він виявив, що setStateце async (викликається, коли стек порожній) або синхронізація (викликається, як тільки викликається), залежно від того, як відбулася зміна стану.

Зараз ці дві речі важко засвоюються

  1. У блозі setStateфункція називається всередині функції updateState, але те, що викликало updateStateфункцію, - це не те, про що може знати функція, що називається.
  2. Чому вони роблять setStateасинхронізацію, оскільки JS є однопоточною мовою, і цей setState не є WebAPI або викликом сервера, тому це потрібно робити лише в потоці JS. Чи роблять вони це так, щоб Re-Rendering не зупиняв усіх слухачів події та інше, або є якась інша проблема дизайну.


1
Я написав сьогодні статтю, яка допомагає описати трохи клімату навколо setState: medium.com/@agm1984/…
agm1984

Відповіді:


156

Ви можете викликати функцію після оновлення значення стану:

this.setState({foo: 'bar'}, () => { 
    // Do something here. 
});

Крім того, якщо у вас є багато станів, які потрібно оновити відразу, згрупуйте їх все в одному і тому ж setState:

Замість:

this.setState({foo: "one"}, () => {
    this.setState({bar: "two"});
});

Просто зробіть це:

this.setState({
    foo: "one",
    bar: "two"
});

17
так добре, у нас є функція callBack, яку ми можемо використовувати, але це не питання.
Ануп

12
Сподіваємось, це допоможе комусь іншому наткнутися на це питання.
JoeTidee

2
ya dat може бути корисним
Anup

97

1) setStateдії є асинхронними та збираються для підвищення продуктивності. Це пояснено в документації Росії setState.

setState () не одразу мутує цей стан, але створює перехід у стані очікування. Доступ до цього.state після виклику цього методу потенційно може повернути наявне значення. Немає гарантії синхронної роботи викликів до setState, і виклики можуть підбиратися для підвищення продуктивності.


2) Чому вони роблять setState async, оскільки JS є єдиною потоковою мовою, і це setStateне виклик WebAPI або сервер?

Це тому, що setStateзмінює стан і викликає рендерінг. Це може бути дорогою операцією, і якщо синхронізувати її, браузер не може відповідати.

Таким чином, виклики setState є асинхронними, а також пакетними для кращого досвіду та продуктивності інтерфейсу.


59
Якщо вам потрібно забезпечити впорядкування подій після виклику setState, ви можете передати функцію зворотного дзвінка. this.setState({ something: true }, () => console.log(this.state))
ianks

1
Дякую @Sachin за пояснення. Однак я все ще сумніваюся, чи може це бути синхронно, як пояснює блог?
Аджай Гаур

2
Ще одне дурне дизайнерське рішення у реакції. Зробіть оновлення стану синхронним, а візуалізацію асинхронним. Ви можете групувати візуалізацію, але я хочу мати можливість встановити щось настільки примітивне, як змінні стану, без того, щоб мати справу з умовами перегонів.
ig-dev

Чому б не дозволити встановити опцію, щоб зробити fucntion або асинхронізацією, або синхронізацією? Це було б корисною функцією
Кодування

Очевидно, я не в команді реагування, але одна причина, на мій погляд, зробити так, щоб оновлення стану асинхронізовано, це те, що браузери є єдиними потоками. Операції синхронізації можуть зробити інтерфейс користувача невідповідним і не є хорошим кандидатом на інтерфейс користувача.
Сечіни

16

Я знаю, що це питання давнє, але воно вже давно викликає багато плутанини у багатьох користувачів reactjs, включаючи мене. Нещодавно Дан Абрамов (з команди реагуючих) просто написав чудове пояснення, чому природа setStateасинхронна:

https://github.com/facebook/react/isissue/11527#issuecomment-360199710

setStateмається на увазі асинхронність, і є кілька дійсно вагомих причин для цього у пов'язаному поясненні Дана Абрамова. Це не означає, що він завжди буде асинхронним - це в основному означає, що ви просто не можете залежати від того, чи буде він синхронним . ReactJS враховує багато змінних у сценарії, в якому ви змінюєте стан, щоб вирішити, коли stateнасправді слід оновити, а ваш компонент повторно відредагувати.
Простий приклад для демонстрації цього полягає в тому, що якщо ви викликаєте setStateяк реакцію на дію користувача, то state, ймовірно, оновлення буде оновлено негайно (хоча, знову ж таки, ви не можете розраховувати на нього), тому користувач не відчує жодної затримки , але якщо ви телефонуєтеsetState у відповідь на відповідь на виклик Ajax або іншу подію, яка не викликається користувачем, стан може бути оновлений з невеликою затримкою, оскільки користувач не буде відчувати цю затримку, і це покращить продуктивність, чекаючи пакетне оновлення кількох станів разом і повторно передайте DOM.


ви не позначили жодного відповіді як правильного. Люди публікують, як її обійти. Не відповідь на поставлене запитання. ця стаття здається гарною.
Ануп

@Anup Відповідь трохи складніша, ніж просто "async" або "sync". Це завжди слід трактувати як "асинхронізацію", але в деяких випадках може діяти як "синхронізація". Я сподіваюся, що я пролити трохи світла на вас.
gillyb

8

Хороша стаття тут https://github.com/vasanthk/react-bits/blob/master/patterns/27.passing-function-to-setState.md

// assuming this.state.count === 0
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
// this.state.count === 1, not 3

Solution
this.setState((prevState, props) => ({
  count: prevState.count + props.increment
}));

або пройти зворотний дзвінок this.setState ({.....},callback)

https://medium.com/javascript-scene/setstate-gate-abc10a9b2d82 https://medium.freecodecamp.org/functional-setstate-is-the-future-of-react-374f30401b6b



1

Уявіть собі збільшення лічильника в якомусь компоненті:

  class SomeComponent extends Component{

    state = {
      updatedByDiv: '',
      updatedByBtn: '',
      counter: 0
    }

    divCountHandler = () => {
      this.setState({
        updatedByDiv: 'Div',
        counter: this.state.counter + 1
      });
      console.log('divCountHandler executed');
    }

    btnCountHandler = () => {
      this.setState({
        updatedByBtn: 'Button',
        counter: this.state.counter + 1
      });
      console.log('btnCountHandler executed');
    }
    ...
    ...
    render(){
      return (
        ...
        // a parent div
        <div onClick={this.divCountHandler}>
          // a child button
          <button onClick={this.btnCountHandler}>Increment Count</button>
        </div>
        ...
      )
    }
  }

Існує обробник лічильників, приєднаний як до батьківського, так і дочірнього компонентів. Це зроблено цілеспрямовано, щоб ми могли виконати setState () двічі в межах одного і того ж контексту, що перетворюється на події клацання, але зсередини двох різних обробників.

Як ми могли б уявити, подія одного клацання на кнопці тепер запустить обидва ці обробники, оскільки подія переходить від цільового до самого зовнішнього контейнера під час фази бульбашки.

Таким чином, btnCountHandler () виконує спочатку, очікується, що він зросте до 1, а потім виконує divCountHandler (), очікується, що кількість зросте до 2.

Однак нараховують лише кроки до 1, як це можна перевірити в інструментах React Developer.

Це доводить, що реагують

  • черга всіх викликів setState

  • повертається до цієї черги після виконання останнього методу в контексті (divCountHandler в цьому випадку)

  • об'єднує всі мутації об'єкта, що відбуваються в межах декількох викликів setState в одному контексті (усі виклики методу в рамках однієї фази події є одним і тим же контекстом, наприклад,) в один синтаксис мутації об'єкта (об'єднання має сенс, тому що ми можемо самостійно оновлювати властивості стану в setState () в першу чергу)

  • і передає його в один єдиний setState () для запобігання повторної рендерингу через кілька викликів setState () (це дуже примітивний опис пакетної обробки).

Код результату, керований реакцією:

this.setState({
  updatedByDiv: 'Div',
  updatedByBtn: 'Button',
  counter: this.state.counter + 1
})

Для припинення такої поведінки замість передачі об'єктів як аргументів методу setState передаються зворотні виклики.

    divCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByDiv: 'Div',
              counter: prevState.counter + 1
            };
          });
          console.log('divCountHandler executed');
        }

    btnCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByBtn: 'Button',
              counter: prevState.counter + 1
            };
          });
      console.log('btnCountHandler executed');
    }

Після того, як останній метод закінчує виконання, і коли react повертається для обробки черги setState, він просто викликає зворотний виклик для кожної черги setState, переходячи в попередній стан компонента.

Цей спосіб реагування гарантує, що останній зворотний виклик у черзі отримує оновлення стану, на який поклали всі попередні аналоги.


0

Так, setState () є асинхронним.

За посиланням: https://reactjs.org/docs/react-component.html#setstate

  • Реакція не гарантує, що зміни держави будуть застосовані негайно.
  • setState () не завжди оновлює компонент.
  • Подумайте про setState () як запит, а не як негайну команду для оновлення компонента.

Тому що вони думають
За посиланням: https://github.com/facebook/react/isissue/11527#issuecomment-360199710

... ми погоджуємось, що синхронно повторне відображення setState () було б неефективним у багатьох випадках

Асинхронний setState () дуже ускладнює життя для тих, хто починає і навіть переживає, на жаль,:
- несподівані проблеми з рендерінгом: затримка візуалізації або відсутність надання (на основі програмної логіки)
- передача параметрів є великою справою
серед інших питань.

Нижче наведено приклад:

// call doMyTask1 - here we set state
// then after state is updated...
//     call to doMyTask2 to proceed further in program

constructor(props) {
    // ..

    // This binding is necessary to make `this` work in the callback
    this.doMyTask1 = this.doMyTask1.bind(this);
    this.doMyTask2 = this.doMyTask2.bind(this);
}

function doMyTask1(myparam1) {
    // ..

    this.setState(
        {
            mystate1: 'myvalue1',
            mystate2: 'myvalue2'
            // ...
        },    
        () => {
            this.doMyTask2(myparam1); 
        }
    );
}

function doMyTask2(myparam2) {
    // ..
}

Сподіваюся, що це допомагає.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.