Введение
React
Компоненты высшего порядка (HOC
), для многихreact
Это не чуждо разработчикам, оно гибкое в использованииreact
Метод компонентов, компонент высшего порядка сам по себе не является компонентом, это функция компонента, параметр которого является компонентом, и возвращаемое значение также является компонентом. Действия высшего порядка используются дляУсильте компоненты, повторно используйте логику, улучшите производительность рендеринга и т. д.эффект. Разобраться в высокоуровневых компонентах не сложно.На самом деле это довольно просто после прикосновения.Далее буду следить,Понимание компонентов высшего порядка?,Как использовать компоненты высшего порядка? Сценарии применения,Расширенная практика компонентов (уровень исходного кода)Для прорыва давайте подробно рассмотрим компоненты высокого уровня. Эта статья относительно длинная, рекомендуется собрать и посмотреть
Начнем сегодняшнюю дискуссию с вопроса:
- 1 Что такое компонент более высокого порядка и какую проблему он решает?
- 2 Существует несколько видов компонентов высшего порядка, каковы их преимущества и недостатки?
- 3 Как написать отличный компонент высокого порядка?
- 4
hoc
Как работать со статическими свойствами на разных уровняхref
И другие вопросы? - 5 Как управлять компонентами высококачественного управления и изолирующим рендерингами?
- 6 Как компонент более высокого порядка контролирует состояние исходного компонента?
- ...
Компоненты высшего порядка (HOC) — это продвинутая техника в React для повторного использования логики компонентов. HOC сам по себе не является частью React API, это шаблон проектирования, основанный на композиционной природе React.
2. Всесторонний взгляд на высокоуровневые компоненты
1 Несколько способов упаковки компонентов арматуры
① смешанный режим
Прототип
старая версияreact-mixins
существуетreact
Изначально предусмотрен комбинированный подход. пройти черезReact.createClass
,Присоединяйсяmixins
свойства, конкретное использование иvue
серединаmixins
сходство. Конкретная реализация заключается в следующем.
const customMixin = {
componentDidMount(){
console.log( '------componentDidMount------' )
},
say(){
console.log(this.state.name)
}
}
const APP = React.createClass({
mixins: [ customMixin ],
getInitialState(){
return {
name:'alien'
}
},
render(){
const { name } = this.state
return <div> hello ,world , my name is { name } </div>
}
})
этоmixins
может существовать толькоcreateClass
в, позжеReact.createClass
вместе сmixins
Этот режим устарел.mixins
будет иметь некоторые негативные последствия.
- 1 Mixin вводит неявные зависимости.
- 2. Могут возникнуть проблемы с последовательностью или даже с охватом конфликтов кода между разными миксинами.
- 3 Код Mixin может привести к снежному кому сложности
Производный способ
createClass
устаревшее, не значитmixin
выход из режимаreact
Этап в компоненте с отслеживанием состоянияclass
, мы можем пройтиНаследование цепочки прототиповреализоватьmixins
.
const customMixin = { /* 自定义 mixins */
componentDidMount(){
console.log( '------componentDidMount------' )
},
say(){
console.log(this.state.name)
}
}
function componentClassMixins(Component,mixin){ /* 继承 */
for(let key in mixin){
Component.prototype[key] = mixin[key]
}
}
class Index extends React.Component{
constructor(){
super()
this.state={ name:'alien' }
}
render(){
return <div> hello,world
<button onClick={ this.say.bind(this) } > to say </button>
</div>
}
}
componentClassMixins(Index,customMixin)
②расширяет режим наследования
Прототип
существуетclass
После того, как компоненты станут популярными, мы сможем дополнительно усилить наши компоненты посредством наследования. Преимущество этого режима в том, что вы можете инкапсулировать основные функциональные компоненты, а затем перейти кextends
Наши основные компоненты, усиливайте компоненты по мере необходимости, но стоит отметить, что вы должны достаточно овладеть базовыми компонентами, иначе это вызовет ряд непредвиденных ситуаций.
class Base extends React.Component{
constructor(){
super()
this.state={
name:'alien'
}
}
say(){
console.log('base components')
}
render(){
return <div> hello,world <button onClick={ this.say.bind(this) } >点击</button> </div>
}
}
class Index extends Base{
componentDidMount(){
console.log( this.state.name )
}
say(){ /* 会覆盖基类中的 say */
console.log('extends components')
}
}
export default Index
Режим ③hoc
Прототип
HOC
Это основное содержание этой главы, конкретное использование, мы будем говорить об этом медленно, давайте сначала попробуем простойHOC
.
function HOC(Component) {
return class wrapComponent extends React.Component{
constructor(){
super()
this.state={
name:'alien'
}
}
render=()=><Component { ...this.props } { ...this.state } />
}
}
@HOC
class Index extends React.Component{
say(){
const { name } = this.props
console.log(name)
}
render(){
return <div> hello,world <button onClick={ this.say.bind(this) } >点击</button> </div>
}
}
④Режим пользовательских хуков
Прототип
hooks
Рождение большой части причины заключается в том, чтобы решитьНет компонента без гражданстваstate
а такжеЛогику сложно использовать повторновопрос.hooks
Вы можете инкапсулировать часть логики и использовать ее «из коробки», я не буду говорить об этом здесь.react-hooks
Статья о принципах, полнаяreact-hooks
Трилогия. Заинтересованные студенты могут прочитать две другие статьи автора, которые подробно знакомятreact-hooks
Принципы и схемы повторного использования логики кода.
Портал:
Играйте с реактивными хуками, пользовательскими шаблонами дизайна хуков и их реальной борьбой
Как используются реактивные хуки?
2 Первоначальный замысел компонентов высокого порядка
компонентыprop
визуализировать какUI
, И компонент высокого уровня должен преобразовать компонент в другой компонент.На что мы должны обратить больше внимания, так это на то, что упакованный компонент получил эти улучшения, сколько логики сохранено или устранены дефекты исходного компонента.Это компонент высокого уровня Значение компонентов заказа. Давайте сначала подумаем, какую проблему решают компоненты высокого порядка 🤔🤔🤔?
① Логика мультиплексирования: Компоненты более высокого порядка больше похожи на механическую обработку.react
Фабрика компонентов, серийная обработка оригинальных компонентовобработка,Упаковкаиметь дело с. Мы можем настроить эксклюзив в соответствии с потребностями бизнесаHOC
, который может решить логику мультиплексирования.
② Укрепить реквизит:ЭтоHOC
Одно из наиболее распространенных применений — компоненты, возвращаемые компонентами более высокого порядка, могут перехватывать компоненты, переданные с предыдущего уровня.props
, затем вмешайте новыйprops
, чтобы улучшить функциональность компонента. шедеврreact-router
серединаwithRouter
.
③ Включение компонентов:HOC
имеет уникальную особенность, которую можно придатьHOC
Упакованные бизнес-компоненты, предоставляющие некоторые расширенные функции, такие какдополнительный жизненный цикл, дополнительное событие, но этоHOC
, которые могут нуждаться в тесной интеграции с бизнес-компонентами. Типичный случайreact-keepalive-router
серединаkeepaliveLifeCycle
черезHOC
образом, добавляя дополнительный жизненный цикл бизнес-компоненту.
④ Контроль рендеринга: угнать рендеринг даhoc
особенность вwrapComponent
При упаковке компонентов можно выполнять операции над исходными компонентами.条件渲染
,节流渲染
,懒加载
и другие функции, о которых будет подробно рассказано позже, типичные представители выполняютreact-redux
серединаconnect
а такжеdva
серединаdynamic
Компоненты загружаются лениво.
Я сосредоточусь на первоначальном замысле компонентов более высокого порядка и подробно расскажу об их принципах и использовании. Следите за моими идеями, давайте сначала взглянем на высокоуровневые компоненты.Как это используется в наших бизнес-компонентах.
3 Компоненты более высокого порядка используют и записывают структуры
HOC
Руководство по использованию очень простое, просто оберните наш компонент.
Использование: шаблон декоратора и шаблон оболочки функции
дляclass
Объявив компоненты с состоянием, мы можем использовать шаблон декоратора для обертывания компонентов класса:
@withStyles(styles)
@withRouter
@keepaliveLifeCycle
class Index extends React.Componen{
/* ... */
}
Мы должны обратить внимание на порядок упаковки, чем ближеIndex
Компонент — внутренний слойHOC
, вдали от компонентаIndex
также ближе.
Для компонентов без состояния (объявлений функций) мы можем написать:
function Index(){
/* .... */
}
export default withStyles(styles)(withRouter( keepaliveLifeCycle(Index) ))
Модель: вложенный HOC
Для тех, кому не нужно передавать параметрыHOC
, мы пишем модель, нам нужно только вложить один уровень, напримерwithRouter
,
function withRouter(){
return class wrapComponent extends React.Component{
/* 编写逻辑 */
}
}
для параметров, которые требуютHOC
, нам нужен слой прокси, как показано ниже:
function connect (mapStateToProps){
/* 接受第一个参数 */
return function connectAdvance(wrapCompoent){
/* 接受组件 */
return class WrapComponent extends React.Component{ }
}
}
Мы видим дваhoc
Модель очень проста.Для прокси-функции может быть один слой, может быть много слоев, но не бойтесь, сколько бы слоев по сути не было одинаковым, нам нужно только отслаивать слой за слоем, проанализировать структуру, всюhoc
Структура и контекст хорошо видны. Пройти черезhoc
Тоже легко.
4 Два разных компонента более высокого порядка
Существует два способа часто используемых компонентов высшего порядка.Прокси-сервер впереда такжеОбратное наследование компонентов, есть некоторые общие черты и различия между ними. Далее будет подробно представлена разница между ними, а конкретная реализация будет подробно представлена в третьей части.
Переадресация прокси-атрибута
Так называемый прямой прокси-атрибут предназначен для обертывания слоя прокси-компонентов компонентами.На прокси-компонентах мы можем выполнять некоторые прокси-операции над исходными компонентами. существуетfiber tree
вверх, первыйmounted
Прокси-компонент, а затем наш бизнес-компонент. Мы можем понять это как отношение родительского компонента к дочернему, и родительский компонент выполняет ряд операций по усилению над дочерним компонентом.
function HOC(WrapComponent){
return class Advance extends React.Component{
state={
name:'alien'
}
render(){
return <WrapComponent { ...this.props } { ...this.state } />
}
}
}
преимущество
- ① Обычный агент атрибутов может иметь низкую связанность и нулевую связь с бизнес-компонентами.
条件渲染
а такжеprops属性增强
, отвечающий только за управление рендерингом дочерних компонентов и передачу дополнительныхprops
Это нормально, так что вам не нужно знать, что делает бизнес-компонент. Таким образом, прямой прокси-атрибут больше подходит для некоторых проектов с открытым исходным кодом.hoc
, в настоящее время с открытым исходным кодомHOC
Это в основном достигается с помощью этого режима. - ② То же самое относится к
class
объявление компонентов иfunction
Заявленные компоненты. - ③ Рендеринг бизнес-компонентов может быть полностью изолирован, по сравнению с обратным наследованием используется режим атрибутивного прокси. Вы можете полностью контролировать визуализацию бизнес-компонента или нет, чего можно избежать
反向继承
Принесите некоторые побочные эффекты, такие как выполнение жизненного цикла. - ④ Может быть вложенным, множественным
hoc
Может быть вложенным и, как правило, не ограничивает упаковкуHOC
порядок старшинства.
недостаток
-
① Как правило, статус бизнес-компонентов нельзя получить напрямую, если вы хотите его получить, вам необходимо
ref
Получите экземпляр компонента. -
② Статические свойства нельзя наследовать напрямую. Если вам нужно наследовать, вам нужно обработать это вручную или ввести стороннюю библиотеку.
пример:
class Index extends React.Component{
render(){
return <div> hello,world </div>
}
}
Index.say = function(){
console.log('my name is alien')
}
function HOC(Component) {
return class wrapComponent extends React.Component{
render(){
return <Component { ...this.props } { ...this.state } />
}
}
}
const newIndex = HOC(Index)
console.log(newIndex.say)
распечатать результат
обратное наследование
Между обратным наследованием и прокси свойств есть определенная разница: упакованный компонент наследует сам бизнес-компонент, поэтому нам не нужно создавать экземпляр нашего бизнес-компонента. Текущий компонент высокого порядка является расширенным бизнес-компонентом после наследования. Этот подход аналогичен усилению защиты компонентов, поэтому вам необходимо знать текущую
class Index extends React.Component{
render(){
return <div> hello,world </div>
}
}
function HOC(Component){
return class wrapComponent extends Component{ /* 直接继承需要包装的组件 */
}
}
export default HOC(Index)
преимущество
- ① Удобно получать внутреннее состояние компонента, например
state
,props
, жизненный цикл, функции связанных событий и т. д. - ②
es6
Наследование может хорошо наследовать статические свойства. Нам не нужно выполнять дополнительную обработку статических свойств и методов.
class Index extends React.Component{
render(){
return <div> hello,world </div>
}
}
Index.say = function(){
console.log('my name is alien')
}
function HOC(Component) {
return class wrapComponent extends Component{
}
}
const newIndex = HOC(Index)
console.log(newIndex.say)
распечатать результат
недостаток
- ① Нельзя использовать компоненты без состояния.
- ② Сильная связь с упакованным компонентом, вам нужно знать внутреннее состояние упакованного компонента, что конкретно он делает?
- ③ Если множественное обратное наследование
hoc
Вложенные вместе, текущее состояние переопределяет предыдущее состояние. Скрытые опасности, приносимые этим, очень велики, например, многоcomponentDidMount
,токcomponentDidMount
перезапишет предыдущийcomponentDidMount
. Такие побочные эффекты связаны последовательно и имеют большое влияние.
3 Как писать компоненты высшего порядка
Далее давайте посмотрим, как написать компонент высокого уровня.Вы можете обратиться к следующим сценариям, чтобы написать свой собственныйHOC
.
1 Укрепить реквизит
① Добавьте реквизит
Это наиболее часто используемая функция высокоуровневых компонентов, которая берет на себя функции верхнего уровня.props
, смешивая в себеprops
, для усиления компонента.
Компоненты с отслеживанием состояния (прокси свойства)
function classHOC(WrapComponent){
return class Idex extends React.Component{
state={
name:'alien'
}
componentDidMount(){
console.log('HOC')
}
render(){
return <WrapComponent { ...this.props } { ...this.state } />
}
}
}
function Index(props){
const { name } = props
useEffect(()=>{
console.log( 'index' )
},[])
return <div>
hello,world , my name is { name }
</div>
}
export default classHOC(Index)
Компоненты с отслеживанием состояния (прокси свойства)
То же самое относится и к компонентам без состояния.
function functionHoc(WrapComponent){
return function Index(props){
const [ state , setState ] = useState({ name :'alien' })
return <WrapComponent { ...props } { ...state } />
}
}
Эффект
② Извлеките обновление управления состоянием
Компоненты высокого порядка могутHOC
изstate
Он работает вместе, чтобы контролировать обновление бизнес-компонентов. Это использование находится вreact-redux
серединаconnect
Используется в компонентах более высокого порядка для обработки данных изredux
серединаstate
Изменения, приносящие эффект обновлений подписки.
Мы изменим приведенный выше код.
function classHOC(WrapComponent){
return class Idex extends React.Component{
constructor(){
super()
this.state={
name:'alien'
}
}
changeName(name){
this.setState({ name })
}
render(){
return <WrapComponent { ...this.props } { ...this.state } changeName={this.changeName.bind(this) } />
}
}
}
function Index(props){
const [ value ,setValue ] = useState(null)
const { name ,changeName } = props
return <div>
<div> hello,world , my name is { name }</div>
改变name <input onChange={ (e)=> setValue(e.target.value) } />
<button onClick={ ()=> changeName(value) } >确定</button>
</div>
}
export default classHOC(Index)
Эффект
2 Управление рендерингом
Управление рендерингом — очень важная функция компонентов высшего порядка.Упомянутые выше два типа компонентов высшего порядка могут управлять рендерингом компонентов. Конкретная реализация все же отличается, давайте изучим ее вместе.
2.1 Условный рендеринг
① Основы: динамическая визуализация
Для компонента более высокого порядка прокси-свойства, несмотря на то, что состоянием визуализации нельзя управлять внутри, он может контролировать, визуализируется ли текущий компонент из внешнего слоя.Изоляция привилегий,ленивая загрузка,ленивая загрузкасцена.
Реализовать HOC, который динамически монтирует компоненты
function renderHOC(WrapComponent){
return class Index extends React.Component{
constructor(props){
super(props)
this.state={ visible:true }
}
setVisible(){
this.setState({ visible:!this.state.visible })
}
render(){
const { visible } = this.state
return <div className="box" >
<button onClick={ this.setVisible.bind(this) } > 挂载组件 </button>
{ visible ? <WrapComponent { ...this.props } setVisible={ this.setVisible.bind(this) } /> : <div className="icon" ><SyncOutlined spin className="theicon" /></div> }
</div>
}
}
}
class Index extends React.Component{
render(){
const { setVisible } = this.props
return <div className="box" >
<p>hello,my name is alien</p>
<img src='https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=294206908,2427609994&fm=26&gp=0.jpg' />
<button onClick={() => setVisible()} > 卸载当前组件 </button>
</div>
}
}
export default renderHOC(Index)
Эффект:
② Дополнительно: фрагментированный рендеринг
Вы чувствуете, что это не очень приятно, чтобы укрепить понимание каждогоHOC
Для понимания условного рендеринга я сделаю еще одинФрагментированный рендеринг + отложенная загрузкаФункция. Для того, чтобы все поняли, я тоже поломал голову 😂😂😂.
Дополнительно: реализовать HOC с функцией ленивой загрузки, которая может реализовать фрагментированный рендеринг компонентов, который используется для фрагментарного рендеринга страниц, чтобы не отображать большое количество компонентов одновременно и вызывать эффект белого экрана.
const renderQueue = []
let isFirstrender = false
const tryRender = ()=>{
const render = renderQueue.shift()
if(!render) return
setTimeout(()=>{
render() /* 执行下一段渲染 */
},300)
}
/* HOC */
function renderHOC(WrapComponent){
return function Index(props){
const [ isRender , setRender ] = useState(false)
useEffect(()=>{
renderQueue.push(()=>{ /* 放入待渲染队列中 */
setRender(true)
})
if(!isFirstrender) {
tryRender() /**/
isFirstrender = true
}
},[])
return isRender ? <WrapComponent tryRender={tryRender} { ...props } /> : <div className='box' ><div className="icon" ><SyncOutlined spin /></div></div>
}
}
/* 业务组件 */
class Index extends React.Component{
componentDidMount(){
const { name , tryRender} = this.props
/* 上一部分渲染完毕,进行下一部分渲染 */
tryRender()
console.log( name+'渲染')
}
render(){
return <div>
<img src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=294206908,2427609994&fm=26&gp=0.jpg" />
</div>
}
}
/* 高阶组件包裹 */
const Item = renderHOC(Index)
export default () => {
return <React.Fragment>
<Item name="组件一" />
<Item name="组件二" />
<Item name="组件三" />
</React.Fragment>
}
Эффект
Общий процесс, при инициализации,HOC
В функции рендеринга, которая будет отображать реальный компонент, поместите его вrenderQueue
Поставьте в очередь, затем инициализируйте рендеринг один раз, затем каждый компонент элемента, завершитеdidMounted
После состояния следующая функция рендеринга будет взята из очереди, и будет рендериться следующий компонент, пока не будут выполнены все задачи рендеринга, очередь рендеринга не будет очищена и эффективно не будет выполнен фрагментированный рендеринг.Этот метод очень эффективен для отображения массивные данные..
использоватьHOC
Реализована функция условного рендеринга - сегментированного рендеринга. Фактический условный рендеринг прост для понимания. Он заключается в управлении тем, монтировать ли компоненты через переменные для удовлетворения потребностей самого проекта. Условный рендеринг может развиваться во многих режимах. Здесь я представляю условный рендеринг.Двумя способами,надеюсь всем понятна суть.
③ Дополнительно: асинхронные компоненты (ленивая загрузка)
Я не знаю, использовали ли вы егоdva
,внутриdynamic
это приложениеHOC
Компоненты, реализованные режимом, загружаются асинхронно, здесь я упростил его и уточнил основной код следующим образом:
/* 路由懒加载HOC */
export default function AsyncRouter(loadRouter) {
return class Content extends React.Component {
state = {Component: null}
componentDidMount() {
if (this.state.Component) return
loadRouter()
.then(module => module.default)
.then(Component => this.setState({Component},
))
}
render() {
const {Component} = this.state
return Component ? <Component {
...this.props
}
/> : null
}
}
}
использовать
const Index = AsyncRouter(()=>import('../pages/index'))
hoc
Также можно комбинировать с другимиAPI
, выполните производную функцию. Совпадение, как указано вышеimport
Реализовать функцию асинхронной загрузки.HOC
С очень гибким,
④ Обратное наследование: перехват рендеринга
Режим обратного наследования HOC, который может обеспечить перехват гранулированного рендеринга, то есть может управлять компонентами базового класса.render
функцию, вы также можете вмешиваться в реквизит илиchildren
, давайте посмотрим, как использовать компоненты более высокого порядка в этом состоянии.
const HOC = (WrapComponent) =>
class Index extends WrapComponent {
render() {
if (this.props.visible) {
return super.render()
} else {
return <div>暂无数据</div>
}
}
}
⑤ Обратное наследование: изменить дерево рендеринга
Изменить состояние рендеринга (захватить рендеринг, чтобы заменить дочерние узлы)
class Index extends React.Component{
render(){
return <div>
<ul>
<li>react</li>
<li>vue</li>
<li>Angular</li>
</ul>
</div>
}
}
function HOC (Component){
return class Advance extends Component {
render() {
const element = super.render()
const otherProps = {
name:'alien'
}
/* 替换 Angular 元素节点 */
const appendElement = React.createElement('li' ,{} , `hello ,world , my name is ${ otherProps.name }` )
const newchild = React.Children.map(element.props.children.props.children,(child,index)=>{
if(index === 2) return appendElement
return child
})
return React.cloneElement(element, element.props, newchild)
}
}
}
export default HOC(Index)
Эффект
Мы используем угон рендеринга для манипулированияsuper.render()
ПослеReact.element
элементы, затем вяжитеcreateElement
, cloneElement
, React.Children
Ждатьapi
, можно гибко манипулировать, реальный рендерингreact.element
, Можно сказать, что это день и день, и это радость.
2.2 Регулирование рендеринга
hoc
В дополнение кусловный рендеринг,рендеринг угонаВ дополнение к функции, он также может выполнять троттлинговый рендеринг, то есть может оптимизировать производительность.Как это сделать, пожалуйста, не отставайте от моего ритма и смотрите вниз.
① Основы: принцип дросселирования
hoc
может сотрудничатьhooks
изuseMemo
ЖдатьAPI
При совместном использовании он может реализовать управление рендерингом бизнес-компонентов, сократить количество рендерингов и добиться эффекта оптимизации производительности. В следующем случае мы ожидаем тогда и только тогда, когдаnum
При изменении визуализируйте компонент, но не влияйте на полученныйprops
. Мы должны написать нашHOC
.
function HOC (Component){
return function renderWrapComponent(props){
const { num } = props
const RenderElement = useMemo(() => <Component {...props} /> ,[ num ])
return RenderElement
}
}
class Index extends React.Component{
render(){
console.log(`当前组件是否渲染`,this.props)
return <div>hello,world, my name is alien </div>
}
}
const IndexHoc = HOC(Index)
export default ()=> {
const [ num ,setNumber ] = useState(0)
const [ num1 ,setNumber1 ] = useState(0)
const [ num2 ,setNumber2 ] = useState(0)
return <div>
<IndexHoc num={ num } num1={num1} num2={ num2 } />
<button onClick={() => setNumber(num + 1) } >num++</button>
<button onClick={() => setNumber1(num1 + 1) } >num1++</button>
<button onClick={() => setNumber2(num2 + 1) } >num2++</button>
</div>
}
Эффект:
Как показано на рисунке, когда мы только нажимаемnum++
Когда дочерний компонент перерисовывается, нажимаются другие кнопки, и он отвечает только за передачуprops
, для достижения желаемого эффекта.
② Дополнительно: индивидуальный процесс рендеринга
Мышление: 🤔 Вышеприведенный случай просто вводит принцип. В реальных проектах невозможно количественно оценить производство. Причина в том, что нам нужно ориентироваться на разныеprops
изменить, написать другоеHOC
компоненты, это не запустится вообщеHoc
реальное использование, т.HOC
исходное намерение. Поэтому нам нужноhoc
Для преобразования и обновления компонент может визуализировать компонент в соответствии с настроенным направлением. то естьHoc
При генерации рендеринг выполняется по определенному контракту.
function HOC (rule){
return function (Component){
return function renderWrapComponent(props){
const dep = rule(props)
const RenderElement = useMemo(() => <Component {...props} /> ,[ dep ])
return RenderElement
}
}
}
/* 只有 props 中 num 变化 ,渲染组件 */
@HOC( (props)=> props['num'])
class IndexHoc extends React.Component{
render(){
console.log(`组件一渲染`,this.props)
return <div> 组件一 : hello,world </div>
}
}
/* 只有 props 中 num1 变化 ,渲染组件 */
@HOC((props)=> props['num1'])
class IndexHoc1 extends React.Component{
render(){
console.log(`组件二渲染`,this.props)
return <div> 组件二 : my name is alien </div>
}
}
export default ()=> {
const [ num ,setNumber ] = useState(0)
const [ num1 ,setNumber1 ] = useState(0)
const [ num2 ,setNumber2 ] = useState(0)
return <div>
<IndexHoc num={ num } num1={num1} num2={ num2 } />
<IndexHoc1 num={ num } num1={num1} num2={ num2 } />
<button onClick={() => setNumber(num + 1) } >num++</button>
<button onClick={() => setNumber1(num1 + 1) } >num1++</button>
<button onClick={() => setNumber2(num2 + 1) } >num2++</button>
</div>
}
Эффект
Эффект достигается отлично. При этом используется шаблон компонентов более высокого порядка, которым можно гибко управлять.React
на уровне компонентов,props
поток данныха такжепоток обновлений, превосходные компоненты высшего порядка имеютmobx
серединаobserver
,inject
, react-redux
серединаconnect
, Заинтересованные студенты, вы можете потратить время на изучение.
3 активирующих компонента
В дополнение к вышеуказанным двумя функциям, компоненты более высокого порядка могут также включить компоненты, такие как добавление некоторыхдополнительный生命周期
,инцидент с угоном,Журналы мониторингаи т.п.
3.1 Захват цепочки прототипов — захват жизненного цикла, функции событий
① Реализация агента атрибутов
function HOC (Component){
const proDidMount = Component.prototype.componentDidMount
Component.prototype.componentDidMount = function(){
console.log('劫持生命周期:componentDidMount')
proDidMount.call(this)
}
return class wrapComponent extends React.Component{
render(){
return <Component {...this.props} />
}
}
}
@HOC
class Index extends React.Component{
componentDidMount(){
console.log('———didMounted———')
}
render(){
return <div>hello,world</div>
}
}
Эффект
② Реализация обратного наследования
Обратное наследование, так как на основе наследования исходных компонентов можноЖизненный циклилимероприятиеУгнать, а то и заменить.
function HOC (Component){
const didMount = Component.prototype.componentDidMount
return class wrapComponent extends Component{
componentDidMount(){
console.log('------劫持生命周期------')
if (didMount) {
didMount.apply(this) /* 注意 `this` 指向问题。 */
}
}
render(){
return super.render()
}
}
}
@HOC
class Index extends React.Component{
componentDidMount(){
console.log('———didMounted———')
}
render(){
return <div>hello,world</div>
}
}
3.2 Мониторинг событий
HOC
Оригинальные компоненты также можно контролировать. такие как некоторые事件监控
,错误监控
,事件监听
Дождитесь серии операций.
① Мониторинг событий в компонентах
Далее делаемHOC
, выполняйте мониторинг только для события click в компоненте.
function ClickHoc (Component){
return function Wrap(props){
const dom = useRef(null)
useEffect(()=>{
const handerClick = () => console.log('发生点击事件')
dom.current.addEventListener('click',handerClick)
return () => dom.current.removeEventListener('click',handerClick)
},[])
return <div ref={dom} ><Component {...props} /></div>
}
}
@ClickHoc
class Index extends React.Component{
render(){
return <div className='index' >
<p>hello,world</p>
<button>组件内部点击</button>
</div>
}
}
export default ()=>{
return <div className='box' >
<Index />
<button>组件外部点击</button>
</div>
}
Эффект
3 ref help экземпляр компонента управления
Для прокси свойства, хотя мы и не можем напрямую получить состояние в компоненте, мы можем передатьref
Получите экземпляр компонента, получите экземпляр компонента, вы можете получить некоторый статус компонента или вручную инициировать некоторые события для дальнейшего усиления компонента, но обратите внимание на:class
Только объявленные компоненты с состоянием имеют экземпляры,function
Для объявленного компонента без состояния не существует экземпляра.
① Агент по недвижимости — добавить дополнительный жизненный цикл
Мы можем добавить компоненту дополнительный жизненный цикл для определенной ситуации, я сделал простойdemo
, мониторnumber
изменить, еслиnumber
Изменить, это автоматически вызовет функцию слушателя компонента.handerNumberChange
.
Конкретное письмо выглядит следующим образом
function Hoc(Component){
return class WrapComponent extends React.Component{
constructor(){
super()
this.node = null
}
UNSAFE_componentWillReceiveProps(nextprops){
if(nextprops.number !== this.props.number ){
this.node.handerNumberChange && this.node.handerNumberChange.call(this.node)
}
}
render(){
return <Component {...this.props} ref={(node) => this.node = node } />
}
}
}
@Hoc
class Index extends React.Component{
handerNumberChange(){
/* 监听 number 改变 */
}
render(){
return <div>hello,world</div>
}
}
Этот способ написания немного неудовлетворителен, не волнуйтесь, в четвертой части, в битве за исходный код, я представлю лучший сценарий. легко понять каждомуHoc
Включение оригинальных компонентов.
4 Резюме
Я следую вышеизложенномуhoc
Основная функция,Укрепить реквизит,рендеринг управления,включение компонентовтри направленияHOC
Напишите подробное введение, введение и сценарии применения, цельПусть все понимают, когда они будут использоваться, когда понимают высокоуровневые компоненты. , как писать? `Я всегда буду резюмировать точки знаний, затронутые в нем.
Для прокси-сервера недвижимости HOC мы можем:
- Укрепляйте реквизит и извлекайте состояние.
- Условный рендеринг, контролируемый рендеринг, фрагментированный рендеринг, отложенная загрузка.
- Перехват событий и жизненных циклов
- Экземпляр компонента управления ссылкой
- Добавить прослушиватель событий, журнал
Для HOC обратного прокси мы можем:
- Перехват рендеринга, манипулирование деревом рендеринга
- Контролируйте/заменяйте жизненный цикл, напрямую получайте состояние компонента и связывайте события.
Для каждого сценария приложения я дал примеры 🌰🌰, вы можете использовать примеры, чтобы понять его принципы и способы использования в деталях.
Четыре исходных уровня компонентов высокого уровня
hoc
Есть много сценариев приложений и много хороших проектов с открытым исходным кодом, которые мы можем изучить и на которые можно ссылаться.Далее я проанализирую функциональное использование в трех направлениях с точки зрения исходного кода.HOC
использование.
1 Усилить prop-withRoute
использовалwithRoute
одноклассники, все понимают его предназначение,withRoute
Цель в том, что дляRoute
Завернутые компоненты, чтобы добавитьhistory
Объекты и другие состояния, связанные с маршрутизацией, чтобы мы могли получать статус маршрутизации и выполнять переходы по маршруту в любом компоненте.HOC
Цель ясна, укрепитьprops
,ПучокRouter
родственные состояния смешиваются вprops
, давайте посмотрим, как это реализовано.
function withRouter(Component) {
const displayName = `withRouter(${Component.displayName || Component.name})`;
const C = props => {
/* 获取 */
const { wrappedComponentRef, ...remainingProps } = props;
return (
<RouterContext.Consumer>
{context => {
return (
<Component
{...remainingProps}
{...context}
ref={wrappedComponentRef}
/>
);
}}
</RouterContext.Consumer>
);
};
C.displayName = displayName;
C.WrappedComponent = Component;
/* 继承静态属性 */
return hoistStatics(C, Component);
}
export default withRouter
withRoute
Процесс на самом деле очень прост, просто начните сprops
выделитьref
а такжеprops
, а затем сохранить весьroute
контекст объектаRouterContext
выигратьroute
объект, который затем смешивается с исходным компонентомprops
средний, последний сhoistStatics
Наследовать статические свойства. Что касаетсяhoistStatics
Мы вернемся к этому позже.
2 Управление рендерингом case connect
из-заconnect
Исходный код относительно длинный и сложный для понимания, поэтому мы извлекаем суть, упрощаем и упрощаем, и обобщаем основные функции следующим образом:connect
также оказывает влияние合并props
, но что более важно принятьstate
, чтобы управлять компонентом обновления. В следующем коде я упростил его для всеобщего удобства. Я надеюсь, вы можете понятьhoc
какраспространятьа такжеконтрольпоток обновлений.
import store from './redux/store'
import { ReactReduxContext } from './Context'
import { useContext } from 'react'
function connect(mapStateToProps){
/* 第一层: 接收订阅state函数 */
return function wrapWithConnect (WrappedComponent){
/* 第二层:接收原始组件 */
function ConnectFunction(props){
const [ , forceUpdate ] = useState(0)
const { reactReduxForwardedRef ,...wrapperProps } = props
/* 取出Context */
const { store } = useContext(ReactReduxContext)
/* 强化props:合并 store state 和 props */
const trueComponentProps = useMemo(()=>{
/* 只有props或者订阅的state变化,才返回合并后的props */
return selectorFactory(mapStateToProps(store.getState()),wrapperProps)
},[ store , wrapperProps ])
/* 只有 trueComponentProps 改变时候,更新组件。 */
const renderedWrappedComponent = useMemo(
() => (
<WrappedComponent
{...trueComponentProps}
ref={reactReduxForwardedRef}
/>
),
[reactReduxForwardedRef, WrappedComponent, trueComponentProps]
)
useEffect(()=>{
/* 订阅更新 */
const checkUpdate = () => forceUpdate(new Date().getTime())
store.subscribe( checkUpdate )
},[ store ])
return renderedWrappedComponent
}
/* React.memo 包裹 */
const Connect = React.memo(ConnectFunction)
/* 处理hoc,获取ref问题 */
if(forwardRef){
const forwarded = React.forwardRef(function forwardConnectRef( props,ref) {
return <Connect {...props} reactReduxForwardedRef={ref} reactReduxForwardedRef={ref} />
})
return hoistStatics(forwarded, WrappedComponent)
}
/* 继承静态属性 */
return hoistStatics(Connect,WrappedComponent)
}
}
export default Index
connect
Задействовано довольно много функциональных точек: первый уровень принимает функции подписки, второй уровень получает оригинальные компоненты, а затем используетforwardRef
иметь дело сref
,использоватьhoistStatics
Обработка наследования статических свойств, внутри компонентов-оболочек, слияниеprops
,useMemo
Кешировать исходные компоненты, только объединенныеprops
изменения, обновите только компонент, а затемuseEffect
внутренний пропускstore.subscribe()
Подпишитесь на обновления. здесь опущеноSubscription
Концепция, правдаconnect
есть одинSubscription
Конкретно отвечает за подписку на сообщения.
3 Включающий компонент - жизненный цикл кеша keepaliveLifeCycle
Я ранее написалreact
Библиотека с открытым исходным кодом для кэширования страницreact-keepalive-router
, можно реализоватьvue
серединаkeepalive
+ router
Function, в исходной версии не было периода кэширования, но более поздние читатели-энтузиасты рассчитывали добавить период кэширования в кэшированный компонент маршрутизации, подобноactivated
Этот вид, после моего анализа, я планирую использоватьHOC
для достижения этой функции.
так react-keepalive-router
Добавлен новый жизненный цикл компонента страницыactived
а такжеunActived
, actived
Он используется, когда компонент маршрутизации кеша активирован, и он будет выполнен один раз по умолчанию, когда он инициализируется.unActived
После завершения кэширования компонента маршрутизации вызовов. Но необходимость использования жизненного циклаHOC
компонентыkeepaliveLifeCycle
пакет.
использовать
import React from 'react'
import { keepaliveLifeCycle } from 'react-keepalive-router'
@keepaliveLifeCycle
class index extends React.Component<any,any>{
state={
activedNumber:0,
unActivedNumber:0
}
actived(){
this.setState({
activedNumber:this.state.activedNumber + 1
})
}
unActived(){
this.setState({
unActivedNumber:this.state.unActivedNumber + 1
})
}
render(){
const { activedNumber , unActivedNumber } = this.state
return <div style={{ marginTop :'50px' }} >
<div> 页面 actived 次数: {activedNumber} </div>
<div> 页面 unActived 次数:{unActivedNumber} </div>
</div>
}
}
export default index
Эффект:
принцип
import {lifeCycles} from '../core/keeper'
import hoistNonReactStatic from 'hoist-non-react-statics'
function keepaliveLifeCycle(Component) {
class Hoc extends React.Component {
cur = null
handerLifeCycle = type => {
if (!this.cur) return
const lifeCycleFunc = this.cur[type]
isFuntion(lifeCycleFunc) && lifeCycleFunc.call(this.cur)
}
componentDidMount() {
const {cacheId} = this.props
cacheId && (lifeCycles[cacheId] = this.handerLifeCycle)
}
componentWillUnmount() {
const {cacheId} = this.props
delete lifeCycles[cacheId]
}
render=() => <Component {...this.props} ref={cur => (this.cur = cur)}/>
}
return hoistNonReactStatic(Hoc,Component)
}
keepaliveLifeCycle
Принцип очень прост, т.ref
или получитьclass
Экземпляр компонента вhoc
при инициализацииСвязать жизненный цикл, существуетhoc
На стадии разрушения жизненный цикл освобождается, а затем передаетсяkeeper
единый график,keeper
Вызов функций жизненного цикла в следующих примерах для реализации функции кэширования жизненного цикла.
5 соображений относительно компонентов высшего порядка
1 Аккуратно измените цепочку прототипов
function HOC (Component){
const proDidMount = Component.prototype.componentDidMount
Component.prototype.componentDidMount = function(){
console.log('劫持生命周期:componentDidMount')
proDidMount.call(this)
}
return Component
}
Это влечет за собой некоторые нежелательные последствия. Например, если вы используете другой, он также изменитcomponentDidMount
изHOC
улучшить его, то предыдущийHOC
не удастся! В то же время этоHOC
Его нельзя применить и к функциональным компонентам без жизненного цикла.
2 Наследование статических свойств
Написано в виде доверенности собственностиHOC
При его использовании следует обратить внимание на проблему отсутствия статических свойств, как было сказано ранее, если с ней не разобраться, все статические методы будут потеряны.
Ручное наследование
Мы можем вручную разместить статический метод исходного компонентаcopy
прибытьhoc
Появляются компоненты, но предпосылка заключается в том, что вы должны точно знать, какие методы следует копировать.
function HOC(Component) {
class WrappedComponent extends React.Component {
/*...*/
}
// 必须准确知道应该拷贝哪些方法
WrappedComponent.staticMethod = Component.staticMethod
return WrappedComponent
}
Импорт сторонних библиотек
Будет утомительно связывать каждый статический метод, особенно для открытого исходного кода.hoc
,Статические методы на нативных компонентах неизвестны, мы можем использоватьhoist-non-react-statics
Автоматически копировать все статические методы:
import hoistNonReactStatic from 'hoist-non-react-statics'
function HOC(Component) {
class WrappedComponent extends React.Component {
/*...*/
}
hoistNonReactStatic(WrappedComponent,Component)
return WrappedComponent
}
3 Захватите реф по уровням
Соглашение о компонентах более высокого порядка состоит в том, чтобы объединить всеprops
передается обернутому компоненту, но это дляrefs
Непригодный. это потому, чтоref
на самом деле неprop
- подобноkey
Мол, это сделаноReact
специально обработанный. еслиref
добавить вHOC
в возвращаемом компоненте , тоref
Ссылка указывает на компонент-контейнер, а не на компонент-оболочку. мы можем пройтиforwardRef
Для решения этой проблемы.
/**
*
* @param {*} Component 原始组件
* @param {*} isRef 是否开启ref模式
*/
function HOC(Component,isRef){
class Wrap extends React.Component{
render(){
const { forwardedRef ,...otherprops } = this.props
return <Component ref={forwardedRef} {...otherprops} />
}
}
if(isRef){
return React.forwardRef((props,ref)=> <Wrap forwardedRef={ref} {...props} /> )
}
return Wrap
}
class Index extends React.Component{
componentDidMount(){
console.log(666)
}
render(){
return <div>hello,world</div>
}
}
const HocIndex = HOC(Index,true)
export default ()=>{
const node = useRef(null)
useEffect(()=>{
/* 就可以跨层级,捕获到 Index 组件的实例了 */
console.log(node.current.componentDidMount)
},[])
return <div><HocIndex ref={node} /></div>
}
распечатать результат:
Решается, как указано выше,HOC
Захват через уровниref
Эта проблема.
4 Не объявляйте HOC в рендере
🙅 Неправильное написание:
class Index extends React.Component{
render(){
const WrapHome = HOC(Home)
return <WrapHome />
}
}
Если это будет написано так, это вызовет огромную проблему, потому что каждый разHOC
вернет новыйWrapHome
,react diff
будут судить дваждыне тот компонент, то каждый разIndex
компонентыrender
вызывать,WrapHome
, перемонтируется, статус будетвсе потеряно. Если вы хотите динамическую привязкуHOC
, пожалуйста, обратитесь к следующему методу.
🙆 Правильное написание:
const WrapHome = HOC(Home)
class index extends React.Component{
render(){
return <WrapHome />
}
}
Шесть резюме
В этой статье в качестве отправной точки рассматривается функция компонентов высокого порядка, и рассказывается, как написать два разных компонента высокого порядка, сценарии приложений и практики. Охватывает большинство знакомых сценариев приложений высокоуровневых компонентов с открытым исходным кодом.Если вы считаете, что эта статья вдохновляет вас, лучше всего следовать инструкциям в статье.demo
, с последующим повторным набором текста, углублением впечатления, знанием того, в каких сценариях использовать высокоуровневые компоненты, как использовать высокоуровневые компоненты.
实践是检验真理的唯一标准
, надеюсь можно поставить компоненты более высокого порядка码
Вставай, используй.
Наконец, подарите розы и оставьте аромат в ваших руках.Друзья, которые чувствуют, что что-то приобрели, могут подарить их автору.Нравится, следуйОдна волна, обновляйте фронтальные суперхардкорные статьи одну за другой.
Просмотрите классические хорошие статьи о реакции в прошлом
реагировать продвинутая серия
-
Восемь предложений по оптимизации для разработчиков React в конце года «React Advanced»
840+
Нравится👍
серия исходников реакции
-
"Анализ исходного кода" На этот раз я полностью понимаю принцип маршрутизации react-router
80+
Нравится👍
серия реактивных крючков
-
Играйте с реактивными хуками, пользовательскими шаблонами дизайна хуков и их реальной борьбой
160+
👍Нравится -
Как использовать React-крючки
90+
Нравится👍
Серия проектов с открытым исходным кодом
-
«Страница кеша React» от спроса до открытого исходного кода (как я впечатлил даму продукта)
250+
лайк 👍