如何把组件改写为 react 16 形式

一个可展开和收缩的 react 组件,组件内部有一个按钮切换收缩和展开,并在组件内部支持下拉收缩,上拉展开,并发在变化时抛出 onChange 事件

如何将该组件改写成 React16.4 推荐的格式

import React from 'react';
import styles from './style.scss';

class ExpandedView extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      expanded: false,
    };

    // 触摸检测
    this.containerNode = null;
    this.startX = 0;
    this.startY = 0;
    this.endX = 0;
    this.endY = 0;

    this.clickArrow = this.clickArrow.bind(this);
    this.onContainerTouchStart = this.onContainerTouchStart.bind(this);
    this.onContainerTouchEnd = this.onContainerTouchEnd.bind(this);
  }

  componentWillReceiveProps(nextProps) {
    const { expanded: newExpanded, onChange } = nextProps;

    if (this.state.expanded !== newExpanded) {
      onChange && onChange(newExpanded);
      this.setState({ expanded: newExpanded });
    }

  }

  async componentDidMount() {
    let { expanded, onChange } = this.props;

    onChange && onChange(expanded);
    this.setState({ expanded });

    if (this.containerNode) {
      this.containerNode.addEventListener('touchstart', this.onContainerTouchStart);
      this.containerNode.addEventListener('touchend', this.onContainerTouchEnd);
    }
  }

  componentWillUnmount() {
    if (this.containerNode) {
      this.containerNode.removeEventListener('touchstart', this.onContainerTouchStart);
      this.containerNode.removeEventListener('touchend', this.onContainerTouchEnd);
    }
  }

  onContainerTouchStart(e) {
    this.startX = e.touches[0].pageX;
    this.startY = e.touches[0].pageY;
  }

  onContainerTouchEnd(e) {

    let { onChange } = this.props;

    this.endX = e.changedTouches[0].pageX;
    this.endY = e.changedTouches[0].pageY;
    const direction = getDirection(this.startX, this.startY, this.endX, this.endY, 30);

    // 向上滑打开
    if (direction === 'up') {
      this.setState({ expanded: true });
      onChange && onChange(true);

      // 向下滑关闭
    } else if (direction === 'down') {
      this.setState({ expanded: false });
      onChange && onChange(false);
    }
  }

  // 点击箭头
  clickArrow() {
    let { onChange } = this.props;

    this.setState((prevState) => {
      let expanded = !prevState.expanded;

      onChange && onChange(expanded);

      return { expanded };
    });
  }

  render() {
    const { expanded } = this.state;

    return (
      <div
        ref={node => this.containerNode = node}
        className={cn(styles.container, { [styles.collapsed]: !expanded })}
      >
        <div className={styles['arrow-wrapper']} onClick={() => this.clickArrow()} />
        <div className={cn(styles['collapsed-panel'], { [styles.hide]: expanded })}>
          <div>collapsed</div>
        </div>
        <div className={cn(styles['expanded-panel'], { [styles.hide]: !expanded })}>
          <div>expanded</div>
        </div>
      </div>
    );
  }
}

export default ExpandedView;

我改写成


  static getDerivedStateFromProps(nextProps, prevState) {
    const { expanded: currentExpanded, onChange } = nextProps;

    if (currentExpanded !== prevState.expanded) {
      onChange && onChange(currentExpanded);

      return {
        expanded: currentExpanded,
      };
    }

    return null;
  }

后发现,clickArrow 的 setState 也会调用到 getDerivedStateFromProps ,导致无法切换收缩与展开,我应该如何解决该问题?
像类似这种组件控制自身状态的行为,是应该在组件内部执行,还是仅仅抛出事件,让外部组件通过传递 props 决定 expanded 的值

阅读 1.1k
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题