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

IT技术 javascript reactjs
2021-04-01 08:30:57

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

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

这是通过每个组件必须向window. 同一窗口上的多个滚动事件侦听器?

或者有没有更好的方法通过在Home所有者组件处将滚动事件侦听器添加到窗口一次那么拥有者的子组件是否仍然能够使用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 */);
 }

});
4个回答

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

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

现在,您可以将通用逻辑放在ScrollWrapper组件中,然后它突然变得可以重用了。你可以创建一个ComponentB渲染 aScrollWrapper就像ComponentA做的一样。

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

实现这类事情的另一种方法是通过 Mixin。虽然,有很多关于 Mixin 是好是坏的讨论,它们甚至可能在未来被 React 弃用?您可以阅读有关此内容并自行决定您的想法。

用于单向流动,分离副作用/关注点,减少冗余,更多重用
2021-06-01 08:30:57

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

  useEffect(() => {
    const onScroll = (event) => console.info("scrolling", event);
      
    window.addEventListener('scroll', onScroll);
    
    return () => {
      window.removeEventListener('scroll', onScroll);
    }
  }, []);
@SoldeplataSaketos 随意编辑代码!
2021-05-25 08:30:57
这正是我正在寻找的。
2021-05-30 08:30:57
onScroll 应该是一个常量,并在 useEffect 中声明
2021-06-03 08:30:57

这是一个更简单的代码片段,应该可以按要求工作。您缺少this绑定,因此,当您执行时,window.addEventListener('scroll', this.handleScroll);您实际上是在指向thiswindow 对象。

相反,您需要在构造函数中绑定 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>
    );
  }
}
这是否意味着我们为 ComponentsA、B 等的滚动事件获得了 4 个 eventHandlers?
2021-05-25 08:30:57
没有@AksanaTolstoguzova 滚动事件只有 1 个事件处理程序
2021-06-03 08:30:57
不要这样做。当您在生命周期方法中绑定时,您正在创建给定实例方法的新实例,因此删除事件侦听器将不起作用。在构造函数中绑定实例方法,而不是生命周期方法。此外,请遵循已接受答案中的组合方法。
2021-06-10 08:30:57
这对我今天不起作用。有没有改变?
2021-06-11 08:30:57

我肯定会添加一个事件侦听器/组件。其理念是拥有可以重复使用的分离组件,并可以将其放置在应用程序的“任何地方” - 以最大限度地减少代码冗余。

因此,您为每个组件保留一个事件监听器的方法是有效的。