Реагировать на повторную визуализацию дочерних элементов в функциональном компоненте, несмотря на использование памятки и отсутствие каких-либо изменений свойств - PullRequest
1 голос
/ 06 мая 2020

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

Вот закуска , который показывает проблему.

Мы эмулируем родительские изменения с помощью setInterval .

Мы эмулируем повторную визуализацию значка, регистрируя 'rerender' в консоли.

Вот код:

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


// or any pure javascript modules available in npm
let interval = null

const Child = ({name}) => {
  //Why would this child still rerender, and how to prevent it?
  console.log('rerender')
  return <Text>{name}</Text>
}
const ChildContainer = ({name}) => {
  const Memo = React.memo(Child, () => true)
  return <Memo name={name}/>
}

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)
  }, [])
  return (
    <View>
      <ChildContainer name={name} />
    </View>
  );
}

Если бы вы могли объяснить мне, почему это происходит и как это исправить, это было бы здорово!

1 Ответ

4 голосов
/ 06 мая 2020

Если вы переместите const Memo = React.memo(Child, () => true) за пределы ChildContainer, ваш код будет работать должным образом.

Хотя ChildContainer не является мемоизированным компонентом, он будет повторно отрисован и создаст мемоизированный Child компонент при каждом повторном рендеринге родителя.

Перемещая мемоизацию за пределы ChildContainer, вы безопасно запоминаете свой компонент Child один раз, и независимо от того, сколько раз будет вызываться ChildContainer, Child будет выполняться только один раз.

Вот рабочая демка . Я также добавил журнал на App для отслеживания каждого повторного рендеринга и один журнал в ChildComponent, чтобы вы могли видеть, что эта функция вызывается при каждом повторном рендеринге, не касаясь больше Child.

Вы также можете заключить Child в React.memo напрямую:

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

// or any pure javascript modules available in npm
let interval = null;

const Child = React.memo(({ name }) => {
  //Why would this child still rerender, and how to prevent it?
  console.log("memoized component rerender");
  return <Text>{name}</Text>;
}, () => true);

const ChildContainer = ({ name }) => {
  console.log("ChildContainer component rerender");
  return <Child name={name} />;
};

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);
  }, []);

  console.log("App rerender");
  return (
    <View>
      <ChildContainer name={name} />
    </View>
  );
}

...