React.js关于从组件中侦听窗口事件的最佳实践

Joe*_*e C 35 javascript reactjs

我根据它们在视口中的位置设置了几个React.js组件的动画.如果组件位于视口中,则将不透明度设置为1,如果它不在视口中,则将其不透明度设置为0.我使用getBoundingClient()'s topbottom属性来确定组件是否在视口内.

ComponentA显示了其他B,C和D组件所遵循的模式.他们每个人都在听window滚动事件.

这是"React"方法,每个组件必须添加一个事件监听器window吗?同一窗口上有多个滚动事件侦听器?

或者有一种更好的方法是在Home所有者组件中将滚动事件监听器添加到窗口一次?然后ownee子组件仍然能够使用getBoundingClient()?知道它们在DOM中的位置?

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 */);
 }

});
Run Code Online (Sandbox Code Playgroud)

sar*_*ink 34

有几种不同的方法可以做到这一点.一个是通过组成:

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's 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's handleScroll");
    },

    render: function() {
        return (
            <ScrollWrapper onWindowScroll={this.handleScroll}>
                <div>whatever</div>
            </ScrollWrapper>
        );
    }
});
Run Code Online (Sandbox Code Playgroud)

现在,您可以将通用逻辑放在ScrollWrapper组件中,然后突然变得可重用.你可以创建一个像ComponentB渲染ScrollWrapper一样的东西ComponentA.

为了满足你的例子,也许你必须传递ScrollWrapper一些额外的道具ComponentA.也许你会传给它一个包含ref调用逻辑的实例的道具.您甚至可以传递一些选项或参数来自定义补间或边界.我没有编写任何代码,因为我认为你会理解它,并且能够使用我提供的基础为你自己定制/编写它.

实现这种事情的另一种方法是通过Mixin.尽管如此,有很多关于Mixins是好还是坏的讨论,他们甚至可能在未来被React弃用?你可以阅读一下这个,然后自己决定你的想法.


mty*_*son 19

这是带有 useEffect 钩子的函数式风格:

  useEffect(() => {
    const onScroll = (event) => console.info("scrolling", event);
      
    window.addEventListener('scroll', onScroll);
    
    return () => {
      window.removeEventListener('scroll', onScroll);
    }
  }, []);
Run Code Online (Sandbox Code Playgroud)

  • 这正是我一直在寻找的。 (2认同)

小智 16

这是一个更简单的代码片段,应该按要求工作.您缺少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>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

另一个选项是下面,两个选项应该工作:)

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>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 不要这样做.在生命周期方法中绑定时,您正在创建给定实例方法的新实例,因此删除事件侦听器将不起作用.绑定构造函数中的实例方法,而不是生命周期方法.此外,在接受的答案中遵循组合方法. (9认同)