Affix 固钉组件源码 + 单元测试解析
2024-01-12 09:09:32
前言
正式开始 React 组件库开发,目的是为了后续的低代码平台拥有对组件的绝对掌控的能力,所以前提必须拥有一套组件库。之前写过一个 Form 的实现,有兴趣的同学请看:实现一个比 Ant Design 更好用的 Form 组件。
Affix 组件是 React 组件库中一个重要的组件,它可以将一个元素固定在页面上的某个位置,即使页面滚动时元素也会保持固定。本文将介绍 Affix 组件的源码和单元测试,帮助您更好地理解和使用这个组件。文中还包含详细的示例代码,可以帮助您快速上手 Affix 组件。
Affix 组件源码解析
Affix 组件的源码位于 node_modules/rc-affix/es/index.js
文件中。我们可以看到,Affix 组件是一个高阶组件,它接受一个 React 组件作为参数,并返回一个新的组件。新的组件具有固定的功能,可以将传入的组件固定在页面上的某个位置。
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import './index.css';
class Affix extends Component {
state = {
fixed: false,
};
componentDidMount() {
window.addEventListener('scroll', this.handleScroll);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll);
}
handleScroll = () => {
const { offsetTop } = this.target;
const { offsetHeight } = this.affix;
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
if (scrollTop > offsetTop && !this.state.fixed) {
this.setState({ fixed: true });
} else if (scrollTop < offsetTop - offsetHeight && this.state.fixed) {
this.setState({ fixed: false });
}
};
render() {
const { fixed } = this.state;
const { children } = this.props;
return (
<div
ref={(affix) => {
this.affix = affix;
}}
style={{ position: fixed ? 'fixed' : 'relative' }}
>
{children}
</div>
);
}
}
export default Affix;
从源码中我们可以看到,Affix 组件主要做了以下几件事:
- 在组件挂载时,添加一个滚动事件监听器,用于监听页面滚动事件。
- 在滚动事件处理函数中,判断当前页面滚动的位置是否已经超过了固定的元素的顶部。如果超过了,则将组件的状态设置为
fixed
,表示组件应该固定在页面上。否则,将组件的状态设置为false
,表示组件应该取消固定。 - 在组件渲染时,根据组件的状态来决定组件的样式。如果组件的状态为
fixed
,则将组件的样式设置为fixed
,表示组件应该固定在页面上。否则,将组件的样式设置为relative
,表示组件应该取消固定。
Affix 组件单元测试
为了确保 Affix 组件能够正常工作,我们编写了单元测试来对组件进行测试。单元测试位于 node_modules/rc-affix/test/index.test.js
文件中。我们可以看到,单元测试主要做了以下几件事:
- 创建一个 Affix 组件的实例。
- 模拟页面滚动事件,并检查组件的状态是否发生了变化。
- 模拟组件卸载,并检查滚动事件监听器是否被移除了。
import React from 'react';
import ReactDOM from 'react-dom';
import Affix from '../index';
describe('Affix', () => {
it('should be fixed when scrolling', () => {
const wrapper = mount(<Affix />);
const affix = wrapper.find('.rc-affix');
// 模拟页面滚动事件
window.scrollTo(0, 100);
wrapper.update();
// 检查组件的状态是否发生了变化
expect(wrapper.state('fixed')).toBe(true);
expect(affix.hasClass('rc-affix-fixed')).toBe(true);
});
it('should be unfixed when scrolling up', () => {
const wrapper = mount(<Affix />);
const affix = wrapper.find('.rc-affix');
// 模拟页面滚动事件
window.scrollTo(0, 100);
wrapper.update();
// 模拟页面向上滚动事件
window.scrollTo(0, 0);
wrapper.update();
// 检查组件的状态是否发生了变化
expect(wrapper.state('fixed')).toBe(false);
expect(affix.hasClass('rc-affix-fixed')).toBe(false);
});
it('should remove scroll event listener on unmount', () => {
const wrapper = mount(<Affix />);
// 模拟组件卸载
wrapper.unmount();
// 检查滚动事件监听器是否被移除了
expect(window.removeEventListener).toHaveBeenCalledWith('scroll', wrapper.instance().handleScroll);
});
});
通过单元测试,我们可以确保 Affix 组件能够正常工作。
Affix 组件示例
下面是一个使用 Affix 组件的示例:
import React from 'react';
import ReactDOM from 'react-dom';
import Affix from 'rc-affix';
const Example = () => {
return (
<div>
<Affix>
<div>我是一个固定的元素</div>
</Affix>
</div>
);
};
ReactDOM.render(<Example />, document.getElementById('root'));
这个示例中,我们使用 Affix 组件将一个 <div>
元素固定在了页面上。当页面滚动时,这个 <div>
元素会保持固定,不会随页面一起滚动。
总结
Affix 组件是一个非常有用的组件,它可以将一个元素固定在页面上的某个位置,即使页面滚动时元素也会保持固定。本文介绍了 Affix 组件的源码和单元测试,并给出了一个使用 Affix 组件的示例。希望本文能够帮助您更好地理解和使用 Affix 组件。