#javascript #reactjs
#javascript #reactjs
Вопрос:
У меня есть элемент tab, куда переходит вкладка — это активная вкладка, определяемая реквизитами, там есть подчеркивание.
Он оформлен значением состояния underlineStyle
.
При начальной загрузке состояние для sizes
не обновилось к моменту его достижения, getUnderlineStyle
которое вызывается впоследствии?
Причина, по которой я не делаю этого с помощью CSS, заключается в том, что есть переход, который требует некоторых вычислений в getSizes()
, поэтому он имеет правильные значения свойств left и right из объекта, на который sizes
вы можете видеть ссылки.
Как я могу вызвать, getUnderlineStyle()
чтобы применить правильный стиль после того, как sizes
состояние было обновлено?
Ниже приведен фрагмент из моего компонента.
const Tabs = (props) => {
const [sizes, updateSizes] = useState({});
const [underlineStyle, updateUnderlinStyle] = useState({});
useEffect(() => {
if (props.underline) {
getSizes();
// State for sizes does not seem to get updated here?
getUnderlineStyle();
window.addEventListener("resize", _.debounce(() => getSizes()));
}
},
[props]
);
let els = {};
const tabsRef = useRef(null);
const transistionTime = 500;
const transistionStyle = `left ${transistionTime}ms, right ${transistionTime}ms`;
const getSizes = () => {
const rootBounds = tabsRef.current.getBoundingClientRect();
const size = {};
Object.keys(els).forEach((key) => {
const el = els[key];
const bounds = el.getBoundingClientRect();
const left = bounds.left - rootBounds.left;
const right = rootBounds.right - bounds.right;
size[key] = { left, right };
});
// This updates size state, but does not seem to carry across into getUnderlineStyle???
updateSizes(size);
}
const getUnderlineStyle = (active) => {
// the state, `size` is still set to it's default value of an empty object, hit first condition, should hit last on initial load?
if (props.active == null || Object.keys(sizes).length === 0) {
updateUnderlinStyle({ left: '0', right: '100%' });
} else if (active) {
updateUnderlinStyle({
left: sizes[active].left,
right: sizes[active].right,
transition: transistionStyle
})
} else {
updateUnderlinStyle({
left: sizes[props.active].left,
right: sizes[props.active].right,
transition: transistionStyle
});
}
}
return (
<div className="Tabs" ref={tabsRef}>
{React.Children.map(props.children, (child, i) => {
return (
<div
className={(child.key === props.active ? 'Tabs-item Tabs-itemActive' : 'Tabs-item') (i === 0 ? ' Tabs-first' : i === (props.children.length - 1) ? ' Tabs-last' : null)}
onClick={() => {
// Call onChange method in Parent Component
props.onChange(child.key);
}}
onMouseOver={() => getUnderlineStyle(child.key)}
ref={el => els[child.key] = el}
>
{child}
</div>
)
})}
{props.underline ?
<div key={"Tabs-underline"} className="Tabs-underline" style={underlineStyle}></div>
: null
}
</div>
);
};
export default Tabs;
Ответ №1:
Сначала вам нужно либо преобразовать ваш компонент в Class
, либо использовать перехватчики для реализации состояния в вашем компоненте. Поскольку Class по-прежнему более распространены, чем хуки, я соглашусь с этим для этого ответа
Вы можете добавить прослушиватель событий в компонент, над которым вы хотите запустить действие при наведении курсора мыши, и в этой функции изменить состояние вашего компонента. Тогда у вас может быть условный стиль / класс, который применяется только тогда, когда это состояние истинно. Также вам нужно убедиться, что ваша setState
функция вызывается только один раз, чтобы предотвратить множество ненужных вызовов. Простой пример:
Class Tabs extends React.Component{
constructor(props){
super(props);
this.state = {
isHovered: false
}
// Needed to keep the reference to this
this.onHover = this.onHover.bind(this);
this.onHoverEnd = this.onHoverEnd.bind(this);
}
render(){
const { isHovered } = this.state;
// Classes
// Ternary operator: acts like a If/else
const hoverClass = isHovered? "yourClass" : "";
return(
<div id="root">
<div onMouseEnter={this.onHover} onMouseLeave={this.onHoverEnd} />
<div className={hoverClass} />
</div>
)
}
onHover(){
const { isHovered } = this.state;
if(!isHovered){
this.setState({isHovered: true})
}
}
onHoverEnd(){
const { isHovered } = this.state;
if(isHovered){
this.setState({isHovered: false})
}
}
}
}
Комментарии:
1. Возможно, мне придется пойти по пути компонента на основе классов, просто мы хотели использовать в нашем веб-приложении, где это возможно, функцию на основе.
2. @mcclosa тогда вы могли бы использовать хуки, которые в основном делают то же самое, но сохраняют функциональность компонента. Вот документация: reactjs.org/docs/hooks-intro.html
3. @Fredric Caplette Разве это не то, что я использую выше?
4. @mcclosa Ах, умница! Да, вы правы. Я еще не пробовал сам использовать хуки, поэтому пока ничем не могу помочь, если вы непременно хотите использовать хуки.
5. Если я все еще застряну к концу дня, я попробую метод на основе класса и приму ваш ответ.
Ответ №2:
итак, ваш вопрос вкратце
«Возможно ли динамически обновлять стиль подчеркивания вкладок, когда пользователь наводит курсор на элемент Tabs?»
ответ: «да» 🙂
в react вы можете определять «состояния» и «монтировать» / «привязывать» их для ваших конкретных нужд! я не могу сейчас написать код для мобильных устройств, но взгляните сюда: https://reactjs.org/docs/state-and-lifecycle.html
Комментарии:
1. Спасибо, вскоре после публикации я понял, что нужно использовать state. Извините, я был брошен в глубокий конец с React, поэтому медленно подбираю его. У меня все еще возникают проблемы, когда я должен вызывать функцию для обновления состояния, она зависит от другого значения состояния и, похоже, не обновилась к моменту, когда я вызвал функцию? Извините, если это действительно элементарно, но в данный момент я не могу разобраться в этом.
2. не волнуйся, чувак, нам всем нужно было с чего-то начинать: D ты пробовал обработчик событий в Mouseenter? (ссылка: reactjs.org/docs/handling-events.html )
3. Обработчик события onMouseOver — это то, где он работает, это при начальной загрузке, где стиль применяется некорректно из-за того, что состояние
sizes
не обновляется приgetUnderlineStyles
вызове вuseEffect
. Извините, если я неправильно понял, что вы имели в виду?