Skip to content

歌手列表2 ​

本节目标:理解“歌手列表2”的核心思路,并能把它用于实际开发或面试表达。

说明:原 OneNote 中有图片引用,但图片未包含在导出目录中;本页保留了可用的文字与代码内容。 ==为了做出小小的分类横向滚动列表,可谓花了不少的力气。不过做完了这个,再来开发歌手列表,简直易如反掌了。== ==进入== Singers/index.js, ==增加以下代码,== //mock ==数据==

const singerList = [1, 2,3, 4,5,6,7,8,9,10,11,12].map (item => {  return {    picUrl: "https://p2.music.126.net/uTwOm8AEFFX_BYHvfvFcmQ==/109951164232057952.jpg",    name: "

==隔壁老樊==

",    accountId: 277313426,  }});
//

==渲染函数,返回歌手列表==

const renderSingerList = () => {
  return (    <List>      {        singerList.map ((item, index) => {          return (            <ListItem key={item.accountId+""+index}>              <div className="img_wrapper">                <img src={`${item.picUrl}?param=300x300`} width="100%" height="100%" alt="music"/>              </div>              <span className="name">{item.name}</span>            </ListItem>          )        })      }    </List>  )};

==然后将返回的== JSX ==代码做一些改动==

:
return (  <div>    <NavContainer>      <Horizen         list={categoryTypes}         title={"

==分类== (==默认热门==

):"}         handleClick={(val) => handleUpdateCatetory (val)}         oldVal={category}></Horizen>      <Horizen         list={alphaTypes}         title={"

==首字母==

:"}         handleClick={val => handleUpdateAlpha (val)}         oldVal={alpha}></Horizen>    </NavContainer>     <ListContainer>      <Scroll>        { renderSingerList () }      </Scroll>    </ListContainer>  </div>)

==现在项目会报错,因为样式组件还没有定义,我们在== style.js ==中添加:==

export const ListContainer = styled.div`  position: fixed;  top: 160px;  left: 0;  bottom: 0;  overflow: hidden;  width: 100%;`;
export const List = styled.div`  display: flex;  margin: auto;  flex-direction: column;  overflow: hidden;  .title {    margin:10px 0 10px 10px;    color: ${style ["font-color-desc"]};    font-size: ${style ["font-size-s"]};  }`;export const ListItem = styled.div`  box-sizing: border-box;  display: flex;  flex-direction: row;  margin: 0 5px;  padding: 5px 0;  align-items: center;  border-bottom: 1px solid ${style ["border-color"]};  .img_wrapper {    margin-right: 20px;    img {      border-radius: 3px;      width: 50px;      height: 50px;    }  }  .name {    font-size: ${style ["font-size-m"]};    color: ${style ["font-color-desc"]};    font-weight: 500;  }`;

==在== index.js ==中引入==

:
import {   NavContainer,  ListContainer,  List,  ListItem} from "./style";

==现在你就能看到一个可以滚动的歌手列表啦!==

数据层开发 ==刚刚只是==mock==数据,要实现真正的线上功能,还有很多工作要做。== axios请求处理 ==进入到==api/request.js==中,加入下面的请求代码==

:
export const getHotSingerListRequest = (count) => {
  return axiosInstance.get(`/top/artists?offset=${count}`);
}
export const getSingerListRequest= (category, alpha, count) => {
  return axiosInstance.get(`/artist/list?cat=${category}&initial=${alpha.toLowerCase()}&offset=${count}`);
}

==这就是我们目前需要的全部==ajax==请求。== redux层开发redux==刚开始接触的时候确实是比较复杂,但多些几次你就会发现其实就是一些模板代码,并没有什么难的。还是按推荐模块一样,我们来按照步骤开发==redux==模块。== ==注意,在这里我们会添加一些新的业务逻辑,比如上拉==/==下拉==/==进场加载动画的控制、列表页数的控制,大家看到不要感到奇怪。== ==在==Singers==目录下,新建==store==文件夹,然后新建以下文件==

:
actionCreators.js //

==放不同==action==的地方==

constants.js      //

==常量集合,存放不同==action==的==type==值==

index.js          //

==用来导出==reducer==,==

actionreducer.js        //

==存放==initialState==和==reducer==函数==


1.

声明初始化state ==初始化==state==在==reducer==中进行==

//reducer.jsimport { fromJS } from 'immutable';
const defaultState = fromJS({  singerList: [],  enterLoading: true,     //

==控制进场==

Loading  pullUpLoading: false,   //

==控制上拉加载动画==

  pullDownLoading: false, //

==控制下拉加载动画==

  pageCount: 0            //

==这里是当前页数,我们即将实现分页功能==

});
2.

定义

constants
export const CHANGE_SINGER_LIST = 'singers/CHANGE_SINGER_LIST';
export const CHANGE_PAGE_COUNT = 'singers/PAGE_COUNT';
export const CHANGE_ENTER_LOADING = 'singers/ENTER_LOADING';
export const CHANGE_PULLUP_LOADING = 'singers/PULLUP_LOADING';
export const CHANGE_PULLDOWN_LOADING = 'singers/PULLDOWN_LOADING';
3.

定义reducer函数 ==在==reducer.js==文件中加入以下处理逻辑,由于存放的是==immutable==数据结构,所以必须用==set==方法来设置新状态,同时取状态用==get==方法。==

export default (state = defaultState, action) => {
  switch(action.type) {
    case actionTypes.CHANGE_SINGER_LIST:
      return state.set('singerList', action.data);
    case actionTypes.CHANGE_PAGE_COUNT:
      return state.set('pageCount', action.data);
    case actionTypes.CHANGE_ENTER_LOADING:
      return state.set('enterLoading', action.data);
    case actionTypes.CHANGE_PULLUP_LOADING:
      return state.set('pullUpLoading', action.data);
    case actionTypes.CHANGE_PULLDOWN_LOADING:
      return state.set('pullDownLoading', action.data);
    default:
      return state;
  }
}
4.

编写具体的

action
import {
  getHotSingerListRequest,
  getSingerListRequest
} from "../../../api/request";
import {
  CHANGE_SINGER_LIST,
  CHANGE_PAGE_COUNT,
  CHANGE_PULLUP_LOADING,
  CHANGE_PULLDOWN_LOADING,
  CHANGE_ENTER_LOADING
} from './constants';
import {
  fromJS
} from 'immutable';
const changeSingerList = (data) => ({
  type: CHANGE_SINGER_LIST,
  data: fromJS(data)
});
export const changePageCount = (data) => ({
  type: CHANGE_PAGE_COUNT,
  data
});
//

进场

loading
export const changeEnterLoading = (data) => ({
  type: CHANGE_ENTER_LOADING,
  data
});
//

滑动最底部

loading
export const changePullUpLoading = (data) => ({
  type: CHANGE_PULLUP_LOADING,
  data
});
//

顶部下拉刷新

loading
export const changePullDownLoading = (data) => ({
  type: CHANGE_PULLDOWN_LOADING,
  data
});
export const getHotSingerList = () => {
  return (dispatch) => {
    getHotSingerListRequest(0).then(res => {
      const data = res.artists;
      dispatch(changeSingerList(data));
      dispatch(changeEnterLoading(false));
      dispatch(changePullDownLoading(false));
    }).catch(() => {
      console.log('

热门歌手数据获取失败

');
    })
  }
};
export const refreshMoreHotSingerList = () => {
  return (dispatch, getState) => {
    const pageCount = getState().getIn(['singers', 'pageCount']);
    const singerList = getState().getIn(['singers', 'singerList']).toJS();
    getHotSingerListRequest(pageCount).then(res => {
      const data = [...singerList, ...res.artists];
      dispatch(changeSingerList(data));
      dispatch(changePullUpLoading(false));
    }).catch(() => {
      console.log('

热门歌手数据获取失败

');
    });
  }
};
export const getSingerList = (category, alpha) => {
  return (dispatch, getState) => {
    getSingerListRequest(category, alpha, 0).then(res => {
      const data = res.artists;
      dispatch(changeSingerList(data));
      dispatch(changeEnterLoading(false));
      dispatch(changePullDownLoading(false));
    }).catch(() => {
      console.log('

歌手数据获取失败

');
    });
  }
};
export const refreshMoreSingerList = (category, alpha) => {
  return (dispatch, getState) => {
    const pageCount = getState().getIn(['singers', 'pageCount']);
    const singerList = getState().getIn(['singers', 'singerList']).toJS();
    getSingerListRequest(category, alpha, pageCount).then(res => {
      const data = [...singerList, ...res.artists];
      dispatch(changeSingerList(data));
      dispatch(changePullUpLoading(false));
    }).catch(() => {
      console.log('

歌手数据获取失败

');
    });
  }
};
5.

将相关变量导出

//index.jsimport reducer from './reducer'import * as actionCreators from './actionCreators'
export { reducer, actionCreators };

组件连接Redux ==首先,需要将==Singers==下的==reducer==注册到全局==store==,在==src==目录下的==store/reducer.js==中,内容如下==

:
import { combineReducers } from 'redux-immutable';import { reducer as recommendReducer } from '../application/Recommend/store/index';import { reducer as singersReducer } from '../application/Singers/store/index';
export default combineReducers({  recommend: recommendReducer,  singers: singersReducer,});

==好,现在已经在全局的==store==下面注册完成。现在在==Singers/index.js==中,准备连接==Redux==。== ==增加代码==

:
import React, {useState, useEffect} from 'react';import Horizen from '../../baseUI/horizen-item';import { categoryTypes, alphaTypes } from '../../api/config';import {   NavContainer,  ListContainer,  List,  ListItem,} from "./style";import {   getSingerList,   getHotSingerList,   changeEnterLoading,   changePageCount,   refreshMoreSingerList,   changePullUpLoading,   changePullDownLoading,   refreshMoreHotSingerList } from './store/actionCreators';import Scroll from './../../baseUI/scroll/index';import {connect} from 'react-redux';
//

==在此省略组件代码==


const mapStateToProps = (state) => ({  singerList: state.getIn(['singers', 'singerList']),  enterLoading: state.getIn(['singers', 'enterLoading']),  pullUpLoading: state.getIn(['singers', 'pullUpLoading']),  pullDownLoading: state.getIn(['singers', 'pullDownLoading']),  pageCount: state.getIn(['singers', 'pageCount'])});const mapDispatchToProps = (dispatch) => {  return {    getHotSingerDispatch() {      dispatch(getHotSingerList());    },    updateDispatch(category, alpha) {      dispatch(changePageCount(0));//

==由于改变了分类,所以==pageCount==清零==

      dispatch(changeEnterLoading(true));//loading

==,现在实现控制逻辑,效果实现放到下一节,后面的==loading==同理==

      dispatch(getSingerList(category, alpha));    },    //

==滑到最底部刷新部分的处理==

    pullUpRefreshDispatch(category, alpha, hot, count) {      dispatch(changePullUpLoading(true));      dispatch(changePageCount(count+1));      if(hot){        dispatch(refreshMoreHotSingerList());      } else {        dispatch(refreshMoreSingerList(category, alpha));      }    },    //

==顶部下拉刷新==

    pullDownRefreshDispatch(category, alpha) {      dispatch(changePullDownLoading(true));      dispatch(changePageCount(0));//

==属于重新获取数据==

      if(category === '' && alpha === ''){        dispatch(getHotSingerList());      } else {        dispatch(getSingerList(category, alpha));      }    }  }};

==记得最后用==react-redux==中的==connect==包裹==

:
export default connect(mapStateToProps, mapDispatchToProps)(React.memo(Singers));

==好,现在就可以完美地显示真实的列表了。== 分类和列表联动 ==但是,点击不同的分类并没有获取相应的列表,现在我们就来实现分类和列表联动的功能。== ==这当然要从==handleUpdatexxx==函数开始着手啦。其实非常简单,只需做如下修改即可:==

let handleUpdateAlpha = (val) => {  setAlpha(val);  updateDispatch(category, val);};
let handleUpdateCatetory = (val) => {  setCategory(val);  updateDispatch(val, alpha);};

==至此,这样联动效果就实现啦!== > 来自

 <https://juejin.im/book/5da96626e51d4524ba0fd237/section/5da97569e51d4578200ccc68>

本站总访问 — 次