React In-Depth Series 3: Реквизиты и состояние

внешний интерфейс JavaScript React.js Immutable.js
React In-Depth Series 3: Реквизиты и состояние

Текст: Сюй Чао, автор книги «Дорога к продвинутому реагированию».

Разрешено публиковать, перепечатывать с указанием автора и источника


React In-Depth Series 3: Реквизиты и состояние

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

Основной идеей React является идея компонентизации, а определение компонентов React можно описать следующей формулой:

UI = Component(props, state)

Компонент вычисляет пользовательский интерфейс соответствующего интерфейса в соответствии с двумя параметрами реквизита и состояния. Видно, что свойства и состояние являются двумя важными источниками данных для компонентов.

Эта статья не является введением в основы использования свойств и состояния, а является попыткой объяснить свойства и состояние на более глубоком уровне и обобщить меры предосторожности при их использовании.

Природа реквизита и состояние

** В одном предложении props — это внешний интерфейс компонента, а state — внутренний интерфейс компонента. ** Компоненты могут ссылаться на другие компоненты, а ссылки между компонентами образуют древовидную структуру (дерево компонентов).Если нижнему компоненту необходимо использовать данные или методы верхнего компонента, верхний компонент может передать свойство props нижнего компонента Итак, props — это внешний интерфейс компонента. В дополнение к использованию данных, переданных компонентом верхнего уровня, компоненту также может потребоваться поддерживать данные и управлять ими, что является состоянием интерфейса внутри компонента. В соответствии с реквизитами внешнего интерфейса и состоянием внутреннего интерфейса компонент вычисляет пользовательский интерфейс соответствующего интерфейса.

Реквизиты и состояние компонента напрямую связаны с окончательным пользовательским интерфейсом, отображаемым компонентом. Основное различие между ними заключается в следующем: состояние является переменным и представляет собой набор состояний, поддерживаемых в компоненте для отражения изменений в пользовательском интерфейсе компонента; в то время как реквизиты являются свойством компонента только для чтения, и реквизиты не могут быть напрямую изменены внутри компонента. Если вы хотите изменить реквизиты, которые можно изменить только в компоненте верхнего уровня этого компонента. в компонентестатус вверхВ сценарии родительский компонент передает требуемое состояние дочернему компоненту через реквизиты дочернего компонента.

Как определить состояние

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

Следует ли использовать переменную, используемую в компоненте, в качестве состояния компонента, можно судить по следующим четырем критериям:

  1. Эта переменная получена из родительского компонента через реквизит? Если да, то это не государство.
  2. Сохраняется ли эта переменная на протяжении всего срока службы компонента? Если да, то это не государство.
  3. Можно ли вычислить эту переменную из существующих данных в состоянии или свойствах? Если да, то это не государство.
  4. Используется ли эта переменная в методе рендеринга компонента? еслинет, то это не состояние. В этом случае переменную лучше определить как одну из составляющихобщественная собственность(Свойства компонента, отличные от свойств и состояния), такие как таймеры, используемые в компонентах, должны быть определены непосредственно как this.timer вместо this.state.timer.

Важно помнить, что не все переменные, используемые в компоненте, являются его состоянием!Когда есть несколько компонентов, которые зависят от одного и того же состояния, общая практика такова.статус вверх, поместите это состояние в общий родительский компонент этих компонентов.

Как правильно изменить состояние

1. Состояние нельзя изменить напрямую.

При непосредственном изменении состояния компонент не будет повторно отправлять рендеринг. Например:

// 错误
this.state.title = 'React';

Правильный способ изменения - использоватьsetState():

// 正确
this.setState({title: 'React'});
2. Обновление состояния происходит асинхронно.

передачаsetState, состояние компонента меняется не сразу,setStateПросто поместите состояние, которое нужно изменить, в очередь, React оптимизирует реальное время выполнения, а React по соображениям производительности поставит его несколько раз.setStateМодификации состояния объединяются в одну модификацию состояния. Таким образом, вы не можете полагаться на текущее состояние для расчета следующего состояния. Когда модификация состояния действительно выполняется, зависимое this.state не обязательно будет самым последним состоянием, потому что React объединит несколько модификаций состояния в одно.В это время this.state по-прежнему равно состоянию до того, как произошли эти модификации. Еще одна вещь, которую следует отметить, это то, что вы не можете полагаться на текущие реквизиты для расчета следующего состояния, потому что обновление реквизитов также является асинхронным.

Например, для приложения электронной коммерции в нашей корзине при однократном нажатии кнопки покупки количество покупок будет увеличено на 1. Если мы нажмем кнопку дважды подряд, она будет вызвана дважды подряд. .this.setState({quantity: this.state.quantity + 1}), в случае слияния React несколько раз и изменения его один раз это эквивалентно выполнению следующего кода:

Object.assign(
  previousState,
  {quantity: this.state.quantity + 1},
  {quantity: this.state.quantity + 1}
)

В результате последняя операция перезаписывает предыдущую операцию, а конечный объем покупки увеличивается только на 1.

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

// 正确
this.setState((preState, props) => ({
  counter: preState.quantity + 1; 
}))
3. Обновление состояния — это процесс поверхностного слияния.

при звонкеsetStateПри изменении состояния компонента вам нужно передать только измененные переменные состояния, а не полное состояние компонента, поскольку обновление состояния компонента — это процесс поверхностного слияния. Например, состояние компонента:

this.state = {
  title : 'React',
  content : 'React is an wonderful JS library!'
}

Когда нужно изменить только состояниеtitle, только модифицированныйtitleперейти кsetState:

this.setState({title: 'Reactjs'});

React объединит новыеtitleв исходное состояние компонента, сохраняя при этом исходное состояниеcontent, комбинированное состояние:

{
  title : 'Reactjs',
  content : 'React is an wonderful JS library!'
}

Состояние и неизменность

React официально рекомендует рассматривать состояние как неизменяемый объект: с одной стороны, если this.state изменить напрямую, компонент не будет перерисовываться, с другой стороны, все состояния, содержащиеся в state, должны быть неизменяемыми объектами. Когда состояние в состоянии изменяется, мы должны воссоздать новое состояние вместо того, чтобы напрямую изменять исходное состояние. Итак, как создать новое состояние при изменении состояния? По типу состояния его можно разделить на три случая:

1. Тип состояния — неизменяемый тип (число, строка, логическое значение, ноль, неопределенный)

Это самый простой случай, потому что состояние является неизменяемым типом, и вы можете напрямую присвоить новое значение состоянию, которое нужно изменить. Чтобы изменить три состояния счетчика (числовой тип), заголовка (строковый тип) и успеха (логический тип):

this.setState({
  count: 1,
  title: 'Redux',
  success: true
})
2. Тип состояния — массив

Если у вас есть массив книг состояний, при добавлении книги в книги используйте метод массива concat или синтаксис распространения массива ES6:

// 方法一:使用preState、concat创建新数组
this.setState(preState => ({
  books: preState.books.concat(['React Guide']);
}))

// 方法二:ES6 spread syntax
this.setState(preState => ({
  books: [...preState.books, 'React Guide'];
}))

Принимая в качестве нового состояния часть элементов из книг, используйте метод slice массива:

// 使用preState、slice创建新数组
this.setState(preState => ({
  books: preState.books.slice(1,3);
}))

При фильтрации некоторых элементов из книг в качестве нового состояния используйте метод filter массива:

// 使用preState、filter创建新数组
this.setState(preState => ({
  books: preState.books.filter(item => {
    return item != 'React'; 
  });
}))

Будьте осторожны, чтобы не использовать push, pop, shift, unshift, splice и другие методы для изменения состояния типа массива, потому что эти методы изменяются на основе исходного массива, а concat, slice и filter вернут новый множество.

3. Тип состояния — Plain Object.

Если в государстве есть государственный собственник, структура выглядит следующим образом:

this.state = {
  owner = {
    name: '老干部',
    age: 30
  }  
}

При изменении состояния есть два способа:

1) Используйте метод ES6 Object.assgin

this.setState(preState => ({
  owner: Object.assign({}, preState.owner, {name: 'Jason'});
}))

2) Используйте синтаксис расширения объекта (object spread properties)

this.setState(preState => ({
  owner: {...preState.owner, name: 'Jason'};
}))

Подводя итог, можно сказать, что ключом к созданию нового состояния является отказ от методов, непосредственно изменяющих исходный объект, и вместо этого использование методов, возвращающих новый объект. Конечно, вы также можете использовать некоторые библиотеки Immutable JS, такие какImmutable.js, для достижения аналогичного эффекта.

Итак, почему React рекомендует, чтобы состояния компонентов были неизменяемыми объектами? С одной стороны, потому что неизменяемые объекты удобны для управления и отладки.обратитесь сюда; С другой стороны, по соображениям производительности, когда состояние компонента является неизменяемым объектом, мыshouldComponentUpdateВ методе вам нужно только сравнить ссылку на состояние, чтобы определить, действительно ли состояние изменилось, чтобы избежать ненужныхrenderвызов метода. Когда мы используем ReactPureComponentКогда необходимо убедиться, что состояние компонента является неизменяемым объектом, в противном случае в состоянии компонентаshouldComponentUpdateметод, сравнение состояний может пойти не так.

Следующее уведомление:

React In-Depth Series 4: Жизненный цикл компонентов


Рекомендация новой книги «Дорога к продвинутому React»

Автор: Сюй Чао

Окончил Чжэцзянский университет со степенью магистра, старший инженер по проектированию, многолетний опыт работы в энергетической компании Интернета вещей Envision Intelligence. 8-летний опыт разработки программного обеспечения, знакомство с крупными передовыми технологиями, богатый опыт разработки веб-интерфейса и мобильных терминалов, особенно глубокое понимание и практический опыт стека технологий React и технологии мобильной гибридной разработки.



В 2019 году оригинальная новая книга iKcamp «Практика разработки Koa и Node.js» была продана на JD.com, Tmall, Amazon и Dangdang!