DevGang
Авторизоваться

Как избежать Prop Drilling в React

Чтобы писать масштабируемые, повторно используемые и поддерживаемые приложения с помощью React, вам нужно будет выйти за рамки использования компонентов React, useEffect, useContext, useState и тому подобного. Это предполагает более детальное изучение того, как работает React.

И если вы должным образом не понимаете эти ключевые концепции React, вы можете столкнуться с различными проблемами, такими как prop drilling.

В этом руководстве вы узнаете, что такое prop drilling. Я также научу вас, как интуитивно избежать этого, не полагаясь на контекст React. В конце концов, вы поймете, как распознать prop drilling, не задумываясь, и зафиксировать его с высокой точностью.

Если вы предпочитаете наглядное руководство, вот видеоверсия этого урока на моем канале YouTube (примерно 15 минут).

Что такое Prop Drilling?

Prop drilling (бурение пропов) происходит, когда родительский компонент генерирует свое состояние и передает props своим дочерним компонентам, которые не используют реквизиты — вместо этого они только передают его другому компоненту, который в конечном итоге его использует.

Ниже приведен пример prop drilling в React:

function App() {
  const [profile, setProfile] = useState({ame: 'John'}); 
  return ( 
    <div> <Header profile={profile} /> 
    </div> 
  ); 
} 
  
function Header({ profile }) { 
  return ( 
    <header> 
      <h1>This is the header</h1> 
      <Content profile={profile} /> 
    </header> 
  ); 
} 

function Content({ profile }) { 
  return ( 
    <main> 
      <h2>Content Component</h2> 
      <p>{profile.name}</p> 
    </main> 
  ); 
} 

export default App;

Если вы ознакомитесь с приведенным выше примером, вы заметите, что profile передается из компонента App через Header в компонент Content, который в конечном итоге использует props. Это обычно называют разбиением по реквизитам (prop drilling), поскольку компонент Header не использует prop, а только передает его компоненту Content, который в конечном итоге его использует.

Теперь, когда вы понимаете, что такое prop drilling, следующая задача - выяснить, как его избежать, потому что это не всегда интуитивно понятный процесс.

Вам нужно будет начать изучать методы решения этой проблемы. Хотя вы можете использовать компонент Composition и React Context для ее решения, проблема заключается в том, что вы не всегда можете распознать проблему позже.

Чтобы по-настоящему овладеть искусством интуитивного сверления реквизита, вы должны научиться распознавать удлиненные реквизиты и контексты.

Что такое удлиненный реквизит (Elongated Prop)?

Elongated prop - это prop, который не потребляется, а только передается другому компоненту. Когда компонент получает prop от своего родительского компонента и не использует prop, он передает свойство другому компоненту. Эта свойство называется удлиненным, потому что она была удлинена.

Всякий раз, когда вы видите, что prop передается компонентами, которые не создают и не потребляют prop, в вашем коде появляется удлиненный реквизит (а также prop drilling). Приведенный ниже фрагмент кода является примером:

function Profile({ user }) { 
  return ( 
    <header> 
      <h1>This is the header</h1> 
      <Content user={user} /> 
    </header> 
  ); 
}

user в этом примере представляет собой удлиненный prop, поскольку он не создается и не используется компонентом Profile. Вместо этого он передается только компоненту Content. А это означает, что мы расширили user через компонент, которому он не нужен, чтобы он мог добраться до того, которому он нужен.

Теперь давайте вернемся к примеру, который мы использовали для иллюстрации prop drilling. Подождите, вы думаете о том же, о чем и я? prop, который передается в примере с prop drilling, действительно является elongated prop, верно? Да, у вас получилось.

function App() {
  const [profile, setProfile] = useState({ame: 'John'}); 
  return ( 
    <div> 
      <Header profile={profile} /> 
    </div> 
  ); 
} 
  
function Header({ profile }) { 
  return ( 
    <header> 
      <h1>This is the header</h1> 
      <Content profile={profile} /> 
    </header> 
  ); 
} 

function Content({ profile }) { 
  return ( 
    <main> 
      <h2>Content Component</h2> 
      <p>{profile.name}</p> 
    </main> 
  ); 
} 

export default App;

В приведенном выше коде вы можете заметить, что prop, переданное в Header, создается в компоненте App. Затем Header передает его своему дочернему компоненту с именем Content. В результате передаваемый profile можно считать удлиненным, поскольку он передается через компонент (Header), который не создает и не потребляет его, вплоть до того, который это делает.

Компонент Header, передающий prop, которое он не создает или не требует, излишне растягивает контекст prop.

Теперь вопрос в том, как удлиненные реквизиты помогают интуитивно избежать prop drilling в React? Они позволяют вам легко обнаружить использование prop там, где он не создается и не потребляется.

Вместо того чтобы сосредотачиваться на том, как решить проблему сверления пропсов, удлиненные реквизиты позволяют вам избежать этого. Это связано с тем, что интуитивно понятно, когда компонент не создает и не потребляет prop, и это помогает вам понять, что компонент не имеет отношения к делу.

Но прежде чем вы узнаете, как быстро избежать prop drilling, имея представление о удлиненных реквизитах, важно, чтобы вы знали основные причины сверления пропсов. Тогда вы действительно будете знать, как избежать этого, не задумываясь об этом.

Что вызывает Prop Drilling?

Сверление пропсов не возникает из воздуха. Это следствие неадекватной организации компонентов, и это не проблема React. Это проблема мышления или дизайна.

Вы не столкнетесь с примером prop drilling, не заметив одну из следующих ошибок компоновки:

  • Прежде всего, группировка статических элементов и зависимых компонентов вместе для достижения привлекательного дизайна пользовательского интерфейса является основной причиной prop drilling. Вы не можете избежать детализации реквизита, когда ваш пользовательский интерфейс группирует статические элементы и зависимые компоненты вместе в родительском элементе. Родительский компонент явно не будет использовать prop, поскольку все внутри него является статическим элементом – за исключением компонента, который нуждается в prop.

Вот пример:

function Header({ profile }) { 
  return ( 
    <header> 
      <h1>This is the header</h1> 
      <Content profile={profile} /> 
    </header> 
  ); 
}

В этом случае статические элементы <header> и <h1> сгруппированы с зависимым компонентом Content – и именно поэтому у нас есть prop-детализация в них.

При условии, что компонент Content независим или не требует props, ему не понадобится профиль, и, во-первых, не будет сверления пропса. Вот почему принуждение компонента, который должен быть независимым, принимать props от своего родительского компонента - это рецепт для prop drilling в React.

  • Во-вторых, когда компонент принимает props, который он не использует, а просто передает его своим дочерним элементам, это признак того, что в вашем компоненте есть prop drilling:
function App () { 
  const [profile, setProfile] = useState({name: "Ayobami"})
  return ( 
    <>
      <Parent profile={profile} /> 
    </>
 ); 
}; 

function Parent({ profile }) { 
  return ( 
    <div>
      <Hero profile={profile} /> 
      <Features profile={profile} /> 
    </div>
 ); 
}; 

В этом случае происходит детализация prop, потому что Parent компонент принимает profile и не использует его, хотя и передает своим дочерним компонентам.

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

Исключением может быть, если он намеренно привязан к своему родительскому элементу по определенным причинам. В таких случаях prop drilling становится необходимым компромиссом.

Если вы вернетесь к примеру prop drilling, приведенному в этой статье, вы поймете, что в нем есть проблема prop drilling, потому что компонент Content, который мог бы быть независимым компонентом, имея состояние, вынужден получать props от своего родительского элемента.

  • И, наконец, наличие удлиненных реквизитов является верным признаком prop drilling. Поскольку elongated prop является фундаментальным элементом, который неизменно присутствует в каждом случае сверления пропсов, понимание этой концепции позволяет вам инстинктивно избегать prop drilling.

Когда вы замечаете elongated prop, вы можете быть уверены, что одна из трех других ошибок также имеет место. Короче говоря, удлиненный реквизит - это реквизит, который не потребляется, а также передается другому компоненту.

Таким образом, группировка статических элементов с зависимыми компонентами, принуждение компонентов принимать реквизиты, удлиненные реквизиты и получение реквизита без его использования - это признаки распознавания prop drilling в React.

Как исправить Prop Drilling с помощью компонента Composition

Композиция компонентов является хорошим подходом для фиксации prop drilling. Если вы когда-нибудь окажетесь в ситуации, когда компонент передает свойство, которое он не создает и не потребляет, вы можете использовать композицию компонентов, чтобы исправить это.

Но чтобы использовать композицию компонентов, вам необходимо понимать контекст компонента.

Что такое контекст компонента?

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

function App() { 
  const [profile, setProfile] = useState({name: 'Ayobami'}); 
  return ( 
    <div> 
      <Header profile={profile} /> 
    </div> 
  ); 
} 

export default App;

В этом сценарии контекст App относится ко всему, что мы можем видеть в компоненте App, включая реквизит profile, Header и другое содержимое App. Следовательно, любые данные, созданные в компоненте App, в идеале должны использоваться в самом компоненте App либо как его собственные данные, либо как props для его дочерних элементов.

Prop drilling всегда возникает, когда дочерние компоненты, получающие props, не потребляют его, а только передают своим дочерним компонентам.

Чтобы избежать детализации реквизита в этом случае, любые дочерние компоненты, которым требуется доступ к тем же props, особенно когда их родительский элемент не использует данные, должны передаваться как дочерние, гарантируя, что данные остаются в контексте App.

export function App() { 
  const [profile, setProfile] = useState({name: 'Ayobami'}); 
  return ( 
    <div> 
      <Header> 
        <Content profile={profile} /> 
      </Header> 
    </div> 
  ); 
}

Или 

export function App() { 
  const [profile, setProfile] = useState({name: 'Ayobami'}); 
  return ( 
    <div> 
      <Header children={<Content profile={profile} />} > 
    </div> 
  ); 
}

Как вы можете видеть, мы решили проблему с prop drilling в предыдущем примере, хотя у нас все еще есть избыточный компонент <Header>, верно? Мы успешно справились с prop drilling с помощью компонентного состава.

Этот процесс довольно прост, потому что мы концентрируемся на распознавании удлиненных реквизитов и их перемещении в соответствующих контекстах.

Концепция prop drilling ориентирована на решение проблемы, но удлинение реквизитов зависит от решения. Когда мы имеем дело с удлиненными реквизитами, наша основная цель - идентифицировать реквизиты, которые не потребляются, а только передаются другим компонентам.

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

Prop drilling также можно исправить, переместив состояние в то, в котором оно используется. В примере prop drilling в этой статье есть компонент с именем Content. Но компонент вынужден получать prop от своего родительского элемента вместо того, чтобы иметь состояние и быть независимым компонентом – и поэтому у нас есть prop drilling.

В этом случае мы можем исправить prop drilling, переместив состояние профиля туда, где оно используется.

Давайте вернемся к примеру:

function App() {
  const [profile, setProfile] = useState({ame: 'John'}); 
  return ( 
    <div> 
      <Header profile={profile} />
      <Footer profile={profile />
    </div> 
  ); 
} 
  
function Header({ profile }) { 
  return ( 
    <header> 
      <h1>This is the header</h1> 
      <Content profile={profile} /> 
    </header> 
  ); 
} 

function Content({ profile }) { 
  return ( 
    <main> 
      <h2>Content Component</h2> 
      <p>{profile.name}</p> 
    </main> 
  ); 
} 

export default App;

В этом случае мы можем исправить prop drilling, переместив profile туда, где он используется:

function App() { 
  return ( 
    <div> 
      <Header />
      <Footer profile={profile />
    </div> 
  ); 
} 
  
function Header() { 
  return ( 
    <header> 
      <h1>This is the header</h1> 
      <Content /> 
    </header> 
  ); 
} 

function Content({ profile }) { 
  const [profile, setProfile] = useState({ame: 'John'});
  return ( 
    <main> 
      <h2>Content Component</h2> 
      <p>{profile.name}</p> 
    </main> 
  ); 
}

Теперь, когда мы перенесли профиль в компонент Content, где он используется, компонент App не имеет состояния, в то время как компонент Header снова не получает prop, поскольку компонент Content имеет свое состояние.

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

Теперь, когда Footer нуждается в profile в качестве prop, нам нужно решить проблему prop drilling другим методом.

Как исправить Prop Drilling с помощью стратегии замены детей-родителей

Ранее в этой статье мы говорили о том, как использовать состав компонентов и перемещать состояние к потребителю для решения задач prop drilling. Но, как вы видели, у них есть некоторые проблемы — дублирование компонентов или состояний.

Но использование этого подхода, основанного на замене детей-родителей, эффективно решает проблему:

Работает, но могло быть лучше:

export function App() { 
  const [profile, setProfile] = useState({name: 'Ayobami'}); 
  return ( 
    <div> 
      <Header> 
        <Content profile={profile} /> 
      </Header> 
    </div> 
  ); 
}

function Header({ profile }) { 
  return ( 
    <header> 
      <h1>This is the header</h1> 
      <Content profile={profile} /> 
    </header> 
  ); 
}

В приведенном выше примере показано решение для примера prop drilling в этой статье. Но, как вы можете видеть, у него есть избыточный компонент, поскольку заголовок ничего не делает.

Вот лучшая версия:

export function App() { 
  const [profile, setProfile] = useState({name: 'Ayobami'}); 
  return ( 
    <header> 
      <h1>This is the header</h1> 
      <Content profile={profile} /> 
    </header> 
  ); 
}

В приведенном выше коде мы улучшаем решение по составу компонентов, которое мы ранее реализовали для примера бурения пропсов, заменяя избыточный компонент заголовка его содержимым в его родительском элементе (приложении).

Чего следует избегать

Очень важно подчеркнуть, чего следует избегать при prop drilling, чтобы избежать ненужных проблем.

  • По возможности избегайте контекста React, чтобы исправить prop drilling. Такой подход привязывает ваш компонент к определенному контексту, ограничивая его удобство использования за пределами этого контекста и затрудняя композицию и возможность повторного использования.
  • Избегайте избыточных компонентов, используя подход замены детей и родителей. Этот подход естественным образом включает композицию компонентов без введения избыточных компонентов или состояний при расчете винтового бурения.

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

Понимая, что такое удлиненные реквизиты, вы можете уверенно размещать реквизит и компоненты в нужном контексте без излишнего стресса.

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

Спасибо за прочтение – удачи!

Источник:

#JavaScript #React
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу