предисловие
Прежде всего, приглашаю всех подписаться на меняБлог на гитхабе, это можно расценивать как небольшое поощрение для меня.В конце концов, я не могу получить деньги, чтобы что-то написать.Я могу придерживаться своего собственного энтузиазма и всеобщего поощрения.Я надеюсь, что все обратят больше внимания! Я давно не писал React, и я обнаружил, что даже Контекст изменился.Внезапно я чувствую, что только что подключился к Интернету в деревне.Может быть, знания, упомянутые в статье, устарели, и это это только мой собственный опыт обучения.
Context
Для разработчиков React Context должен быть знакомой концепцией, но до версии 16.3 React официально объявил ее устаревшей, утверждая, что эта функция является экспериментальным API и может быть удалена из более поздних версий. Но на практике многие сторонние библиотеки основаны на этой фиче, например: react-redux, mobx-react.
Как показано на дереве вышеуказанного компонента, существует множество компонентов между компонентом A и компонентом B. Если компонент a хочет пройти свойство для компонента B, вы должны использовать реквизиты для передачи свойства от компонента A через серию промежуточных компонентов к Окончательный компонент. Компонент B. Этот код не только очень неприятно, но, что более важно, промежуточные компоненты могут вообще не использовать это свойство, но должны предпринять ответственность за передачу, которую мы не хотим видеть. Цель контекста состоит в том, чтобы решить этот сценарий, чтобы мы могли непосредственно пропускать свойства от компонента до B компонента.
Legacy Context
Упомянутая здесь старая версия Context относится к свойству Context, предоставленному в версии до React 16.3. На мой взгляд, этот Context используется согласованным образом. Поскольку поставщик свойств (Provider) должен явно объявить, к каким свойствам можно получить доступ через иерархию, и должен объявить тип этих свойств. Как пользователь свойства (Consumer) также необходимо явно объявить тип этих свойств. В официальной документации приведен следующий пример:
import React, {Component} from 'react';
import PropTypes from 'prop-types';
class Button extends React.Component {
static contextTypes = {
color: PropTypes.string
};
render() {
return (
<button style={{background: this.context.color}}>
{this.props.children}
</button>
);
}
}
class Message extends React.Component {
render() {
return (
<div>
{this.props.text} <Button>Delete</Button>
</div>
);
}
}
class MessageList extends React.Component {
static childContextTypes = {
color: PropTypes.string
};
getChildContext() {
return {color: "red"};
}
render() {
const children = this.props.messages.map((message) =>
<Message text={message.text} />
);
return <div>{children}</div>;
}
}
Мы видим, чтоMessageList
по функцииgetChildContext
Явно заявить о предоставленииcolor
свойства и через статические свойстваchildContextTypes
Он объявляет тип свойства. а такжеButton
через статические свойстваcontextTypes
Тип используемого атрибута объявляется, и они согласовывают передачу информации об атрибутах между уровнями. Контекст действительно очень удобен для решения ситуации с передачей атрибутов между слоями, но почему он официально не рекомендуется?
первыйContext
Использование React противоречит логике повторно используемых компонентов React.По мнению React, все компоненты должны иметь характеристики повторного использования, но именно из-за введения Context использование повторного использования компонентов стало строгим. Возьмите приведенный выше код в качестве примера, если вы хотите повторно использоватьButton
Компоненты должны быть предоставлены в верхнем компонентеString
тип цветаContext
, поэтому требования к повторному использованию ужесточаются. И что еще более важно, когда вы пытаетесь изменить значение контекста, может возникнуть неопределенное состояние. Возьмем пример, мы поместим вышеMessageList
С небольшой модификацией содержимое контекста может быть изменено динамически:
class MessageList extends React.Component {
state = {
color: "red"
};
static childContextTypes = {
color: PropTypes.string
};
getChildContext() {
return {color: this.state.color};
}
render() {
const children = this.props.messages.map((message) =>
<Message text={message.text} />
);
return (
<div>
<div>{children}</div>
<button onClick={this._changeColor}>Change Color</button>
</div>
);
}
_changeColor = () => {
const colors = ["red", "green", "blue"];
const index = (colors.indexOf(this.state.color) + 1) % 3;
this.setState({
color: colors[index]
});
}
}
В приведенном выше примере мыMessageList
Контекст компонента предоставленcolor
свойство изменено наstate
свойство, когда каждое использованиеsetState
обновитьcolor
, дочерние компоненты также будут обновлены, поэтому цвет соответствующей кнопки также изменится, и все выглядит идеально. Но как только между компонентами есть функция жизненного циклаShouldComponentUpdate
Так что все становится странным. мы знаемPureComponent
Суть в том, чтобы использоватьShouldComponentUpdate
Чтобы избежать ненужных обновлений, мы можем внести небольшую модификацию в предыдущий пример:
class Message extends React.PureComponent {
render() {
return (
<div>
{this.props.text} <Button>Delete</Button>
</div>
);
}
}
Вы обнаружите, что даже если выMessageList
изменилось вContext
Значение, не может привести к обновлению цвета кнопок узлов. Это потому чтоMessage
Компоненты наследуются отPureComponent
, без получения новогоprops
изменить илиstate
Функции жизненного цикла при измененииshouldComponentUpdate
то, что возвращаетсяfalse
,следовательноMessage
и его подкомпоненты не обновляются, что приводит кButton
Компоненты не обновляются до последнего цвета.
Если ваше значение контекста не изменяется, или используется только один раз, когда компонент инициализируется, то все проблемы не будут существовать. Но если вам нужно изменить контекст, как его использовать безопасно? Мишель ВестерстратHow to safely use React context
В этой статье представлено решение внедрения зависимостей (DI). Автор считает, что мы не должны прямоgetChildContext
Свойство состояния возвращается непосредственно в файле . Вместо этого conext следует использовать как внедрение зависимостей (DI).
class Theme {
constructor(color) {
this.color = color
this.subscriptions = []
}
setColor(color) {
this.color = color
this.subscriptions.forEach(f => f())
}
subscribe(f) {
this.subscriptions.push(f)
}
}
class Button extends React.Component {
static contextTypes = {
theme: PropTypes.Object
};
componentDidMount() {
this.context.theme.subscribe(() => this.forceUpdate());
}
render() {
return (
<button style={{background: this.context.theme.color}}>
{this.props.children}
</button>
);
}
}
class MessageList extends React.Component {
constructor(props){
super(props);
this.theme = new Theme("red");
}
static childContextTypes = {
theme: PropTypes.Object
};
getChildContext() {
return {
theme: this.theme
};
}
render() {
const children = this.props.messages.map((message) =>
<Message text={message.text} />
);
return (
<div>
<div>{children}</div>
<button onClick={this._changeColor}>Change Color</button>
</div>
);
}
_changeColor = () => {
const colors = ["red", "green", "blue"];
const index = (colors.indexOf(this.theme.color) + 1) % 3;
this.theme.setColor(colors[index]);
}
}
В приведенном выше примере мы создалиTheme
Класс, используемый для управления стилями, затемContext
БудуTheme
Экземпляр передается вButton
Получите экземпляр и подпишитесь на изменения стиля, вызовите, когда стиль изменитсяforceUpdate
Принудительное обновление позволяет обновить интерфейс. Конечно, вышеприведенный пример — это всего лишь прототип, и при его использовании необходимо учитывать другие аспекты, такие как необходимость отмены мониторинга при уничтожении компонента.
Оглядываясь назад на предыдущую версию Context, настроить ее довольно проблематично, особенно в соответствующих двух компонентах.childContextTypes
а такжеcontextTypes
Объявите тип свойства контекста. И на самом деле объявления этих двух типов не очень хорошо ограничивают.context
. В качестве примера предположим, что есть три компонента: Дедушка, Отец, Сын, и порядок рендеринга таков:
GrandFather -> Father -> Son
Затем предположим, что контекст, предоставляемый компонентом GrandFather, имеет типnumber
ключvalue
Значение 1, в то время как предоставленный Отец также имеет типnumber
ключvalue
Значение 2, которое получает объявление компонента Son, имеет типnumber
ключvalue
Контекст, который мы знаем для определенных компонентов Сынаthis.context.value
Значение равно 2, так как контекст должен брать ближайший родительский компонент, когда он встречает значение ключа с тем же именем.
Точно так же мы предполагаем, что контекст, обеспечиваемый произведением GrandFather, относится к типуstring
ключvalue
Значение "1", в то время как отец предоставлен типnumber
ключvalue
Значение 2, которое получает объявление компонента Son, имеет типstring
ключvalue
КОНТЕКСТ, то компонент СЫН примет значение КОНТЕКСТ дедушки? На самом деле его не будет, и значение, которое все равно берется, равно 2, но оно будет выводиться только в среде процесса разработки:
Invalid context
value
of typenumber
supplied toSon
, expectedstring
Таким образом, мы можем вывести статические свойстваchildContextTypes
а такжеcontextTypes
Он может оказывать лишь вспомогательную роль для развития и не может играть обязывающую роль в актуальном значении контекста, но и в этом случае мы должны повторять ручной труд и декларировать его снова и снова.childContextTypes
а такжеcontextTypes
Атрибуты.
New Context
Новый Context был выпущен в React 16.3. По сравнению с предыдущим способом согласования и объявления компонентов, Context в новой версии сильно отличается.Он использует декларативный метод записи и получает Context через свойства рендеринга, которые не будут затронуты. по жизненному циклу.shouldComponentUpdate
Влияние.上面的例子用新的Context改写为:
import React, {Component} from 'react';
const ThemeContext = React.createContext({ theme: 'red'});
class Button extends React.Component {
render(){
return(
<ThemeContext.Consumer>
{({color}) => {
return (
<button style={{background: color}}>
{this.props.children}
</button>
);
}}
</ThemeContext.Consumer>
);
}
}
class Message extends React.PureComponent {
render() {
return (
<div>
{this.props.text} <Button>Delete</Button>
</div>
);
}
}
class MessageList extends React.Component {
state = {
theme: { color: "red" }
};
render() {
return (
<ThemeContext.Provider value={this.state.theme}>
<div>
{this.props.messages.map((message) => <Message text={message.text}/>)}
<button onClick={this._changeColor}>Change Color</button>
</div>
</ThemeContext.Provider>
)
}
_changeColor = () => {
const colors = ["red", "green", "blue"];
const index = (colors.indexOf(this.state.theme.color) + 1) % 3;
this.setState({
theme: {
color: colors[index]
}
});
}
}
мы видим, что новый Context используетReact.createContext
способ создатьContext
экземпляр, а затем передатьProvider
способ предоставления значения контекста иConsumer
Значение контекста получается при взаимодействии с реквизитами рендеринга, даже если оно существует в промежуточном компоненте.shouldComponentUpdate
вернутьfalse
, это не приведет к невозможности обновления контекста и решит предыдущую проблему. мы видим это призваниеReact.createContext
СоздайтеContext
например, мы передаем значение по умолчаниюContext
значение, которое будет толькоConsumer
Не удалось найти соответствие в дереве компонентовProvider
используется, поэтому даже если вы дадитеProvider
изvalue
входящийundefined
стоимость,Consumer
Также не будет использоваться значение по умолчанию.
Новая версия Context API больше соответствует идее React, чем предыдущая версия Context API, и может решить проблемуcomponentShouldUpdate
поставленные проблемы. В то же время в ваш проект необходимо добавить специальные файлы для созданияContext
. В React v17 поддержка старого Context API может быть удалена, поэтому его все равно необходимо обновить как можно скорее. Так много было сказано в конце, но мы должны стараться избегать злоупотреблений Context в проекте, иначе зависимости между компонентами будут слишком сложными.