Реагировать на разницу между компонентом и рендером

Я действительно не понимаю разницы между рендерингом и компонентом prop в Route в ответном маршрутизаторе, в документах говорится, что рендер не создает новый элемент, а компонент делает, я пытался вернуться с историей, но я обнаружил, что componentWillMount вызываемые при использовании рендеринга в Route, что они подразумевают под "если вы предоставляете встроенную функцию атрибуту компонента, вы должны создать новый компонент для каждого рендеринга. Это приведет к размонтированию существующего компонента и установке нового компонента вместо простого обновления существующий компонент."

Ответ 1

Исходный код говорит о различии:

if (component)
  return match ? React.createElement(component, props) : null

if (render)
  return match ? render(props) : null

Когда вы используете component prop, компонент создается для каждого вызова Route#render. Это означает, что для вашего компонента, который вы передаете component prop of Route, конструктор, componentWillMount и componentDidMount будут выполняться каждый раз, когда маршрут отображается.

Например, если у вас есть

<Route path="/:locale/store" component={Store} />

и пользователь переходит в /en/store, затем переходит в другое место, а затем переводит обратно в /en/store, компонент Store будет монтироваться, затем отключен, а затем снова установлен. Это похоже на выполнение

<Route path="/:locale/store">
  <Store />
</Route>

По сравнению с этим, если вы используете render prop, компонент оценивается на каждом Route#render. Помните, что каждый компонент является функцией? Эта функция будет выполняться как есть, без каких-либо методов жизненного цикла. Поэтому, когда вам это нравится

<Route path="/:locale/store" render={Store} />

вы можете думать об этом как

<Route path="/:locale/store">
  {Store()}
</Route>

Это экономит время выполнения, поскольку не выполняются методы жизненного цикла, но также имеет недостаток, если в компоненте Store есть некоторые методы жизненного цикла после установки, такие как shouldComponentUpdate, которые также могут повысить производительность.


Был хороший пост на Medium об этом взломе производительности, пожалуйста, взгляните на него. Это написано очень хорошо и применимо также к React 16.

Ответ 2

Поэтому я тоже запутался в этом разделе документов, но наконец-то понял.

Ключом к пониманию этого является утверждение "предоставить встроенную функцию компоненту prop"

Мы все знаем, что компонент Route выполнит повторную визуализацию при изменении местоположения, и реагирует на сравнение старого и нового виртуального дерева DOM, получит некоторый результат сравнения и применим к реальному DOM.

И реакция будет стараться изо всех сил повторно использовать узел DOM, если не изменена тип или ключ нового ReactElement.

Так

// 1.
const componentA = React.createElement(App, props)
const componentB = React.createElement(App, props)
console.log(componentA.type === componentB.type)             // true

// 2.
const componentA = React.createElement(() => <App />, props)
const componentB = React.createElement(() => <App />, props)
console.log(componentA.type === componentB.type)             // false

Все элементы ReactElement, созданные способом 1, имеют одинаковый тип (компонент приложения), но они не имеют одинакового типа, если все они созданы способом 2.

Почему?

Поскольку всегда существует новая анонимная функция, созданная способом 2, когда вызывается метод визуализации родительского компонента (компонента, который содержит компонент Route), поэтому тип нового & старого ReactElement - это два разных экземпляра анонимная функция

() => <App />

Таким образом, с точки зрения React существуют разные типы элементов, и их следует обрабатывать с помощью операции unmount old> mount new, что означает, что каждое состояние или изменения, внесенные в старый компонент, теряются каждый раз, когда родительский элемент повторный рендеринг компонента.

Но почему рендер-реквизит избегает размонтирования и монтирования? Это тоже анонимная функция !?

Здесь я хотел бы сослаться на код, который разместил @Rishat Muhametshin, основную часть метода рендеринга компонента Route:

if (component)
  // We already know the differences:
  // React.createElement(component)
  // React.createElement(() => <component/>)
  return match ? React.createElement(component, props) : null

if (render)
  return match ? render(props) : null

render prop - это функция, которая при вызове возвращает ReactElement, какого типа этот возвращаемый элемент?

<Route render={() => <AppComponent />}></Route>

Это AppComponent, а не анонимный упаковщик функций! Потому что после компиляции jsx:

render = () => React.createElement(AppComponent)
render() = React.createElement(AppComponent)

React.createElement(render) =
  React.createElement(() => React.createElement(AppComponent))

React.createElement(render()) =
  React.createElement(React.createElement(AppComponent))

Поэтому, когда вы используете render вместо компонента prop, тип элемента, который render prop возвращает функцию, не будет меняться при каждом рендеринге, даже если для каждого parentElement.render всегда создается новый экземпляр анонимной функции ( )

С моей точки зрения, вы можете добиться того же поведения, что и рендеринг prop с компонентом prop, присвоив имя анонимной функции:

// Put this line outside render method.
const CreateAppComponent = () => <AppComponent />

// Inside render method
render(){
  return <Route component={CreateAppComponent}/>
}

Таким образом, вывод заключается в том, что между компонентом и реквизитом рендеринга нет разницы в производительности, если вы используете компонент = {AppComponent} напрямую, если вы хотите назначить некоторые реквизиты для AppComponent, используйте render={() => <AppComponent {...props}/> } вместо component={() => <AppComponent {...props}/> }

Ответ 3

Большинство понятий были объяснены другими ответами. Позвольте мне разобраться в следующем:

Прежде всего, у нас есть исходный код:

if (component)
  return match ? React.createElement(component, props) : null

if (render)
  return match ? render(props) : null

case # 1: компонент без функции

<Route path="/create" component={CreatePage} />

React.createElement(CreatePage, props) вызывается из-за React.createElement(component, props) из исходного кода. Инстанцирование может привести к перемонтированию.

дело № 2: визуализация без функции

<Route path="/create" render={CreatePage} />

React.createElement(CreatePage, props) вызывался перед переходом в команду render, а затем вызывался render(props) из исходного кода. Нет экземпляров, нет перемонтирования.

дело № 3: компонент с функцией

<Route path="/create" component={ () => <CreatePage /> } />

React.createElement(CreatePage, props) вызываться дважды. Сначала для анализа jsx (анонимная функция), сначала для возврата экземпляра CreatePage из анонимной функции, затем из исходного кода. Так почему бы не сделать это в компоненте prop.

oligofren указывает на ошибки:

Разбор JSX не вызывает это. Это просто заканчивается созданием выражения функции. Причина, по которой вы не хотите делать # 3, заключается в том, что вы каждый раз создаете новый анонимный тип, вызывая перемонтирование DOM.

случай № 4: визуализация с функцией

<Route path="/create" render={ () => <CreatePage /> } />

Каждый раз при маршрутизации на path=/create создается экземпляр (синтаксический анализ jsx). Это похоже на case # 1?

Заключение

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

<Route path="/abc" render={()=><TestWidget num="2" someProp={100}/>}/>

Это немного далеко от темы, поэтому я оставляю официальное обсуждение для дальнейшего чтения.

Ответ 4

Даже если мы не передадим какие-либо реквизиты в ComponentToRender, я обнаружил некоторые преимущества использования рендера вместо компонента. По умолчанию <Route \> передает дополнительные реквизиты ({ history, location, match }) в ComponentToRender при использовании компонента. Мы можем получить доступ к этим реквизитам также через функцию обратного вызова рендеринга, но также мы можем опустить ее. Зачем нам это нужно? Каждый рендер <Route/>'s родителя или любой навигации (даже если изменение маршрута в такой же, как и раньше) создать новый match объект. Поэтому, когда мы передаем его нашему ComponentToRender, мы каждый раз получаем новые реквизиты, что может вызвать некоторые проблемы с производительностью, особенно с PureComponent.