Как да се справим със състоянието в React. Липсващите често задавани въпроси.

мотивиране

Напоследък се водят много дебати за това как да се управлява държавата в React. Някои твърдят, че setState () не работи както може да очаквате. Че трябва да екстернализирате състоянието с помощта на контейнер на състояние, наподобяващ Flux и напълно да избегнете състоянието на компонента.

От друга страна, има хора, загрижени, че тези погрешни представи могат да станат догма:

Добавянето на контейнер за външно състояние само за забавление или защото някои уроци ви казват да го направите, звучи като че не е добър критерий за техническо решение.

За да се изясня, няма нищо лошо в Redux, MobX или какъвто и да е контейнер за външно състояние, за който се сещате. Всъщност те са доста полезни и се предлагат с широка екосистема, която да отговаря на всички ваши нужди. Но, нещото е: може да не ви трябват изобщо или поне още.

Ако научавате React от нулата, Pete Hunt предложи най-добрия съвет, който можете да получите. Дори създателят на основния екип на Redux и React Дан Абрамов го препоръчва:

Това означава, че трябва да разберете как да се справите със състоянието по React начин, преди да мислите за Flux.

В случай, че стартирате приложение в реалния живот, можете да постигнете много без Flux. Разделяне на компонентите на две категории: контейнери и презентационни. По този начин ще спечелите поддръжка и многократна употреба. Освен това, в случай че трябва да въведете Flux по-късно, пътят на миграция ще бъде по-чист. Давам ви възможност да вземете решението в последния отговорен момент.

Дори ако вече използвате Flux, има случаи, когато трябва да използвате състоянието на компонента. Помислете за библиотека от компоненти, която искате да споделяте в проекти, където всички компоненти са самостоятелни и независими. Наистина не искате да имате контейнер за държава като зависимост тук.

Моят въпрос е:

  • В обществото има кръстосани мнения, погрешни схващания и липса на знания как да се управлява държавата в React.
  • За да обхванете цялата сила, която Ви дава React, от решаващо значение е солидно разбиране за това как да се справите със състоянието.
  • Не добавяйте още един сложен слой към приложението си, ако нямате нужда от него. Запомнете: простотата има значение.

Целта на следващия раздел често задавани въпроси е да смекчи тънкостите на състоянието на работа в React.

Често задавани въпроси.

Как работи държавата?

Реактивният компонент е като машина на състоянието, която представлява потребителски интерфейс. Всяко потребителско действие потенциално задейства промяна в тази машина на състоянието. Тогава новото състояние е представено от различен елемент React.

React съхранява състоянието на компонента в this.state. Можете да зададете началната стойност на this.state по два различни начина. Всеки от тях съответства на начина, по който създавате компонента:

// Използване на React.createClass
var Counter = React.createClass ({
    getInitialState: function () {
        връщане {брояч: 0};
    }
    ...
});
// Използване на класове ES6
брояч клас разширява React.Component {
    конструктор (подпори) {
        супер (подпори);
        this.state = {counter: 0};
    }
    ...
}

Състоянието на компонента може да бъде променено като се извиква:

this.setState (данни, обратен сигнал);

Този метод извършва плитко сливане на данни в this.state и рендерира компонента. Аргументът на данни може да бъде обект или функция, която връща обект, съдържащ ключове за актуализиране. Опционалният обратен разговор - ако е предвиден - ще бъде извикан, след като компонентът завърши повторно изобразяване. Рядко ще ви се наложи да използвате този обратен сигнал, тъй като React ще се погрижи да поддържа актуалността на потребителския ви интерфейс.

Нека да разгледаме един пример:

Какво трябва да поддържам в състояние React?

Dan Abramov отговори на този въпрос с един туит:

По принцип той казва, че не дръжте състоянието изчислено от реквизити, нито състоянието, което не се използва в метода render (). Да дадем пример:

// Не дублирайте данни от реквизити в състояние
// Antipattern
клас Компонент разширява React.Component {
    конструктор (подпори) {
        супер (подпори);
        this.state = {съобщение: props.message};
    }
    
    render () {
        връщане 
{this.state.message}
;     } }

Проблемът с горния пример е, че той ще зададе състоянието само когато компонента е създаден за първи път. Когато пристигнат нови реквизити, състоянието остава същото, поради което потребителският интерфейс не се актуализира. След това трябва да актуализирате състоянието в компонентWillReceiveProps (), което води до дублиране на източника на истината. Можете да направите по-добре, като избягвате тази ситуация:

// По-добър пример
клас Компонент разширява React.Component {
    render () {
        връщане 
{this.props.message}
;     } }

Същото се отнася, когато държите състояние въз основа на изчисляване на реквизит

// Не задържайте състояние въз основа на изчислението на подпори
// Antipattern
клас Компонент разширява React.Component {
    конструктор (подпори) {
        супер (подпори);
        this.state = {fullName: `$ {props.name} $ {props.lastName}`};
    }
    
    render () {
        връщане 
{this.state.fullName}
;     } }
// По-добър подход
клас Компонент разширява React.Component {
    render () {
        const {name, lastName} = this.props;
        return 
{`$ {name} $ {lastName}`}
;     } }

Въпреки че няма нищо лошо в задаването на първоначално състояние въз основа на реквизит, ако поясните, че това са само семенни данни:

// Не е антипатерн
клас Компонент разширява React.Component {
    конструктор (подпори) {
        супер (подпори);
        this.state = {count: props.initialCount};
        this.onClick = this.onClick.bind (това);
    }
    
    render () {
        връщане 
{this.state.count}
;     }          onClick () {         this.setState ({count: this.state.count + 1});     } }

Не на последно място:

// Не задържайте състояние, което не използвате за изобразяване.
// Води до ненужни рендери и други несъответствия.
// Antipattern
клас Компонент разширява React.Component {
    конструктор (подпори) {
        супер (подпори);
        this.state = {брой: 0};
    }
    
    render () {
        връщане 
{this.state.count}
;     }          компонентDidMount () {         интервал на const = setInterval (() => (             this.setState ({count: this.state.count + 1})         ), 1000);
        this.setState ({интервал});
    }
    компонентWillUnmount () {
        clearInterval (this.state.interval);
    }
}
// По-добър подход
клас Компонент разширява React.Component {
    конструктор (подпори) {
        супер (подпори);
        this.state = {брой: 0};
    }
    
    render () {
        връщане 
{this.state.count}
;     }          компонентDidMount () {         this._interval = setInterval (() => (             this.setState ({count: this.state.count + 1})         ), 1000);     }
    компонентWillUnmount () {
        clearInterval (this._interval);
    }
}

Вярно ли е, че setState () е асинхронен?

Краткият отговор: Да.

По принцип, когато извиквате setState () React планира актуализация, изчисленията се забавят, докато е необходимо. Документацията на React е малко подвеждаща по този въпрос:

Явно тези две изречения са в противоречие. Нека експериментираме малко, за да видим какво ще се случи:

клас Компонент разширява React.Component {
    конструктор (подпори) {
        супер (подпори);
        this.state = {брой: 0};
        this.onClick = this.onClick.bind (това);
    }
    
    render () {
        връщане 
{this.state.count}
;     }          onClick () {         this.setState ({count: this.state.count + 1});         console.log (this.state.count);     } }

Когато направите този компонент и взаимодействате с него, ще видите, че стойностите, показани в конзолата, са стойностите от предишното състояние. Това е така, защото React е собственик на събитието и знае достатъчно, за да подготви актуализацията. Резултатът: мутация на асинхронно състояние.

Но какво се случва, ако събитието идва от външен източник?

// Извикването setState () два пъти в един и същ контекст на изпълнение е лоша // практика. Тук се използва за илюстрация. Вместо това използвайте // атомна актуализация в реален код
клас Компонент разширява React.Component {
    конструктор (подпори) {
        супер (подпори);
        this.state = {брой: 0};
    }
    
    render () {
        връщане 
{this.state.count}
;     }          компонентDidMount () {         this._interval = setInterval (() => {             this.setState ({count: this.state.count + 1});             console.log (this.state.count);             this.setState ({count: this.state.count + 1});             console.log (this.state.count);         }, 1000);     }
компонентWillUnmount () {
        clearInterval (this._interval);
    }
}

Направете! Връща съществуващата стойност, както предлага документацията, дори извиква setState () два пъти в същия контекст на изпълнение. Това е така, защото React не знае достатъчно, за да изведе актуализацията и трябва да актуализира състоянието възможно най-скоро.

Това е сложно, предлагам винаги да се отнасяте към setState () като към асинхронен и ще избегнете проблеми.

Чух, че при определени обстоятелства повикването setState () не задейства повторно изобразяване. Кои са тези обстоятелства?

  1. Когато извикате setState () в компонента компонентWillMount () или компонентWillRecieveProps (), той няма да задейства допълнително повторно изобразяване. React партиди на актуализациите.
  2. Когато върнете фалшиво от trebaComponentUpdate (). По този начин методът render () се пропуска напълно заедно с компонентWillUpdate () и компонентDidUpdate ().
Забележка:
Ако искате да копаете повече в жизнения цикъл на компонента, вече написах публикация за него.

Заключения

Правилното боравене със състояние в React може да бъде предизвикателство. Надявам се досега да имате по-ясна картина.

Ако имате въпрос, на който не сте получили отговор, не се колебайте да го зададете в коментари или чрез Twitter. Ще се радвам да получите вашите отзиви и ще включите въпроса в секцията с често задавани въпроси.

Надявам се този пост да ви помогне да задълбочите знанията си за React. Ако е така, моля препоръчайте го, за да достигне до повече хора.