У меня проблема с мигающим изображением, потому что оно отображается без какой-либо причины, несмотря на использование React.memo и несмотря на то, что его реквизиты или состояние изменяются.

Мне удалось здесь чтобы правильно использовать React.memo для этой работы, BUUUT, по причине, которую я не понимаю, если я использую Компонент Высокого Порядка в компоненте Parent, memo больше не работает, и я получаю моя мигающая проблема снова

Вот закуска, иллюстрирующая проблему .

Вот код:

import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';

let interval = null

const Icon = ({ name }) => {
  // We emulate a rerender of the Icon by logging 'rerender' in the console
  console.log('rerender')
  return <Text>{name}</Text>
}

const Memo = React.memo(Icon, () => true)

const withHOC = (Comp) => (props) => {
  return <Comp {...props}/>
}

export default function App() {
  const [state, setState] = React.useState(0)
  const name = 'constant'
  // Change the state every second
  React.useEffect(() => {
    interval = setInterval(() => setState(s => s+1), 1000)
    return () => clearInterval(interval)
  }, [])
  // Remove this line and replace NewView by View to see the expected behaviour
  const NewView = withHOC(View)
  return (
    <NewView>
      <Memo name={name} />
    </NewView>
  );
}

Я не понимаю, почему мой HOC нарушает запоминание, и я понятия не имею, как предотвратить мигание в моем приложении и все еще иметь возможность использовать HOC ...

0
Sharcoux 7 Май 2020 в 00:32

2 ответа

Вы воссоздаете HOC в вашей функции рендеринга. Из-за этого React не может поддерживать ни одного из дочерних элементов этого компонента между рендерами.

Если вы переместите создание HOC за пределы рендера, это сработает!

const Text = 'span';
const View = 'div';

let interval = null

const Icon = ({ name }) => {
  // We emulate a rerender of the Icon by logging 'rerender' in the console
  console.log('rerender')
  return <Text>{name}</Text>
}

const Memo = React.memo(Icon, () => true)

const withHOC = (Comp) => (props) => {
  return <Comp {...props}/>
}

// move it out here!
// 👇👇👇
const NewView = withHOC(View)
// 👆👆👆

function App() {
  const [state, setState] = React.useState(0)
  const name = 'constant'
  // Change the state every second
  React.useEffect(() => {
    interval = setInterval(() => setState(s => s+1), 1000)
    return () => clearInterval(interval)
  }, [])
  // Remove this line and replace NewView by View to see the expected behaviour
  
  return (
    <NewView>
      <Memo name={name} />
    </NewView>
  );
}
ReactDOM.render(<App />, document.querySelector('#root'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
2
Rico Kahler 6 Май 2020 в 22:04

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

Если вы переместите const NewView = withHOC(View) за пределы функции App, ваш HOC будет вызван один раз, создав NewView, который будет использоваться при каждом повторном рендеринге, и это предотвратит ваш { {X4}} от уничтожения, и, поскольку вы запомнили это, вы защищены от ненужных повторных визуализаций.

import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';

let interval = null

const Icon = ({ name }) => {
  // We emulate a rerender of the Icon by logging 'rerender' in the console
  console.log('rerender')
  return <Text>{name}</Text>
}

const Memo = React.memo(Icon, () => true)

const withHOC = (Comp) => (props) => {
  return <Comp {...props}/>
}

const NewView = withHOC(View);

export default function App() {
  const [state, setState] = React.useState(0)
  const name = 'constant'
  // Change the state every second
  React.useEffect(() => {
    interval = setInterval(() => setState(s => s+1), 1000)
    return () => clearInterval(interval)
  }, [])
  // Remove this line and replace NewView by View to see the expected behaviour
  return (
    <NewView>
      <Memo name={name} />
    </NewView>
  );
}

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

0
Michalis Garganourakis 6 Май 2020 в 22:22