使用react + webpack4 + antd-mobile + scss 参见:https://github.com/yangdongMC/react-mobile.git
效果图如下(gif图被压缩,请见谅):
需求大致是这样:
- 一个导航栏可以左右(横向)滑动,
- 操作后,再点击任意一个,可以保持在可视区,
第一步好实现,一个父元素让其overflow-x:scroll,子元素(ul)长度为:导航栏个数*固定值,但是第二步在react中如何实现?
先回顾一下在jquery时代是怎么实现的:
$('.parent').animate({scrollLeft:($('.parent li.active').index()-2)*$width},500);
在真实dom操作时,找到overflow-x:scroll;的元素,因为只有它有该css样式,它的scrollLeft才会生效,不然一值是0,然后找到点击那个的index,一系列减乘,此时animate动画又秀了一波
但是,这是react,单向数据流,但是导航栏的位置的移动跟数据单项还是双向好像关系不大,又不想用jquery来实现,react事件合成SyntheticEvent貌似可以解决,先了解一下合成事件的定义、如何用?
1.React合成事件一套机制:React并不是将click事件直接绑定在dom上面,而是采用事件冒泡的形式冒泡到document上面,然后React将事件封装给正式的函数处理运行和处理。
2.如果DOM上绑定了过多的事件处理函数,整个页面响应以及内存占用可能都会受到影响。React为了避免这类DOM事件滥用,同时屏蔽底层不同浏览器之间的事件系统差异,实现了一个中间层——SyntheticEvent。
3.当用户在为onClick添加函数时,React并没有将Click时间绑定在DOM上面。
4.而是在document处监听所有支持的事件,当事件发生并冒泡至document处时,React将事件内容封装交给中间层SyntheticEvent(负责所有事件合成)
5.所以当事件触发的时候,对使用统一的分发函数dispatchEvent将指定函数执行。
class Test extends Component {
constructor() {
super(arguments);
this.onReactClick.bind(this);
}
componentDidMount() {
const parentDom = ReactDOM.findDOMNode(this);
const childrenDom = parentDom.queneSelector(".button");
childrenDom .addEventListen('click', this.onDomClick, false);
}
onDomClick() { // 事件委托
console.log('Javascript Dom click');
}
onReactClick() { // react合成事件
console.log('React click');
}
render() {
<div>
<button className="button" onClick={this.onReactClick()}>点击</button>
</div>
}
}
上述代码执行结果:
Javascript Dom click
React click
下面是导航栏实现方法:
import React from 'react';
import ReactDOM from 'react-dom';
import fetch from 'node-fetch';
import { baseUrl } from '../../utils/config';
import './index.scss';
import classnames from 'classnames';
import eventProxy from '../../utils/eventProxy';
class CateGroys extends React.Component {
constructor(props) {
super(props);
this.state = {
platform: props.platform,
categroys: [],
defaultActive: '',
currentIdx: 0,
width: document.body.clientWidth / 5
};
}
fetchCategroy = (platform) => {
fetch(`${baseUrl}/platforms/${platform}/categorys`).then(response => response.json()
).then(json => {
const { data } = json;
this.setState({
...this.state,
categroys: data.data,
defaultActive: data.data.length ? data.data[0].id : ''
})
})
}
componentDidMount() {
this.fetchCategroy(this.state.platform);
//注册滚动事件
const parent = ReactDOM.findDOMNode(this);
const $child = parent.querySelector('.categroysNav');
parent.addEventListener('click', this.parentDOMscroll, false);
}
//获取index
idx = (arr, text) => {
let i = '';
arr.map((item, index) => {
if (item.title === text) {
return i = index;
}
})
return i;
}
//父原生dom
parentDOMscroll = (event) => {
//兼容写法安卓、苹果
let path = event.path || (event.composedPath && event.composedPath());
path.map((item, index) => {
if (item['className'] === 'categroysNav') {
path[index].scrollLeft = (this.idx(this.state.categroys, event.target.innerText) - 2) * this.state.width;
}
});
}
//静态方法,获取不到this
static getDerivedStateFromProps(props, state) {
if (props.platform !== state.platform) {
//如果platform更新,那么就要对它的兄弟组件table数据请求参数进行修改
eventProxy.trigger('platform', props.platform);
//通过return将state.platform的值进行更新
return {
platform: props.platform
}
} else {
return null
}
}
//通过静态方法中修改类platform,再在下面方法中更新categroy请求
//上面的静态方法先执行
componentDidUpdate(prevProps, prevState) {
if (prevProps.platform !== this.state.platform) {
this.fetchCategroy(this.state.platform)
}
}
//categroy nav click
handlerNav = (id, index) => {
this.setState({
...this.state,
defaultActive: id,
currentIdx: index
});
//$('.rank_filtrate_classify_over').animate({scrollLeft:($('.rank_filtrate_classify_over li.active').index()-2)*$width},500);
//发布订阅事件
eventProxy.trigger('category', id)
}
render() {
return <div >
<div className="categroysNav">
<ul style={{ width: `${this.state.categroys.length * 68}px` }}>
{
this.state.categroys.length ? this.state.categroys.map((item, index) => <li onClick={() => this.handlerNav(item.id, index)} key={item.id} className={classnames({ 'active': this.state.defaultActive == item.id })}><span>{item.title}</span></li>) : <li>暂无数据</li>
}
</ul>
</div>
</div>
}
}
export default CateGroys