A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

前言在重构的路上,总能写点什么东西出来 , 这组件并不复杂,放出来的总觉得有点用处
一方面当做笔记,一方面可以给有需要的人; 有兴趣的小伙伴可以瞅瞅...

效果图




实现的功能
  • 渲染支持图片,文字,图文
  • 支持删除条目(并给予父回调)
用到技术点:
  • css module: 包括内置的继承特性,类似less的嵌套写法那种
  • 用到的react 16.6特性
    • lazy, Suspense来实现子组件的懒加载
    • memo让函数式组件有PureComponent的特性(浅比较)
  • flexbox来布局
  • 用了lodash的isEqual来深度比较对象,用于getDerivedStateFromProps(避免每次都更新state)

代码实现index.js : 组件的主入口import React, { PureComponent, lazy, Suspense } from 'react';import { Avatar, Icon, Popover } from 'antd';import style from './index.css';// lodash 深比较import isEqual from 'lodash/isEqual';// 渲染不同内容的组件const LazyComponent = lazy(() => import('./RenderContent'));export default class index extends PureComponent {  state = {    deleteBtnSpin: false,    loading: true,    list: [      {        time: '2018-11-12 15:35:15',        avatar:          'https://sx-stag.oss-cn-shenzhen.aliyuncs.com/user-avatar/3_avatar.jpg?x-oss-process=image/resize,m_fixed,w_90,h_90/quality,q_90',        nickname: '用户甲',        pos: 1,        voice:          'https://sx-stag.oss-cn-shenzhen.aliyuncs.com/user-chat/3_508340417_c84f79407f5bc16b9e7ee0373631cf35.aac',        text: '',      },      {        time: '2018-11-12 15:36:15',        avatar:          'https://sx-stag.oss-cn-shenzhen.aliyuncs.com/user-avatar/3_avatar.jpg?x-oss-process=image/resize,m_fixed,w_90,h_90/quality,q_90',        nickname: '用户甲',        pos: 1,        voice:          'https://sx-stag.oss-cn-shenzhen.aliyuncs.com/user-chat/3_508340417_c84f79407f5bc16b9e7ee0373631cf35.aac',        text: '',      },      {        time: '2018-11-12 15:37:15',        avatar:          'https://sx-stag.oss-cn-shenzhen.aliyuncs.com/user-avatar/3_avatar.jpg?x-oss-process=image/resize,m_fixed,w_90,h_90/quality,q_90',        nickname: '卡布奇诺',        pos: 2,        voice: '',        text:          '该词语多用于讽刺和揶揄调侃。也有送快递、查水电气、社区送温暖等引申说法。例如:(1)有人在网络上发表了不合乎相关法律法规或者破坏社会稳定和谐等消息而被警方捕;(2)在贴吧或论坛里拥有删帖权限的大小吧主,检查贴吧里是否存在灌水的帖子或跟帖,遇到就进行删除的行为。',      },      {        time: '2018-11-12 15:38:15',        avatar:          'https://sx-stag.oss-cn-shenzhen.aliyuncs.com/user-avatar/3_avatar.jpg?x-oss-process=image/resize,m_fixed,w_90,h_90/quality,q_90',        nickname: '卡布奇诺',        pos: 2,        voice: '',        img:          'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3040115650,4147729993&fm=26&gp=0.jpg',        text:          '该词语多用于讽刺和揶揄调侃。也有送快递、查水电气、社区送温暖等引申说法。例如:(1)有人在网络上发表了不合乎相关法律法规或者破坏社会稳定和谐等消息而被警方捕;(2)在贴吧或论坛里拥有删帖权限的大小吧主,检查贴吧里是否存在灌水的帖子或跟帖,遇到就进行删除的行为。',      },      {        time: '2018-11-12 15:39:15',        avatar:          'https://sx-stag.oss-cn-shenzhen.aliyuncs.com/user-avatar/3_avatar.jpg?x-oss-process=image/resize,m_fixed,w_90,h_90/quality,q_90',        nickname: '卡布奇诺',        pos: 2,        voice: '',        img:          'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3040115650,4147729993&fm=26&gp=0.jpg',      },    ],  };  static getDerivedStateFromProps(nextProps, prevState) {    const { data } = nextProps;    // 若是props和缓存state一致,则不更新state    if (isEqual(prevState.prevData, nextProps.data)) {      return null;    }    // 若是没有传入props也是    if (!data || !Array.isArray(data) || data.length <= 0) {      return null;    }    return {      list: data,      prevData: nextProps.data,    };  }  // 唤醒子组件的回调过程  wakeUpLazyComponent = () => {    return <div>loading.....</div>;  };  // 悬浮条目显示删除按钮  showOperatBtn = index => {    let tmpList = [...this.state.list];    tmpList = tmpList.map((item, innerIndex) => {      if (index === innerIndex) {        item.operatBtn = true;      } else {        item.operatBtn = false;      }      return item;    });    this.setState({ list: tmpList });  };  // 关闭操作按钮  hideOperatBtn = index => {    let tmpList = [...this.state.list];    tmpList = tmpList.map((item, innerIndex) => {      item.operatBtn = false;      return item;    });    this.setState({ list: tmpList });  };  // 删除这条回复  deleteCurrentReplay = (index, itemInfo) => {    let tmpList = [...this.state.list];    tmpList.splice(index, 1);    this.setState({ list: tmpList });    // 给父的回调,把该item的所有信息返回,外部再去执行接口操作什么的    if (this.props.deleteItem) {      this.props.deleteItem(itemInfo);    }  };  render() {    const { list, deleteBtnSpin } = this.state;    // 是否显示操作区域    const { operate } = this.props;    // 渲染组件的前置条件    const isRender = list && list.length > 0;    return (      <ul className={style['list-wrapper']}>        {isRender &&          list.map((item, listIndex) => {            return (              <Suspense fallback={this.wakeUpLazyComponent()} key={listIndex}>                <li                  className={style['list-item']}                  onMouseOver={() => this.showOperatBtn(listIndex)}                  onMouseLeave={() => this.hideOperatBtn(listIndex)}                >                  <span className={style['time']}>{item.time ? item.time : '时间占位符'}</span>                  <div                    className={                      item.pos === 1                        ? style['list-item-horizontal']                        : style['list-item-horizontal-reverse']                    }                  >                    <Avatar                      shape="square"                      src={                        item.avatar                          ? item.avatar                          : 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png'                      }                    />                    <div                      className={                        item.pos === 1 ? style['content-wrapper-flex'] : style['content-wrapper']                      }                    >                      <p className={item.pos === 1 ? style.nickname : style['nickname-right']}>                        {item.nickname ? item.nickname : '用户昵称占位符'}                      </p>                      <div className={style.content}>                        <LazyComponent {...item} />                      </div>                    </div>                    {!!operate && item.operatBtn ? (                      <Popover                        content={'此操作会删除该记录'}                        title="谨慎操作!"                        onMouseEnter={() => {                          this.setState({ deleteBtnSpin: true });                        }}                        onMouseLeave={() => {                          this.setState({ deleteBtnSpin: false });                        }}                      >                        <Icon                          type="delete"                          spin={deleteBtnSpin}                          style={{                            fontSize: 24,                            alignSelf: 'flex-end',                            color: `${this.state.deleteBtnSpin ? '#ec1414' : '#1890ff'}`,                          }}                          onClick={() => this.deleteCurrentReplay(listIndex, item)}                        />                      </Popover>                    ) : null}                  </div>                </li>              </Suspense>            );          })}      </ul>    );  }}复制代码
RenderContent.js:渲染对话条目import React, { memo } from 'react';import style from './index.css';// antd 图文组件import { Card } from 'antd';const { Meta } = Card;const RenderContent = memo(props => {  if (props.img && props.text) {    return (      <Card        hoverable        style={{ width: 300 }}        cover={<img alt="example" src={props.img ? props.img : ''} />}      >        <Meta description={props.text ? props.text : ''} />      </Card>    );  }  if (props.img) {    return (      <div className={style['img-wrapper']}>        <img className={style['img-preview']} src={props.img ? props.img : ''} alt="photos" />      </div>    );  }  if (props.text) {    return <div className={style['bubble']}>{props.text}</div>;  }  if (props.voice) {    return <audio src={props.voice ? props.voice : ''} controls />;  }  return null;});export default RenderContent;复制代码
index.css : 样式composes是css module能识别的特殊字段,用于继承其他样式的
/* 列表全局样式 */.list-wrapper {  list-style-type: none;  list-style: none;  padding-left: 0;}/* 列表基本样式 */.list-item {  display: flex;  flex-direction: column;  justify-content: flex-start;  align-content: flex-start;  margin: 15px 0;}/* 水平展开 */.list-item-horizontal {  display: flex;  justify-content: flex-start;  align-items: flex-start;}/* 右对齐方式变化 */.list-item-horizontal-reverse {  composes: list-item-horizontal;  flex-direction: row-reverse;}/* 用户名 */.nickname {  font-size: 12px;  padding:0 10px;  color: #8a8484;  margin-bottom: 5px;}/* 用户名右对齐 */.nickname-right {  composes: nickname;  text-align: right;}/* 时间样式 */.time {  text-align: center;  background-color: #cecece;  color: #fff;  border-radius: 3px;  align-self: center;  font-size: 12px;  padding: 5px;  margin:5px;}/* 内容区域 */.content-wrapper {  margin: 0 15px;}/* 弹性伸缩 */.content-wrapper-responsive {  flex: 1;}/* 气泡文字 */.bubble {  padding: 8px;  color: #333;  max-width: 300px;  line-height: 1.5;  background-color: #a7e544;  border-radius: 3px;  text-align: left;  text-indent: 10px;  margin:0 3px;}/* 图片预览 */.img-wrapper {  box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.2), 0px 1px 1px 0px rgba(0, 0, 0, 0.14),    0px 2px 1px -1px rgba(0, 0, 0, 0.12);  border-radius: 3px;  padding: 5px;}.img-preview {  max-width: 200px;}复制代码
使用姿势接受的props
  • data, 格式是[Obj](数组对象);
  • operate : 布尔值(是否显示操作区域)
列表条目字段解释
  • time: 时间
  • avatar: 用户头像
  • nickname:用户昵称
  • pos: 1 (1在左侧渲染,2在右侧渲染)
  • voice(音频)/text(文本内容)/ img(图片内容) => voice(唯一)/** text + img** / text
  {    time: '2018-11-12 15:35:15',    avatar:      'https://sx-stag.oss-cn-shenzhen.aliyuncs.com/user-avatar/3_avatar.jpg?x-oss-process=image/resize,m_fixed,w_90,h_90/quality,q_90',    nickname: '用户甲',    pos: 1,    voice:      'https://sx-stag.oss-cn-shenzhen.aliyuncs.com/user-chat/3_508340417_c84f79407f5bc16b9e7ee0373631cf35.aac',    text: '',    img:          'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3040115650,4147729993&fm=26&gp=0.jpg'  },复制代码
总结
  • 上拉下拉不考虑,因为没这个需求,数据量不大..一个人的留言十几条最多了
  • 有需要的可以自行添加,也就是多暴露两个事件给外部(判断滚动的高度)
有不对之处请留言,会及时修正,谢谢阅读...


【转载】
作者:CRPER
链接:https://juejin.im/post/5bf614316fb9a04a006ea3c9



3 个回复

倒序浏览
回复 使用道具 举报
奈斯
回复 使用道具 举报
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马