Appearance
歌手列表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>