Рекомендация React.js относительно прослушивания событий окна из компонентов
Я оживляю несколько компонентов React.js на основе их положения в окне просмотра. Если компонент находится в области просмотра, анимируйте непрозрачность до 1, если она не находится в окне просмотра, анимируйте ее непрозрачность до 0. Я использую свойства getBoundingClient()
top
и bottom
, чтобы определить, находится ли компонент в области просмотра.
ComponentA показывает шаблон, который я использовал для других компонентов B, C и D. Каждый из них прослушивает событие прокрутки window
.
Является ли это способом "Реакт" для этого каждым компонентом, имеющим необходимость добавить прослушиватель событий в window
? Несколько прослушивателей событий прокрутки в одном окне?
Или есть ли лучший способ, добавив прослушиватель событий прокрутки к окну один раз в компоненте владельца Home
? Затем дочерние компоненты-пользователи еще смогут узнать, где они находятся в DOM, используя getBoundingClient()
?
Home = React.createClass({
render: function() {
<div>
<ComponentA />
<ComponentB />
<ComponentC />
<ComponentD />
</div>
};
});
ComponentA = React.createClass({
componentDidMount: function() {
window.addEventListener('scroll', this.handleScroll);
},
componentWillUnmount: function() {
window.removeEventListener('scroll', this.handleScroll);
},
handleScroll: function() {
var domElement = this.refs.domElement.getDOMNode();
this.inViewPort(domElement);
},
inViewPort: function(element) {
var elementBounds = element.getBoundingClientRect();
(elementBounds.top <= 769 && elementBounds.bottom >= 430) ? TweenMax.to(element, 1.5, { opacity: 1 }) : TweenMax.to(element, 1.5, { opacity: 0 });
},
render: function() {
return (/* html to render */);
}
});
Ответы
Ответ 1
Есть несколько способов сделать это. Один из них состоит из композиции:
var React = require("react");
var _ = require("underscore");
var ScrollWrapper = React.createClass({
propTypes: {
onWindowScroll: React.PropTypes.func
},
handleScroll: function(event) {
// Do something generic, if you have to
console.log("ScrollWrapper handleScroll");
// Call the passed-in prop
if (this.props.onWindowScroll) this.props.onWindowScroll(event);
},
render: function () {
return this.props.children;
},
componentDidMount: function() {
if (this.props.onWindowScroll) window.addEventListener("scroll", this.handleScroll);
},
componentWillUnmount: function() {
if (this.props.onWindowScroll) window.removeEventListener("scroll", this.handleScroll);
}
});
var ComponentA = React.createClass({
handleScroll: function(event) {
console.log("ComponentA handleScroll");
},
render: function() {
return (
<ScrollWrapper onWindowScroll={this.handleScroll}>
<div>whatever</div>
</ScrollWrapper>
);
}
});
Теперь вы можете разместить свою общую логику в компоненте ScrollWrapper
, и вдруг она станет многоразовой. Вы можете создать ComponentB
, который отображает ScrollWrapper
так же, как ComponentA
.
Чтобы удовлетворить ваш пример, возможно, вам придется передать ScrollWrapper
несколько дополнительных реквизитов из ComponentA
. Возможно, вы передадите ему опору, которая содержит экземпляр ref
для вызова вашей логики. Вы даже можете передать ему некоторые параметры или аргументы, чтобы настроить анимацию или рамки. Я не кодировал ничего из этого, потому что я думаю, вы поймете это и сможете настроить/написать его для себя с базой, которую я предоставил.
Другим способом достижения такого рода является использование Mixin. Хотя, есть много разговоров о том, хороши или плохи ли Миксины, и в будущем они могут быть отвергнуты Реактивом? Вы можете прочитать об этом и сами решить, что думаете.
Ответ 2
Вот более простой фрагмент кода, который должен работать по мере необходимости. Вам не хватает привязки this
, как таковая, когда вы выполняете window.addEventListener('scroll', this.handleScroll);
, вы на самом деле указываете this
на объект окна.
Вместо этого вам нужно будет связать это в конструкторе. Надеюсь, что он
class Home extends Component {
constructor(props) {
super(props)
this.handleScroll = this.handleScroll.bind(this);
}
componentDidMount() {
window.addEventListener('scroll', this.handleScroll);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll);
}
handleScroll(e) {
console.log('scroll event');
console.log(e);
}
render() {
return (
<div>
<ComponentA />
<ComponentB />
<ComponentC />
<ComponentD />
</div>
);
}
}
Другим вариантом является нижеследующее: оба варианта должны работать:)
class Home extends Component {
componentDidMount() {
window.addEventListener('scroll', this.handleScroll.bind(this));
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll.bind(this));
}
handleScroll(e) {
console.log('scroll event');
console.log(e);
}
render() {
return (
<div>
<ComponentA />
<ComponentB />
<ComponentC />
<ComponentD />
</div>
);
}
}
Ответ 3
Я бы определенно добавил один eventener/компонент. Идеология состоит в том, чтобы иметь отделенные компоненты, которые могут быть повторно использованы и помещены "где угодно" в приложение - чтобы минимизировать избыточность кода.
Таким образом, ваш подход к тому, чтобы держать список событий за compnenent действительным.