Создание новых типов компонентов в перехватчиках реакции, как сохранить производительность? - PullRequest
2 голосов
/ 08 мая 2020

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

//pseudocode
const [animatedValue, animationRef, ...otherUsefuLStuff] = useMyGenericAnimationHook(...some animation config)

return (
    <Animated.View style={{transform: [{translateY: animatedValue}]}}>
        .....
    </Animated.View>
)

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

//pseudocode
const [AnimatedTranslateY, animatedValue, animationRef, ...otherUsefulStuff] = useMyGenericAnimationHook(... some animation config)

return (
    <AnimatedTranslateY>.....</AnimatedTranslateY>
)

Это выглядит чище, но возврат нового типа компонента из хука определенно вызывает проблему . React будет разбирать и перестраивать дочерние элементы на каждом рендере, потому что каждый раз, когда запускается useMyAnimationHook (), он будет возвращать новый тип компонента!

//pseudocode
const useMyGenericAnimationHook= (....configuration arguments) => {
    const animatedValueRef = ....create the animated value(s) ref(s)

    const WrapperView: React.FC<ViewProps> = useMemo(
        () =>
            React.memo(({ children, style, ...restProps }) => (
                <Animated.View {...restProps} 
                    style={[style, { transform: ...apply animated value(s)}]}>
                    {children}
                </Animated.View>
            )),
        [animatedValueRef.current]
    )

    return [WrapperView, animationHandle, otherUsefulStuff]
}

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

Какова будет реакция при использовании <WrapperView>?

Есть ли причина, по которой это не лучший шаблон?

Спасибо за понимание!

1 Ответ

0 голосов
/ 08 мая 2020

Вот еще одна идея. Я собираюсь немного подправить ваш API-интерфейс ловушки, чтобы вместо возврата новых компонентов (которые просто добавляют свойства к Animated.View компоненту) вы возвращали набор функций, которые возвращают свойства оболочки. Затем вы вызываете нужные функции и объединяете результаты с опорой стиля компонента. Пример:

function useMyGenericAnimationHook(...animationConfig) {
  let animatedValue, animationRef, otherUsefulStuff;
  return {
    animatedValue,
    animationRef,
    ...otherUsefulStuff,
    // here's the meat of it
    translateY: () => ({
      transform: [{translateY: animatedValue}]
    }),
    color: () => ({
      color: `hsl(120,100%,${animatedValue}%)`
    }),
  };
}

function Component() {
  let { translateY, color } = useMyGenericAnimationHook(...animationConfig);
  return (
    <Animated.View style={{ ...translateY(), ...color() }}>
      {children}
    </Animated.View>
  );
}

Старый ответ

Мне нравится этот шаблон - он выглядит очень чистым. Пока вы убедитесь, что он не генерирует новый тип компонента при каждом рендеринге, он должен быть эффективным, и useMemo должно работать для этого.

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

//pseudocode
const WrapperView = React.memo(({ children, style, ...restProps }: ViewProps) => (
  <Animated.View {...restProps} 
                 style={[style, { transform: ...apply animated value(s)}]}>
                 {children}
  </Animated.View>
);

const useMyGenericAnimationHook= (....configuration arguments) => {
    const animatedValueRef = ....create the animated value(s) ref(s)

    return [WrapperView, animationHandle, otherUsefulStuff]
}

Третьим, но более громоздким вариантом будет возврат JSX из обработчика.

function useDiv() {
  return <div />;
}
function Component(props) {
  const div = useDiv();
  return (
    <main>{div}</main> // equivalent to <main><div/></main>
  )
}

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

...