Управляемое чтение
Фронтенд разработка идет очень быстро, страницы и компоненты становятся все сложнее и сложнее, как лучше реализовать状态逻辑复用
Это всегда было важной частью приложения, которая напрямую связана с качеством приложения и простотой обслуживания.
В этой статье описываетсяReact
Используются три реализации状态逻辑复用
технологий и анализирует принципы их реализации, методы использования, практические приложения и способы их использования.
Эта статья немного длиннее, и ниже приведена карта ума этой статьи, вы можете прочитать ее с самого начала или выбрать интересующую вас часть для чтения:
Шаблоны дизайна Mixin
Mixin
(Смешивание) — это способ расширения функции сбора, которая по существу копирует свойства одного объекта в другой объект, но вы можете копировать任意多
объект任意个
метод к новому объекту, который继承
Не реализована. Его внешний вид в основном предназначен для решения проблемы кодового мультиплексирования.
Многие библиотеки с открытым исходным кодом предоставляютMixin
реализация, напримерUnderscore
из_.extend
метод,JQuery
изextend
метод.
использовать_.extend
Метод повторного использования кода:
var LogMixin = {
actionLog: function() {
console.log('action...');
},
requestLog: function() {
console.log('request...');
},
};
function User() { /*..*/ }
function Goods() { /*..*/ }
_.extend(User.prototype, LogMixin);
_.extend(Goods.prototype, LogMixin);
var user = new User();
var good = new Goods();
user.actionLog();
good.requestLog();
Мы можем попробовать вручную написать простойMixin
метод:
function setMixin(target, mixin) {
if (arguments[2]) {
for (var i = 2, len = arguments.length; i < len; i++) {
target.prototype[arguments[i]] = mixin.prototype[arguments[i]];
}
}
else {
for (var methodName in mixin.prototype) {
if (!Object.hasOwnProperty(target.prototype, methodName)) {
target.prototype[methodName] = mixin.prototype[methodName];
}
}
}
}
setMixin(User,LogMixin,'actionLog');
setMixin(Goods,LogMixin,'requestLog');
ты можешь использовать этоsetMixin
Метод расширяет любой метод любого объекта на целевой объект.
Применение миксинов в React
React
также обеспечиваетMixin
Реализация, если существует аналогичная функциональность, которая совершенно другой, мы можем ввести мультиплексирование кода, конечно, только используется только.createClass
создаватьReact
компонент можно использовать только потому, что вReact
компонентes6
Это устарело в письменной форме.
Например, в следующем примере многие компоненты или страницы должны записывать поведение пользователя, показатели производительности и т. д. Если ввести логику записи логов в каждый компонент, будет генерироваться много повторяющегося кода.Mixin
Мы можем решить эту проблему:
var LogMixin = {
log: function() {
console.log('log');
},
componentDidMount: function() {
console.log('in');
},
componentWillUnmount: function() {
console.log('out');
}
};
var User = React.createClass({
mixins: [LogMixin],
render: function() {
return (<div>...</div>)
}
});
var Goods = React.createClass({
mixins: [LogMixin],
render: function() {
return (<div>...</div>)
}
});
Вред Миксина
React
Официальная документация находится наMixins Considered Harmfulупоминается в статьеMixin
приносит вред:
-
Mixin
Могут быть взаимозависимыми и связанными, что не способствует сопровождению кода. - разные
Mixin
методы могут конфликтовать друг с другом -
Mixin
Очень часто компонент ощутим и даже нуждается в обработке, что лавинообразно увеличивает сложность кода.
React
Больше не рекомендуется использоватьMixin
решить проблему повторного использования кода, потому чтоMixin
вред больше, чем ценность, которую он производит, иReact
Вместо этого полностью рекомендуется использовать компоненты более высокого порядка. Кроме того, компоненты более высокого порядка также могут реализовывать больше других и более мощных функций.Прежде чем изучать компоненты более высокого порядка, давайте рассмотрим шаблон проектирования.
орнамент
декоратор (decorator
) может динамически добавлять обязанности к объектам во время выполнения программы без изменения самих объектов. Декораторы — это более легкий и гибкий подход, чем наследование.
Компоненты высшего порядка (HOC)
Компоненты более высокого порядка можно рассматривать какReact
Реализация шаблона декоратора, компонент высшего порядка — это функция, которая принимает компонент в качестве параметра и возвращает новый компонент.
Компоненты высшего порядка (
HOC
)ДаReact
Расширенные методы в , чтобы повторно использовать логику компонента. Но компоненты более высокого порядка сами по себе неReact API
.它只是一种模式,这种模式是由React
Его собственная композиционная природа неизбежно возникает.
function visible(WrappedComponent) {
return class extends Component {
render() {
const { visible, ...props } = this.props;
if (visible === false) return null;
return <WrappedComponent {...props} />;
}
}
}
Приведенный выше код представляет собойHOC
Простое приложение функции принимает компонент в качестве параметра и возвращает новый компонент, который может получитьvisible props
,согласно сvisible
Значение, определяющее, отображать ли видимый.
Далее мы подробно рассмотрим следующие аспекты:HOC
.
Как реализуется HOC
Риелтером
Функция возвращает компонент, который мы определяем сами, а затем вrender
возвращает компонент для переноса, чтобы мы могли проксировать все входящиеprops
, и решить, как визуализировать. Фактически, компонент более высокого порядка, сгенерированный таким образом, является родительским компонентом исходного компонента. Вышеприведенная функцияvisible
только одинHOC
Как реализован прокси свойства.
function proxyHOC(WrappedComponent) {
return class extends Component {
render() {
return <WrappedComponent {...this.props} />;
}
}
}
Улучшенные предметы по сравнению с нативными компонентами:
- Действует на все входящие
props
- Жизненный цикл активного компонента
- операционные компоненты
static
метод - Получать
refs
обратное наследование
Возвращает компонент, который наследует исходный компонент, вrender
вызвать исходный компонентrender
. Поскольку исходный компонент наследуется, к исходному компоненту можно получить доступ через этот生命周期、props、state、render
и т. д., он может манипулировать большим количеством свойств, чем прокси-сервер свойств.
function inheritHOC(WrappedComponent) {
return class extends WrappedComponent {
render() {
return super.render();
}
}
}
Усовершенствованные элементы по сравнению с родными компонентами:
- Действует на все входящие
props
- Жизненный цикл активного компонента
- операционные компоненты
static
метод - Получать
refs
- оперативный
state
- Может отображать угон
Что может ХОК?
Комбинированный рендеринг
Вы можете использовать любой другой компонент и исходный компонент для комбинированного рендеринга, чтобы добиться эффектов повторного использования стиля и макета.
Реализация атрибутивным агентом
function stylHOC(WrappedComponent) {
return class extends Component {
render() {
return (<div>
<div className="title">{this.props.title}</div>
<WrappedComponent {...this.props} />
</div>);
}
}
}
Реализовано через обратное наследование
function styleHOC(WrappedComponent) {
return class extends WrappedComponent {
render() {
return <div>
<div className="title">{this.props.title}</div>
{super.render()}
</div>
}
}
}
условный рендеринг
Определяет, визуализируется ли исходный компонент в соответствии с определенным свойством
Реализовано через прокси свойства
function visibleHOC(WrappedComponent) {
return class extends Component {
render() {
if (this.props.visible === false) return null;
return <WrappedComponent {...props} />;
}
}
}
Реализовано через обратное наследование
function visibleHOC(WrappedComponent) {
return class extends WrappedComponent {
render() {
if (this.props.visible === false) {
return null
} else {
return super.render()
}
}
}
}
Реквизит для действий
может использоваться для входящих компонентовprops
добавить, изменить, удалить илиprops
выполнять специальные операции.
Реализовано через прокси свойства
function proxyHOC(WrappedComponent) {
return class extends Component {
render() {
const newProps = {
...this.props,
user: 'ConardLi'
}
return <WrappedComponent {...newProps} />;
}
}
}
получить реф.
Исходная компонента может быть получена в компоненте более высокого порядкаref
,пройти черезref
Получите мощность компонента, например следующий код, когда инициализация программы завершена, вызывается метод журнала исходного компонента. (Я не умею пользоваться рефами, пожалуйста👇Refs & DOM)
Реализовано через прокси свойства
function refHOC(WrappedComponent) {
return class extends Component {
componentDidMount() {
this.wapperRef.log()
}
render() {
return <WrappedComponent {...this.props} ref={ref => { this.wapperRef = ref }} />;
}
}
}
Обратите внимание: при вызове компонента более высокого порядка вы не можете получить истинное значение исходного компонента.ref
, его необходимо передать вручную, см.передать рефери
государственное управление
Извлеките состояние исходного компонента вHOC
Управляемый в следующем коде, мы будемInput
изvalue
извлечь вHOC
Управляйте им в элементе управления, делая его контролируемым компонентом, не влияя на его использование.onChange
метод для выполнения некоторых других операций. На основе этого подхода мы можем реализовать простой双向绑定
, пожалуйста, посмотридвусторонняя привязка.
Реализовано через прокси свойства
function proxyHoc(WrappedComponent) {
return class extends Component {
constructor(props) {
super(props);
this.state = { value: '' };
}
onChange = (event) => {
const { onChange } = this.props;
this.setState({
value: event.target.value,
}, () => {
if(typeof onChange ==='function'){
onChange(event);
}
})
}
render() {
const newProps = {
value: this.state.value,
onChange: this.onChange,
}
return <WrappedComponent {...this.props} {...newProps} />;
}
}
}
class HOC extends Component {
render() {
return <input {...this.props}></input>
}
}
export default proxyHoc(HOC);
рабочее состояние
В приведенном выше примере состояние HOC используется для улучшения исходного компонента через прокси атрибута, но он не может напрямую управлять исходным компонентом.state
, а с помощью обратного наследования мы можем напрямую манипулироватьstate
. Однако не рекомендуется напрямую изменять или добавлять исходные компоненты.state
, потому что это может конфликтовать с операцией внутри компонента.
Реализовано через обратное наследование
function debugHOC(WrappedComponent) {
return class extends WrappedComponent {
render() {
console.log('props', this.props);
console.log('state', this.state);
return (
<div className="debuging">
{super.render()}
</div>
)
}
}
}
надHOC
существуетrender
генерал-лейтенантprops
а такжеstate
Распечатайте его, его можно использовать как этап отладки, конечно же вы можете написать в нем больше отладочного кода. Представьте, что вы просто добавляете компонент, который хотите отлаживать.@debug
Вы можете отлаживать компонент без написания большого количества избыточного кода каждый раз, когда отлаживаете его. (Если вы еще не знаете, как использовать HOC, пожалуйста 👇Как использовать ХОК)
рендеринг угона
Компоненты более высокого порядка могут выполнять множество операций в функции рендеринга для управления выводом рендеринга исходного компонента. Пока рендеринг исходного компонента изменяется, мы называем это своего рода渲染劫持
.
На самом деле вышеизложенноеКомбинированный рендеринга такжеусловный рендерингобе渲染劫持
Своего рода путем обратного наследования можно не только достичь двух вышеуказанных пунктов, но и напрямую增强
по оригинальным компонентамrender
генерируется функциейReact元素
.
Реализовано через обратное наследование
function hijackHOC(WrappedComponent) {
return class extends WrappedComponent {
render() {
const tree = super.render();
let newProps = {};
if (tree && tree.type === 'input') {
newProps = { value: '渲染被劫持了' };
}
const props = Object.assign({}, tree.props, newProps);
const newTree = React.cloneElement(tree, props, tree.props.children);
return newTree;
}
}
}
Обратите внимание на инструкции выше, которые я использовал增强
вместо更改
.render
Внутри функция фактически вызываетReact.creatElement
произведеноReact元素
:
getOwnPropertyDescriptors
функция для печати элементов конфигурации:
Можно обнаружить, что всеwritable
свойства настроены какfalse
, т. е. все свойства неизменны. (Если у вас есть какие-либо вопросы об этих элементах конфигурации, пожалуйста, 👇defineProperty)
не может быть изменен напрямую, мы можем использоватьcloneElement
метод улучшения нового компонента на основе исходного компонента:
React.cloneElement()
клонировать и вернуть новыйReact元素
,использоватьelement
в качестве отправной точки. Результирующий элемент будет иметь неглубокое слияние реквизитов исходного элемента с новыми реквизитами. Новые дети заменяют существующих детей. Ключ и ссылка исходного элемента будут сохранены.
React.cloneElement()
почти эквивалентно:
<element.type {...element.props} {...props}>{children}</element.type>
Как использовать ХОК
Приведенный выше пример кода посвящен тому, как объявитьHOC
,HOC
на самом деле является функцией, поэтому мы называем компонент, который хотим улучшить, параметромHOC
функция, расширенный компонент.
class myComponent extends Component {
render() {
return (<span>原组件</span>)
}
}
export default inheritHOC(myComponent);
compose
В практических приложениях компонент может использоваться несколькимиHOC
все улучшения, которые мы используем,HOC
Расширенные компоненты, одолжить один装饰模式
Это может быть легче понять с диаграммой, чтобы проиллюстрировать:
Допустим, теперь у нас естьlogger
,visible
,style
больше, чем одинHOC
, теперь для одновременного повышенияInput
Компоненты:
logger(visible(style(Input)))
Такой код очень сложно читать, мы можем вручную инкапсулировать простой инструмент композиции функций и переписать его следующим образом:
const compose = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args)));
compose(logger,visible,style)(Input);
compose
Функция возвращает функцию, которая объединяет все функции,compose(f, g, h)
а также(...args) => f(g(h(...args)))
это то же самое.
Многие сторонние библиотеки предоставляют аналогичныеcompose
функция, такая какlodash.flowRight
,Redux
который предоставилcombineReducers
функция и т. д.
Decorators
Мы также можем использоватьES7
предоставил намDecorators
Давайте сделаем наше письмо более элегантным:
@logger
@visible
@style
class Input extends Component {
// ...
}
Decorators
даES7
Предложение для , которое еще не стандартизировано, но в настоящее времяBabel
Транскодер уже поддерживается, нам нужно настроить его заранееbabel-plugin-transform-decorators-legacy
:
"plugins": ["transform-decorators-legacy"]
Также можно комбинировать вышеперечисленноеcompose
Использование функции:
const hoc = compose(logger, visible, style);
@hoc
class Input extends Component {
// ...
}
Практическое применение HOC
Вот некоторые из реальных тестов, которые у меня есть в производствеHOC
Из-за длины статьи код был сильно упрощен, если у вас есть какие-либо вопросы, пожалуйста, укажите в области комментариев:
управление журналом
На самом деле это одно из самых распространенных приложений, несколько компонентов имеют схожую логику, нам нужно повторно использовать повторяющуюся логику.
в официальной документацииCommentList
В примере также решается проблема повторного использования кода.Он написан очень подробно.Если интересно,можете 👇Используйте компоненты высшего порядка (HOC) для решения сквозных проблем.
Некоторым страницам необходимо записывать поведение пользователей, показатели производительности и т. д. Выполнение этих действий с помощью компонентов более высокого порядка может сэкономить много повторяющегося кода.
function logHoc(WrappedComponent) {
return class extends Component {
componentWillMount() {
this.start = Date.now();
}
componentDidMount() {
this.end = Date.now();
console.log(`${WrappedComponent.dispalyName} 渲染时间:${this.end - this.start} ms`);
console.log(`${user}进入${WrappedComponent.dispalyName}`);
}
componentWillUnmount() {
console.log(`${user}退出${WrappedComponent.dispalyName}`);
}
render() {
return <WrappedComponent {...this.props} />
}
}
}
в наличии, контроль доступа
function auth(WrappedComponent) {
return class extends Component {
render() {
const { visible, auth, display = null, ...props } = this.props;
if (visible === false || (auth && authList.indexOf(auth) === -1)) {
return display
}
return <WrappedComponent {...props} />;
}
}
}
authList
Это список всех разрешений, которые мы запрашиваем у бэкэнда при входе в программу.Когда разрешения, требуемые компонентом, отсутствуют в списке или установленыvisible
даfalse
, мы отображаем его как входящий стиль компонента, илиnull
. Мы можем применить любой компонент, который требует проверки разрешенияHOC
:
@auth
class Input extends Component { ... }
@auth
class Button extends Component { ... }
<Button auth="user/addUser">添加用户</Button>
<Input auth="user/search" visible={false} >添加用户</Input>
двусторонняя привязка
существуетvue
中,绑定一个变量后可实现双向数据绑定,即表单中的值改变后绑定的变量也会自动改变。 а такжеReact
В , такая обработка не выполняется, по умолчанию элементы формы非受控组件
. После привязки состояния к элементу формы его часто нужно прописывать вручнуюonChange
способ переписать его как受控组件
, эти повторяющиеся операции очень болезненны в случае очень большого количества элементов формы.
Мы можем реализовать простую двустороннюю привязку с помощью компонентов высокого порядка.Код немного длиннее, что можно понять в сочетании со следующей картой ума.
Сначала мы настраиваемForm
Компонент, который используется для переноса всех компонентов формы, которые необходимо обернуть, черезcontex
Предоставьте два свойства дочерним компонентам:
-
model
:токForm
Все данные, управляемые формойname
а такжеvalue
состав, например{name:'ConardLi',pwd:'123'}
.model
Он может быть импортирован извне, а также может управляться сам по себе. -
changeModel
:Изменятьmodel
один изname
ценность .
class Form extends Component {
static childContextTypes = {
model: PropTypes.object,
changeModel: PropTypes.func
}
constructor(props, context) {
super(props, context);
this.state = {
model: props.model || {}
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.model) {
this.setState({
model: nextProps.model
})
}
}
changeModel = (name, value) => {
this.setState({
model: { ...this.state.model, [name]: value }
})
}
getChildContext() {
return {
changeModel: this.changeModel,
model: this.props.model || this.state.model
};
}
onSubmit = () => {
console.log(this.state.model);
}
render() {
return <div>
{this.props.children}
<button onClick={this.onSubmit}>提交</button>
</div>
}
}
Следующие определения для двусторонней привязкиHOC
, который проксирует формуonChange
свойства иvalue
Атрибуты:
- происходить
onChange
Вызов верхнего уровня при возникновении событияForm
изchangeModel
способ изменитьcontext
серединаmodel
. - при рендеринге
value
Изменено сcontext
значение взято из .
function proxyHoc(WrappedComponent) {
return class extends Component {
static contextTypes = {
model: PropTypes.object,
changeModel: PropTypes.func
}
onChange = (event) => {
const { changeModel } = this.context;
const { onChange } = this.props;
const { v_model } = this.props;
changeModel(v_model, event.target.value);
if(typeof onChange === 'function'){onChange(event);}
}
render() {
const { model } = this.context;
const { v_model } = this.props;
return <WrappedComponent
{...this.props}
value={model[v_model]}
onChange={this.onChange}
/>;
}
}
}
@proxyHoc
class Input extends Component {
render() {
return <input {...this.props}></input>
}
}
Приведенный выше код является лишь краткой частью, за исключениемinput
, мы также можемHOC
используется дляselect
и другие компоненты формы, вы даже можете добавить вышеуказанныеHOC
совместимый сspan、table
Ожидание компонента дисплея, это может значительно упростить код, и давайте сэкономим много государственных управлений, используйте следующее:
export default class extends Component {
render() {
return (
<Form >
<Input v_model="name"></Input>
<Input v_model="pwd"></Input>
</Form>
)
}
}
проверка формы
Основываясь на приведенном выше примере с двусторонней привязкой, давайте возьмем еще один валидатор формы. Валидатор формы может содержать функции проверки и подсказки. При сбое проверки отображается сообщение об ошибке:
function validateHoc(WrappedComponent) {
return class extends Component {
constructor(props) {
super(props);
this.state = { error: '' }
}
onChange = (event) => {
const { validator } = this.props;
if (validator && typeof validator.func === 'function') {
if (!validator.func(event.target.value)) {
this.setState({ error: validator.msg })
} else {
this.setState({ error: '' })
}
}
}
render() {
return <div>
<WrappedComponent onChange={this.onChange} {...this.props} />
<div>{this.state.error || ''}</div>
</div>
}
}
}
const validatorName = {
func: (val) => val && !isNaN(val),
msg: '请输入数字'
}
const validatorPwd = {
func: (val) => val && val.length > 6,
msg: '密码必须大于6位'
}
<HOCInput validator={validatorName} v_model="name"></HOCInput>
<HOCInput validator={validatorPwd} v_model="pwd"></HOCInput>
Конечно, вы также можетеForm
При отправке оценивается, прошли ли все валидаторы, также валидаторы могут быть установлены в виде массива и т. д. Из-за длины статьи код сильно упростился, и заинтересованные студенты могут реализовать его самостоятельно.
Редукс подключить
в редуксеconnect
, на самом делеHOC
, ниже приведена упрощенная версияconnect
выполнить:
export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {
class Connect extends Component {
static contextTypes = {
store: PropTypes.object
}
constructor () {
super()
this.state = {
allProps: {}
}
}
componentWillMount () {
const { store } = this.context
this._updateProps()
store.subscribe(() => this._updateProps())
}
_updateProps () {
const { store } = this.context
let stateProps = mapStateToProps ? mapStateToProps(store.getState(), this.props): {}
let dispatchProps = mapDispatchToProps? mapDispatchToProps(store.dispatch, this.props) : {}
this.setState({
allProps: {
...stateProps,
...dispatchProps,
...this.props
}
})
}
render () {
return <WrappedComponent {...this.state.allProps} />
}
}
return Connect
}
Код очень понятный,connect
Функция на самом деле делает одну вещь,mapStateToProps
а такжеmapDispatchToProps
Они деконструируются и передаются в исходный компонент, чтобы мы могли использовать их непосредственно в исходном компоненте.props
Получатьstate
так же какdispatch
функция.
Меры предосторожности при использовании HOC
Предостережение — копирование статического свойства
когда мы применяемHOC
При улучшении другого компонента фактически используемый компонент больше не является исходным компонентом, поэтому мы не можем получить какие-либо статические свойства исходного компонента, мы можемHOC
Скопируйте их вручную в конце:
function proxyHOC(WrappedComponent) {
class HOCComponent extends Component {
render() {
return <WrappedComponent {...this.props} />;
}
}
HOCComponent.staticMethod = WrappedComponent.staticMethod;
// ...
return HOCComponent;
}
Если оригинальный компонент имеет много статических свойств, этот процесс очень болезненлен, но вы должны понимать, что необходимо повысить статические свойства всех компонентов, заключается в том, что мы можем использоватьhoist-non-react-statics
чтобы помочь нам решить эту проблему, он может автоматически копировать все не-React
Статический метод используется следующим образом:
import hoistNonReactStatic from 'hoist-non-react-statics';
function proxyHOC(WrappedComponent) {
class HOCComponent extends Component {
render() {
return <WrappedComponent {...this.props} />;
}
}
hoistNonReactStatic(HOCComponent,WrappedComponent);
return HOCComponent;
}
Предостережение: проходные рефери
После использования компонентов более высокого порядка полученныйref
На самом деле это самый внешний компонент контейнера, а не исходный компонент, но во многих случаях нам нужно использовать исходный компонент.ref
.
Компоненты более высокого порядка не похожи на Passthroughprops
что будетrefs
Прозрачная передача, мы можем использовать функцию обратного вызова для завершенияref
Доставка:
function hoc(WrappedComponent) {
return class extends Component {
getWrappedRef = () => this.wrappedRef;
render() {
return <WrappedComponent ref={ref => { this.wrappedRef = ref }} {...this.props} />;
}
}
}
@hoc
class Input extends Component {
render() { return <input></input> }
}
class App extends Component {
render() {
return (
<Input ref={ref => { this.inpitRef = ref.getWrappedRef() }} ></Input>
);
}
}
React 16.3
версия обеспечиваетforwardRef API
чтобы помочь намrefs
проход, так что мы попадаем на компонент более высокого порядкаref
это оригинальный компонентref
, без необходимости передавать его вручную, если вашReact
версия больше, чем16.3
, вы можете использовать следующие методы:
function hoc(WrappedComponent) {
class HOC extends Component {
render() {
const { forwardedRef, ...props } = this.props;
return <WrappedComponent ref={forwardedRef} {...props} />;
}
}
return React.forwardRef((props, ref) => {
return <HOC forwardedRef={ref} {...props} />;
});
}
Предостережение — не создавайте компоненты более высокого порядка внутри метода рендеринга.
React
Diff
Принципы алгоритма таковы:
- Используйте идентификатор компонента, чтобы определить, следует ли удалить или обновить компонент.
- Если идентификатор компонента совпадает с предыдущим рендерингом, рекурсивно обновите дочерние компоненты.
- Если идентификатор отличается, размонтируйте компонент и перемонтируйте новый компонент.
Каждый раз, когда вызывается компонент более высокого порядка, создается совершенно новый компонент, и уникальный идентификатор компонента также будет меняться.render
Метод вызывает компонент более высокого порядка, что вызывает размонтирование и повторное монтирование компонента каждый раз.
Соглашение - не изменять исходный компонент
Официальная документация объясняет компоненты более высокого порядка:
Компонент более высокого порядка — это чистая функция без побочных эффектов.
Давайте снова посмотрим на определение чистой функции:
Если функция вызывается с одними и теми же параметрами, она всегда будет возвращать один и тот же результат. Он не зависит ни от каких изменений состояния или данных вне функции во время выполнения программы и должен зависеть только от ее входных параметров. Функция не производит каких-либо наблюдаемых побочных эффектов, таких как сетевые запросы, устройства ввода и вывода или мутации данных.
Если мы изменим исходный компонент в компоненте более высокого порядка, например, в следующем коде:
InputComponent.prototype.componentWillReceiveProps = function(nextProps) { ... }
Это нарушает наше соглашение о компонентах более высокого порядка, а также меняет первоначальное намерение использовать компоненты более высокого порядка: мы используем компоненты более высокого порядка для增强
вместо改变
оригинальные компоненты.
Конвенция - прозрачно передавать нерелевантный реквизит
Компоненты с высоким порядком, мы можем все агентыprops
, но часто конкретноHOC
использовать только один или несколько из нихprops
. Нам нужно поставить другие неактуальныеprops
Передайте его исходному компоненту, как в следующем коде:
function visible(WrappedComponent) {
return class extends Component {
render() {
const { visible, ...props } = this.props;
if (visible === false) return null;
return <WrappedComponent {...props} />;
}
}
}
мы используем толькоvisible
свойства для управления отображением компонента можно скрыть, поставить другиеprops
Передайте это.
соглашение-displayName
В использованииReact Developer Tools
При отладке, если мы используемHOC
, интерфейс отладки может стать очень трудным для чтения, как в следующем коде:
@visible
class Show extends Component {
render() {
return <h1>我是一个标签</h1>
}
}
@visible
class Title extends Component {
render() {
return <h1>我是一个标题</h1>
}
}
Для облегчения отладки мы можем вручнуюHOC
указатьdisplayName
, Официальная рекомендацияHOCName(WrappedComponentName)
:
static displayName = `Visible(${WrappedComponent.displayName})`
Это соглашение помогает обеспечить максимальную гибкость и возможность повторного использования компонентов более высокого порядка.
Мотивация использования HOC
Пересмотрите вышеупомянутоеMixin
Риски, связанные с:
-
Mixin
Могут быть взаимозависимыми и связанными, что не способствует сопровождению кода. - разные
Mixin
методы могут конфликтовать друг с другом -
Mixin
Очень часто компонент ощутим и даже нуждается в обработке, что лавинообразно увеличивает сложность кода.
а такжеHOC
Появление может решить эти проблемы:
- Компонент более высокого порядка — это чистая функция без побочных эффектов, и каждый компонент более высокого порядка не будет зависеть и связываться друг с другом.
- Компоненты более высокого порядка также могут вызывать конфликты, но мы можем избежать такого поведения, придерживаясь соглашений.
- Компонентам более высокого порядка все равно, как и почему используются данные, а обернутому компоненту все равно, откуда берутся данные. Добавление компонентов более высокого порядка не увеличит нагрузку на исходные компоненты.
Недостатки HOC
-
HOC
Его необходимо обернуть или вложить в исходный компонент, если он используется в больших количествах.HOC
, создаст большое количество вложений, что очень усложнит отладку. -
HOC
может быть угнанprops
, также может вызвать конфликты, если соглашение не соблюдается.
Hooks
Hooks
даReact v16.7.0-alpha
Добавлены новые функции в . это позволяет вамclass
использовать кромеstate
и другиеReact
характеристика.
использоватьHooks
, вы можете включитьstate
Логика абстрагируется от компонента, что упрощает ее тестирование. в то же время,Hooks
Может помочь вам повторно использовать эту логику, не переписывая структуру компонента. Таким образом, его также можно использовать в качестве реализации状态逻辑复用
строить планы.
Прочтите главы нижеМотивация использования хуковВы можете обнаружить, что он может решить обаMixin
а такжеHOC
вызванные проблемы.
Официальные крючки
State Hook
Мы хотим использоватьclass
Реализация компонентов计数器
функцию, мы могли бы написать:
export default class Count extends Component {
constructor(props) {
super(props);
this.state = { count: 0 }
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => { this.setState({ count: this.state.count + 1 }) }}>
Click me
</button>
</div>
)
}
}
пройти черезuseState
, мы также можем использовать функциональные компоненты для достижения этой функции:
export default function HookTest() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => { setCount(count + 1); setNumber(number + 1); }}>
Click me
</button>
</div>
);
}
useState
это хук, который может добавить некоторое состояние к функциональному компоненту и предоставить функции для изменения этих состояний, и он получает параметр, который является значением состояния по умолчанию.
Effect Hook
Эффект-хук позволяет выполнять некоторые операции с побочными эффектами в функциональных компонентах.
параметр
useEffect
Метод принимает два параметра:
- 1. Функция обратного вызова: один раз в первом компоненте
render
Каждый раз после негоupdate
после бега,React
гарантировано вDOM
Обратный вызов не будет выполняться, пока обновление не будет завершено. - 2. Зависимость от состояния (массив): если настроена зависимость от состояния, функция обратного вызова будет вызываться только при обнаружении настроенного изменения состояния.
useEffect(() => {
// 只要组件render后就会执行
});
useEffect(() => {
// 只有count改变时才会执行
},[count]);
возвращаемое значение обратного вызова
useEffect
Первый параметр может возвращать функцию, когда страница отображает результат следующего обновления, выполняется в следующий раз.useEffect
Раньше эта функция называлась. Эта функция часто используется дляuseEffect
помыть.
export default function HookTest() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('执行...', count);
return () => {
console.log('清理...', count);
}
}, [count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => { setCount(count + 1); setNumber(number + 1); }}>
Click me
</button>
</div>
);
}
Выполните приведенный выше код и нажмите кнопку несколько раз, вы получите следующий результат:
Обратите внимание, что если вы добавите рендеринг в браузере, результат должен выглядеть так:
页面渲染...1
执行... 1
页面渲染...2
清理... 1
执行... 2
页面渲染...3
清理... 2
执行... 3
页面渲染...4
清理... 3
执行... 4
Так почему же после рендеринга браузера метод очистки все еще может найти последнийstate
Шерстяная ткань? Причина проста, мыuseEffect
То, что возвращается, — это функция, которая формирует замыкание, гарантирующее, что переменные, хранящиеся в функции, которую мы выполнили в прошлый раз, не будут уничтожены и загрязнены.
Вы можете попробовать следующий код, возможно, лучше понять
var flag = 1;
var clean;
function effect(flag) {
return function () {
console.log(flag);
}
}
clean = effect(flag);
flag = 2;
clean();
clean = effect(flag);
flag = 3;
clean();
clean = effect(flag);
// 执行结果
effect... 1
clean... 1
effect... 2
clean... 2
effect... 3
фиктивный компонентDidMount
componentDidMount
ЭквивалентноuseEffect
Обратный вызов выполняется только один раз после завершения инициализации страницы, когдаuseEffect
Этот эффект может быть достигнут, когда второй параметр включен в пустой массив.
function useDidMount(callback) {
useEffect(callback, []);
}
Официальный не рекомендует этот способ написания, потому что это может привести к некоторым ошибкам.
фиктивный компонентWillUnmount
function useUnMount(callback) {
useEffect(() => callback, []);
}
В отличие от componentDidMount или componentDidUpdate, эффекты, используемые в useEffect, не блокируют отображение страницы браузером. Это сделает ваше приложение более плавным.
ref Hook
использоватьuseRef Hook
, вы можете легко получитьdom
изref
.
export default function Input() {
const inputEl = useRef(null);
const onButtonClick = () => {
inputEl.current.focus();
};
return (
<div>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</div>
);
}
УведомлениеuseRef()
можно использовать не только для полученияref
использовать, использоватьuseRef
произведеноref
изcurrent
Свойства изменяемы, что означает, что вы можете использовать их для хранения произвольного значения.
фиктивный компонентDidUpdate
componentDidUpdate
эквивалентно удалению первого вызоваuseEffect
, мы можем использоватьuseRef
Создайте флаг, чтобы записать, является ли это первым выполнением:
function useDidUpdate(callback, prop) {
const init = useRef(true);
useEffect(() => {
if (init.current) {
init.current = false;
} else {
return callback();
}
}, prop);
}
Меры предосторожности при использовании хуков
Сфера использования
- только в
React
Функциональный компонент или пользовательскийHook
используется вHook
.
Hook
предлагается в основном для решенияclass
Ряд проблем с компонентами, поэтому мы можемclass
Компоненты используют его.
декларативные ограничения
- Не вызывайте хуки в циклах, условных выражениях или вложенных функциях.
Hook
Реализуется через массив, каждый разuseState
изменит индекс,React
Порядок вызова необходимо использовать для корректного обновления соответствующего состояния, еслиuseState
Заключенный в циклы или условные операторы, он может вызвать путаницу в вызывающей последовательности, что приведет к непредвиденным ошибкам.
мы можем установитьeslint
Плагины, которые помогут нам избежать этих проблем.
// 安装
npm install eslint-plugin-react-hooks --save-dev
// 配置
{
"plugins": [
// ...
"react-hooks"
],
"rules": {
// ...
"react-hooks/rules-of-hooks": "error"
}
}
Пользовательский крючок
как описано вышеHOC
а такжеmixin
Точно так же мы можем настроитьHook
Извлеките аналогичную логику состояния в компонентах.
настроитьHook
Очень просто, нам нужно только определить функцию и поставить соответствующее требуемое состояние иeffect
упакованы, и в то же время,Hook
Они также могут ссылаться друг на друга. использоватьuse
имя нестандартное в началеHook
, что делает его удобнымeslint
проверка.
Ниже мы рассмотрим несколько конкретныхHook
Упаковка:
управление журналом
Мы можем использовать жизненный цикл, инкапсулированный вышеHook
.
const useLogger = (componentName, ...params) => {
useDidMount(() => {
console.log(`${componentName}初始化`, ...params);
});
useUnMount(() => {
console.log(`${componentName}卸载`, ...params);
})
useDidUpdate(() => {
console.log(`${componentName}更新`, ...params);
});
};
function Page1(props){
useLogger('Page1',props);
return (<div>...</div>)
}
Изменить название
Изменить страницы в соответствии с разными именами страницtitle
:
function useTitle(title) {
useEffect(
() => {
document.title = title;
return () => (document.title = "主页");
},
[title]
);
}
function Page1(props){
useTitle('Page1');
return (<div>...</div>)
}
двусторонняя привязка
мы сформируемonChange
Логика извлекается и упаковывается вHook
, чтобы можно было повторно использовать все компоненты формы, для которых требуется двусторонняя привязка:
function useBind(init) {
let [value, setValue] = useState(init);
let onChange = useCallback(function(event) {
setValue(event.currentTarget.value);
}, []);
return {
value,
onChange
};
}
function Page1(props){
let value = useBind('');
return <input {...value} />;
}
Конечно вы можетеHOC
таким образом, комбинируйтеcontext
а такжеform
Чтобы инкапсулировать более общую двустороннюю привязку, вы можете реализовать ее вручную, если вам это интересно.
Мотивация использования хуков
Уменьшите риск повторного использования логики состояния
Hook
а такжеMixin
Есть определенные сходства в использовании, ноMixin
Введенные логика и состояние могут перекрывать друг друга, и несколькоHook
Они не влияют друг на друга, что избавляет нас от необходимости сосредотачиваться на предотвращении конфликтов, избегающих логического повторного использования.
использовать без соблюденияHOC
Также могут быть некоторые конфликты, такие какprops
Переопределить и т. д., используйтеHook
Этих проблем можно избежать.
Избегайте гнездования в аду
интенсивное использованиеHOC
В случае очень глубокой вложенности кода используйтеHook
, мы можем добиться повторного использования логики плоского состояния и избежать большого количества вложенных компонентов.
Сделайте компоненты более понятными
В использованииclass
Когда компоненты строят нашу программу, каждый из них имеет свое собственное состояние.Сложность бизнес-логики делает эти компоненты все больше и больше, и в каждом жизненном цикле будет вызываться все больше и больше логики, что усложняет ее обслуживание. использоватьHook
Это позволяет сделать большее ограничение общедоступной логики, разделить компонент на меньшую функцию, а не навязывать метод жизненного цикла для сегментации.
Используйте функции вместо классов
Вместо функции напишитеclass
Возможно, вам потребуется освоить больше знаний, и тем больше моментов, на которые вам нужно обратить внимание, таких какthis
Указывать, связывать события и т. д. Кроме того, компьютер понимает функцию лучше, чемclass
Быстрее.Hooks
так что вы можетеclasses
использовать большеReact
Новые особенности.
рациональный выбор
Фактически,Hook
существуетreact 16.8.0
только что официально выпущенHook
Стабильная версия не использовалась автором в производственной среде, в настоящее время автор больше всего использует ее в производственной среде.HOC
.
React
Чиновники неclasses
отReact
предназначен для удаления вclass
компоненты иHook
Он может существовать одновременно, и чиновник также рекомендует избегать любого «масштабного рефакторинга», ведь это очень новая версия, если она вам нравится, вы можете использовать ее в новом некритическом коде.Hook
.
резюме
mixin
был заброшен,HOC
В расцвете сил,Hook
Front-end круг именно такой, и скорость технической итерации очень высока, но когда мы изучаем эти знания, мы должны понимать, зачем нам нужно учиться, полезно ли учиться, и стоит ли нам это использовать или нет. Не забывайте о первоначальном намерении, Фанг Де всегда.
Если в тексте есть ошибки, исправьте их в комментариях, спасибо за прочтение.
Рекомендуемое чтение
Рекомендуемое внимание
Если вы хотите прочитать больше качественных статей или вам нужен исходный файл ментальной карты в статье, вы можете подписаться на меня.блог на гитхабе, добро пожаловать звезда✨.
Рекомендую обратить внимание на мой публичный аккаунт WeChat [code secret garden], будем общаться и расти вместе.
Подписавшись на официальную учетную запись, ответьте на [Добавить группу], чтобы включить вас в высококачественную группу внешнего интерфейса.