React+dva+webpack+antd-mobile 实战分享

Posted 前端学习123321123

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React+dva+webpack+antd-mobile 实战分享相关的知识,希望对你有一定的参考价值。

第一篇 https://segmentfault.com/a/11...

在上一篇文章中教给大家了怎么搭建项目的架子;那么今天我们就来说一下项目里的导航和列表的实现

导航

技术图片

废话不说啦 下面直接给大家讲一下代码
项目用的antd-mobile的框架 应该没什么难度,我相信大家认真看文档的都能布局出来;

TabButton.js

import React, { Component } from ‘react‘;
import { Tabs, WhiteSpace,ListView,Toast} from ‘antd-mobile‘;
import { routerRedux } from ‘dva/router‘;
import { connect } from ‘dva‘;
import Request from ‘../common/fetch‘
import {width,height} from ‘../common/style‘;

const TabPane = Tabs.TabPane;

class TabButton extends Component {
  constructor(props) {
    super(props);
    this.state = {
      channels: []
    }
  }
 
  componentDidMount() {
  // 这个地方是封装的fetch请求;
    Request(‘/api/article/channel‘,{
      secret:1111,
    },((res) => {
      this.setState({
        channels: res.result.channels
      })
      // 请求过来的数据全部存下来,以便后期调用,同时可以减少请求
      this.props.dispatch({
        type: ‘indexList/TabData‘,
        payload: res.result.channels,
      });
    }))
  }
//这个点需要注意:此处是将click事件传递给子组件,另一界面 <TabButton ButtonClick ={this.ButtonClick.bind(this)} />就可以取到此组件传递过去的click事件;
  _handleTabClick(key){
    this.props.ButtonClick(key);
  }

  _renderList() {
    let result = [];
    const channels = this.state.channels;
    for(let i in channels) {
      if(channels[i].attval == 1 || channels[i].attval == 2){
        result.push(
          <TabPane tab={`${channels[i].title}`} key={`${parseInt(channels[i].ID)}`}>
            <div style={{ display: ‘flex‘, alignItems: ‘center‘, justifyContent: ‘center‘,border:‘none‘ }}>
            </div>
          </TabPane>
        )
      }
    }
    return result
  }

  _getMore() {
    this.props.dispatch(
      routerRedux.push(‘/moreChannel‘)
    )
  }

  render() {
    return(
      <div style={{position:‘fixed‘,top:44,zIndex:999,backgroundColor:‘#fff‘,width:(width/7)*6}}>
        <Tabs defaultActiveKey="1"
              pageSize={7}
              onTabClick={(key) => {this._handleTabClick(key)}}
              swipeable = {false}
          >
          {this._renderList()}
        </Tabs>
        <p style={styles.moreChannel} onClick={() => this._getMore()}>
          <img style={{width:26,height:26,marginTop:8,marginLeft:14}} src={require(‘../../assets/list/addchannel@2x.png‘)} />
        </p>
      </div>
    )
  }
}

const styles = {
  moreChannel:{
    position:‘absolute‘,
    top:0,
    right:-width/7,
    zIndex:9999,
    width:width/7,
    height:42,
    backgroundColor:‘#fff‘,
    alignItems:‘center‘,
    justifyContent:‘center‘
  }
}


function indexList({indexList}) {
  return { indexList };
}

export default connect(indexList)(TabButton);

fetch.js

export default function Request(url,body,callback){
  fetch(url,{
    method: ‘POST‘,
    mode: "cors",
    headers: {
      ‘Content-Type‘: ‘application/json‘,
      ‘Accept‘: ‘application/json‘
    },
    body: JSON.stringify(body)
  }).then((res) => res.json()).then((res) => {
    callback(res)
  }).catch((err) => {
    console.log(err)
  })
}

列表

indexTab.js

import React, { Component,PureComponent,PropTypes } from ‘react‘;
import { Tabs, WhiteSpace,ListView,Toast} from ‘antd-mobile‘;
import { routerRedux } from ‘dva/router‘;
import { connect } from ‘dva‘;
import ReactPullLoad,{ STATS } from ‘react-pullload‘;
import TabButton from ‘./TabButton‘;
import {width,height} from ‘../common/style‘;

let devicenum = localStorage.getItem(‘devicenum‘)
const loadMoreLimitNum = 10;

const defaultStyle ={
  width: "100%",
  textAlign: "center",
  fontSize: "14px",
  lineHeight: "1.5",
  paddingTop:"12px",
  color:‘#ccc‘
}

class HeadNode extends PureComponent{

  static propTypes = {
    loaderState: PropTypes.string.isRequired,
  };

  static defaultProps = {
    loaderState: STATS.init,
  };

  render(){
    const {
      loaderState
      } = this.props

    let content = ""
    if(loaderState == STATS.pulling){
      content = "下拉刷新"
    } else if(loaderState == STATS.enough){
      content = "松开刷新"
    } else if(loaderState == STATS.refreshing){
      content = "正在刷新..."
    } else if(loaderState == STATS.refreshed){
      content = "刷新成功"
    }

    return(
      <div style={defaultStyle}>
        {content}
      </div>
    )
  }
}


class FooterNode extends PureComponent{
  static propTypes = {
    loaderState: PropTypes.string.isRequired,
    hasMore: PropTypes.bool.isRequired
  };

  static defaultProps = {
    loaderState: STATS.init,
    hasMore: true
  };

  render(){
    const {
      loaderState,
      hasMore
      } = this.props
    let content = ""
    if(loaderState == STATS.loading){
      return(
        <div style={defaultStyle}>
          <img src={require(‘../../assets/state/fail@2x.png‘)}  style={{width:32,height:40}} />
          <span>正在加載喔~</span>
        </div>
      )
    } else if(hasMore === false){
      content = "没有更多"
    }

    return(
      <div style={defaultStyle}>
        {content}
      </div>
    )
  }
}


class indexTab extends Component {
  constructor(props) {
    super(props)
    this.state = {
      channels : [],
      channelid : 1,
      showT:false,
      loading : false,
      hasMore: true,
      data: [],
      action: STATS.init,
      index: loadMoreLimitNum,
      newsLength:‘‘
    }
  }

  componentDidMount() {
    this.getListData(this.state.channelid);
  }

  getListData(channelid) {
    // List
    fetch(‘/api/article‘,{
      method: ‘POST‘,
      mode: "cors",
      headers: {
        ‘Content-Type‘: ‘application/json‘,
        ‘Accept‘: ‘application/json‘
      },
      body: JSON.stringify({
        channelID: channelid,
        type: 0,
        pageSize: 10,
        dt : 2,
        action: 1,
        devicenum:devicenum
      })
    }).then((res) => res.json()).then((res) => {
      this.setState({
        data: res.result.news,
        newsLength:res.result.news.length
      })
      this.props.dispatch({
        type: ‘indexList/detailData‘,
        payload: res.result.news,
      });
    }).then(() => {
      setTimeout(() => {
        this.setState({
          showT : true
        })
      },1900)
    }).then(() => {
      setTimeout(() => {
        this.setState({
          showT : false
        })
      },2900)
    }).catch((err) => {
      console.log(err)
    })
  }

  handleAction = (action) => {
    console.info(action, this.state.action,action === this.state.action);
    if(action === this.state.action){
      return false
    }
    if(action === STATS.refreshing){//刷新
      this.handRefreshing();
    } else if(action === STATS.loading){
      this.handLoadMore();
    } else{
      this.setState({
        action: action
      })
    }
  }
  handRefreshing = () =>{
    if(STATS.refreshing === this.state.action){
      return false
    }
    this.getListData(this.state.channelid)
    setTimeout(()=>{
      this.setState({
        action: STATS.refreshed,
        index: loadMoreLimitNum
      });
    }, 3000)

  }
  handLoadMore = () => {
    if(STATS.loading === this.state.action){
      return false
    }
    setTimeout(()=>{
      if(this.state.index === 0){
        this.setState({
          action: STATS.reset,
          hasMore: false
        });
      } else{
        fetch(‘/api/article‘,{
          method: ‘POST‘,
          headers: {
            ‘Content-Type‘: ‘application/json;charset=UTF-8‘,
            ‘Accept‘: ‘application/json‘
          },
          body: JSON.stringify({
            channelID: this.state.channelid,
            type: 0,
            pageSize: 10,
            dt : 2,
            action: 1,
            devicenum:devicenum
          })
        }).then((res) => res.json()).then((res) => {
          this.setState({
            data: [...this.state.data,...res.result.news],
              action: STATS.reset,
              index: this.state.index - 1
          })
          this.props.dispatch({
            type: ‘indexList/detailData‘,
            payload: [...this.state.data,...res.result.news],
          });
        }).then(() => {
          console.log(this.state.showT)
          setTimeout(() => {
            this.setState({
              showT : true
            })
          },1900)
        }).then(() => {
          setTimeout(() => {
            this.setState({
              showT : false
            })
          },2900)
        }).catch((err) => {
          console.log(err)
        })
      }
    }, 3000)
    this.setState({
      action: STATS.loading
    })
  }

  //跳转到详情页
  _routerDetail(index) {
    localStorage.setItem(‘detailid‘,index)
    this.props.dispatch(
      routerRedux.push(`/detail/${index}`)
    )
  }

  //Tab 切换重新调取
  ButtonClick(key) {
    this.getListData(key);
    this.setState({
      channelid:key
    })
  }

  _renderShow() {
    if(this.state.showT == true){
      if(this.state.newsLength != 0){
        return(
          <p style={styles.more}>更新了{this.state.newsLength}条内容</p>
        )
      }else{
        return(
          <p style={styles.more}>暂無更新推送</p>
        )
      }
    }else{
      return(
        <p></p>
      )
    }
  }

  render(){
    const {data,hasMore} = this.state
    return (
      <div>
        <TabButton
          ButtonClick = {this.ButtonClick.bind(this)}
          />
        <p style={{width:100,height:80}}></p>
        <ReactPullLoad
          downEnough={50}
          action={this.state.action}
          handleAction={this.handleAction}
          hasMore={hasMore}
          distanceBottom={10}
          HeadNode={HeadNode}
          FooterNode={FooterNode}
          >
          <ul className="test-ul">
            {
              data.map( (str, index )=>{
                if(str.images[0] != ‘‘){
                  return <li key={index}>
                    <div style={styles.news} onClick = {() => this._routerDetail(index)}>
                      <img src={str.images[0]} style={styles.imgStyle} />
                      <p style={styles.newsTitle}>{str.title}</p>
                      <p style={{fontSize:12,color:‘#ccc‘,borderWidth:1}}><span style={{color:‘#03D7FF‘}}>{str.source}</span> | {str.publishTime}</p>
                    </div>
                  </li>
                }else{
                  return <li key={index}>
                    <div style={styles.news} onClick = {() => this._routerDetail(index)}>
                      <p style={styles.newsTitle}>{str.title}</p>
                      <p style={{fontSize:12,color:‘#ccc‘,borderWidth:1}}><span style={{color:‘#03D7FF‘}}>{str.source}</span> | {str.publishTime}</p>
                    </div>
                  </li>
                }
              })
            }
          </ul>
        </ReactPullLoad>
        <div>
        </div>
        {this._renderShow()}
      </div>
    )
  }

}


const styles = {
  more: {
    width:width,
    backgroundColor:‘#FFDB01‘,
    position:‘absolute‘,
    zIndex:9999,
    top:86,
    textAlign:‘center‘,
    padding:5,
    fontSize:14,
    display:‘block‘,

  },
  news: {
    padding:15,
    justifyContent:‘center‘,
    alignItems:‘center‘
  },
  imgStyle: {
    width:width-30,
    //height:100
  },
  newsTitle: {
    fontSize:18,
    marginTop:10,
    marginBottom:10
  },
  moreTab: {
    width:width-(width/7)*6,
    height:43,
    backgroundColor:‘#fff‘,
    position: ‘absolute‘,
    justifyContent:‘center‘,
    alignItems:‘center‘,
    top:44,
    right:0,
    zIndex:9999
  }
}
function indexList({ indexList }) {
  return { indexList };
}
export default connect(indexList)(indexTab);

好啦 上述就是整个首页的主要代码,知道如何创建项目的你们可以尝试啦~~~


以上是关于React+dva+webpack+antd-mobile 实战分享的主要内容,如果未能解决你的问题,请参考以下文章

react dva 的 connect 与 @connect

creat-react-app/dva静态项目,用nginx部署在次级域名路径(如a.com/sub/)需要注意的几点

基于umi+dva+antd打造的数据可视化平台

dva 是啥

React dva 的使用

react dva 基本介绍