Я пытаюсь построить HO C, который бы анимировал детей с заданным набором типов анимации. Все работает хорошо, за исключением случая, когда useTransition
применяется к массиву item
s, которые являются material-ui
Accordion
s.
HO C обрабатывает как логические, так и ключевые элементы на основе массива, направляя их в useSpring
или useTransition
соответственно.
Проблема: Когда useTransition
применяет стиль высоты к анимации, эта высота не обновляется при открытии Accordion
и закрывается. Интересно, что если что-то еще вызывает повторную визуализацию, она с опозданием анимируется должным образом.
Нужно ли принудительно повторять визуализацию? Если да, то где мне разместить этот код?
Animate. js (HO C)
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { animated, useSpring, useTransition } from 'react-spring';
import { animationStyles } from 'data';
import styles from './Animate.module.scss';
const types = {
TOP: {
from: animationStyles.enterTop.from,
enter: animationStyles.enterTop.enter,
leave: animationStyles.leaveTop.leave,
},
LEFT: {
from: animationStyles.enterLeft.from,
enter: animationStyles.enterLeft.enter,
leave: animationStyles.leaveRight.leave,
},
FADE: {
from: animationStyles.enterFade.from,
enter: animationStyles.enterFade.enter,
leave: animationStyles.leaveFade.leave,
},
HEIGHT: {
from: animationStyles.enterHeight.from,
enter: animationStyles.enterHeight.enter,
leave: animationStyles.enterHeight.leave,
},
};
export const Animate = ({
children,
type = 'TOP',
omit = [],
condition,
idKey,
render,
props,
style,
}) => {
const uType = type.toUpperCase();
const animateHeight = omit?.map(v => v.toUpperCase()).indexOf('HEIGHT') === -1;
const isList = Array.isArray(condition);
const [refMap] = useState(new WeakMap());
const [show, set] = useState(false);
useEffect(() => {
if (isList) return;
set(condition ?? true);
}, [show, condition, isList]);
const heightProps = item => {
const ref = refMap.get(item);
if (!ref) return {};
const height = ref.offsetHeight;
return {
from: animateHeight ? { height: 0 } : {},
enter: animateHeight ? { height } : {},
update: animateHeight ? { height } : {},
leave: animateHeight ? { height: 0 } : {},
};
};
const from = { ...types[uType].from, ...props?.from };
const enter = { ...types[uType].enter, ...props?.to };
const to = show ? enter : from;
const springProps = useSpring({ from, to });
const transitions = useTransition(condition, item => item[idKey], {
from: item => ({ ...from, ...heightProps(item).from, overflow: 'hidden' }),
update: item => async next => {
const props = { ...enter, ...heightProps(item).update, overflow: 'visible' };
return await next(props);
},
leave: item => async next => {
const props = { ...from, ...heightProps(item).leave, overflow: 'hidden' };
return await next(props);
},
unique: true,
});
return isList ? (
transitions.map(({ item, props, key }) => (
<animated.div key={key} style={props} className={classnames(styles.root, style)}>
<div ref={ref => ref && refMap.set(item, ref)}>{render(item)}</div>
</animated.div>
))
) : (
<animated.div style={springProps} className={classnames(styles.root, style)}>
<div>{children}</div>
</animated.div>
);
};
Animate.propTypes = {
children: PropTypes.node,
render: PropTypes.func,
omit: PropTypes.array,
type: PropTypes.string,
condition: PropTypes.oneOfType([PropTypes.bool, PropTypes.array]),
props: PropTypes.object,
idKey: PropTypes.string,
style: PropTypes.string,
expanded: PropTypes.bool,
};
export default Animate;
Пример использования.
const renderItem = subId => (
<Accordion
key={subId}
expanded={expanded === subId}
onChange={onChange(subId)}
>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<InfoTitles titles={titles} style={styles.infoTitles} />
{action && (
<AppButton onClick={action.func} />
)}
</AccordionSummary>
<AccordionDetails>{children}</AccordionDetails>
</Accordion>
);
return <Animate
style={styles.animateRoot}
condition={Object.keys(itemList).map(v => ({ subId: v }))}
idKey="subId"
type="left"
render={item => renderItem(item.subId)}
/>