React 自定義ListView組件

本文主要實現ListView的適配器功能,基本可以解決列表的絕大部分問題,原文可查看React 自定義ListView組件-適配模式

ListView組件實現

通過適配器的實現,適配器模式(Adapter Pattern)是作爲兩個不兼容的接口之間的橋樑。這種類型的設計模式屬於結構型模式,它結合了兩個獨立接口的功能。這種模式涉及到一個單一的類,該類負責加入獨立的或不兼容的接口功能。這樣就可以讓ListView如同Android中的ListView一樣動態設置Item佈局、數據格式,Item類型等。

/* eslint no-dupe-keys: 0 */
import React, { Component } from "react";

class ListView extends Component{

  constructor(props) {
    super(props);
    // console.log("get props:",this.props)
    this.state = {
      data: props.data?props.data:[],
    };
  }

  componentDidMount() {
    //組件首次加載時
    console.log("get componentDidMount props:",this.props)
  };

  componentWillReceiveProps(nextProps){
    if(this.props.data != nextProps.data){
      this.setState({
        data:nextProps.data?nextProps.data:[],
      })
    }
  };

  /**
   * 
   * @param {*子佈局數據格式} item 
   * @param {*子佈局位置} index 
   */
  getItem(item,index){
    let itemLayout = this.props.adapter(item,index,{onClick:this.onItemClick.bind(this,item,index)})
    return itemLayout
  };

  onItemClick(item,index){
    console.log("-------------pre click log--------------")
    return this.props.OnItemClick(item,index)
  };

  render() {
    return (
      <div className="list" >
        {
          {
         !!this.state.data?(this.state.data.map((item,index) => {
           return this.getItem(item,index)
         })):null
        }
        }
      </div>   
    );
  }
}

export default ListView;

ListView組件調用

ListView可以在任意頁面調用使用,只需在listView中指定adapter適配內容和點擊操作接口即可,代碼調用如下:

import React, { Component } from "react";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import {
  NavBar,
  Icon,
  Carousel,
  WingBlank,
  NoticeBar,
  WhiteSpace,
} from "antd-mobile";
import { StickyContainer, Sticky } from "react-sticky";
import ListView from "./listview";
import * as homeActions from "../redux/reduces/home";
import { Toast } from "../../node_modules/antd-mobile/lib/index";
import axios from "axios";


axios.defaults.timeout = 100000;
axios.defaults.baseURL = "http://test.mediastack.cn/";

/**
 * http request 攔截器
 */
axios.interceptors.request.use(
  (config) => {
    config.data = JSON.stringify(config.data);
    config.headers = {
      "Content-Type": "application/json",
    };
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

@connect(
  (state) => ({ home: state.home }),
  (dispatch) => bindActionCreators(homeActions, dispatch)
)
class Home extends Component {
  state = {
    data: ["1", "2", "3"],
    imgHeight: 176,
  };

  constructor(props) {
    super(props);

    setTimeout(() => {
      this.setState({
        data: [
          "AiyWuByWklrrUDlFignR",
          "TekJlZRVCjLFexlOCuWn",
          "IJOtIlfsYdTyaDTRVrLI",
        ],
      });
    }, 100);
  }

  componentDidMount() {

    axios.get('/article/home/index').then(
      (res) => {
        this.setState({
          list: res.data.data,
        });
        console.log("get article response:", res);
      },
      (error) => {
        console.log("get response failed:", error);
      }
    );
  }

  handleBrowserChange = () => {
    const { history, changeRoute } = this.props;
    changeRoute();
    history.push("/docs");
  };

  /**
   * 推薦內容適配器
   * @param {*} item
   * @param {*} index
   */
  RecommentAdapter(item, index,props) {
    return (
      <div
        {...props}
        className="item"
        style={{
          height: 80,
          backgroundColor: "#fff",
          textAlign: "left",
          padding: "0 15px 0",
          display: "flex",
          fontSize: 20,
          flexFlow: "column nowrap",
          justifyContent: "center",
          marginBottom: 10,
        }}
        key={index}
      >
        <label
          className="title"
          style={{
            minWidth: 0,
            overflow: "hidden",
            textOverflow: "ellipsis",
            whiteSpace: "nowrap",
          }}
        >
          {item.title}
        </label>
        <div style={{
          fontSize:12,
          marginTop:12,
          color:'#888888'
        }}>
        <span style={{
          color:'#111111',
          fontWeight:'500',
        }}>{item.category}</span> |
        <span> {item.author}</span> |
        <span> 閱讀量:{item.views}</span> |
        <span> {item.date}</span>
        </div>

      </div>
    );
  };


  onItemClick(item,index){
    Toast.show("你點擊的第"+index+"個item.",Toast.SHORT)
  };

  render() {
    return (
      <div className="home">
        <StickyContainer>
          <Sticky>
            {({
              style,
              isSticky,
              wasSticky,
              distanceFromTop,
              distanceFromBottom,
              calculatedHeight,
            }) => (
              <NavBar
                style={{
                  ...style,
                  zIndex: 3,
                  padding: "2px 0",
                }}
                mode="light"
                icon={<Icon type="left" />}
                onLeftClick={() => console.log("onLeftClick")}
                rightContent={[
                  <Icon
                    key="0"
                    type="search"
                    style={{ marginRight: "16px" }}
                  />,
                  <Icon key="1" type="ellipsis" />,
                ]}
              >
                胖蔡雜談
              </NavBar>
            )}
          </Sticky>
          {/* Sticky 爲懸浮框 */}
          <WingBlank>
            <WhiteSpace size="lg" />
            <Carousel
              autoplay={false}
              infinite
              beforeChange={(from, to) =>
                console.log(`slide from ${from} to ${to}`)
              }
              afterChange={(index) => console.log("slide to", index)}
            >
              {this.state.data.map((val) => (
                <a
                  key={val}
                  href="http://www.alipay.com"
                  style={{
                    display: "inline-block",
                    width: "100%",
                    height: this.state.imgHeight,
                  }}
                >
                  <img
                    src={`https://zos.alipayobjects.com/rmsportal/${val}.png`}
                    alt=""
                    style={{
                      width: "100%",
                      verticalAlign: "top",
                    }}
                    onLoad={() => {
                      // fire window resize event to change height
                      window.dispatchEvent(new Event("resize"));
                      this.setState({
                        imgHeight: "auto",
                      });
                    }}
                  />
                </a>
              ))}
            </Carousel>
            <WhiteSpace size="lg" />
            <NoticeBar
              marqueeProps={{ loop: true, style: { padding: "0 7.5px" } }}
            >
              通知:北京繼續暫停出租車順風車出京運營
              合肥明日繼續新一波消費券發放,預計發放4000萬元
            </NoticeBar>
            <WhiteSpace size="lg" />
            <ListView data={this.state.list} 
                  adapter={this.RecommentAdapter} 
                  OnItemClick={this.onItemClick}/>
          </WingBlank>
        </StickyContainer>
      </div>
    );
  }
}

export default Home;

adapter 返回單個Item的jsx,可通過React組件或者方法返回均可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章