Подтвердить что ты не робот

Рекомендация 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 */);
 }

});
4b9b3361

Ответ 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 действительным.