本节代码对应 GitHub 分支: chapter7
# axios 请求准备
//api/request.js
export const getSingerInfoRequest = id => {
return axiosInstance.get (`/artists?id=${id}`);
};
# redux 层开发
# 1. 声明初始化 state
//store/reducer.js
import * as actionTypes from './constants';
import { fromJS } from 'immutable';
const defaultState = fromJS ({
artist: {},
songsOfArtist: [],
loading: true
});
# 2. 定义 constants
//store/constants.js
export const CHANGE_ARTIST = 'singer/CHANGE_ARTIST';
export const CHANGE_SONGS_OF_ARTIST = 'singer/CHANGE_SONGS_OF_ARTIST';
export const CHANGE_ENTER_LOADING = 'singer/CHNAGE_ENTER_LOADING';
# 3. 定义 reducer 函数
//store/reducer.js
export default (state = defaultState, action) => {
switch (action.type) {
case actionTypes.CHANGE_ARTIST:
return state.set ('artist', action.data);
case actionTypes.CHANGE_SONGS_OF_ARTIST:
return state.set ('songsOfArtist', action.data);
case actionTypes.CHANGE_ENTER_LOADING:
return state.set ('loading', action.data);
default:
return state;
}
}
# 4. 编写具体的 action
import { CHANGE_SONGS_OF_ARTIST, CHANGE_ARTIST, CHANGE_ENTER_LOADING } from './constants';
import { fromJS } from 'immutable';
import { getSingerInfoRequest } from './../../../api/request';
const changeArtist = (data) => ({
type: CHANGE_ARTIST,
data: fromJS (data)
});
const changeSongs = (data) => ({
type: CHANGE_SONGS_OF_ARTIST,
data: fromJS (data)
})
export const changeEnterLoading = (data) => ({
type: CHANGE_ENTER_LOADING,
data
})
export const getSingerInfo = (id) => {
return dispatch => {
getSingerInfoRequest (id).then (data => {
dispatch (changeArtist (data.artist));
dispatch (changeSongs (data.hotSongs));
dispatch (changeEnterLoading (false));
})
}
}
# 5. 将相关变量导出
//index.js
import reducer from './reducer'
import * as actionCreators from './actionCreators'
export { reducer, actionCreators };
# 组件连接 Redux
首先,需要将 Singer 下的 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';
import { reducer as rankReducer } from '../application/Rank/store/index';
import { reducer as albumReduimport { reducer as singerInfoReducer } from "../application/Singer/store/index";
import { reducer as singerInfoReducer } from "../application/Singer/store/index";
export default combineReducers ({
recommend: recommendReducer,
singers: singersReducer ,
rank: rankReducer,
album: albumReducer,
singerInfo: singerInfoReducer
});
现在在 Singer/index.js 中,准备连接 Redux。增加代码:
import { connect } from 'react-redux';
import { getSingerInfo, changeEnterLoading } from "./store/actionCreators";
// 组件代码省略
// 映射 Redux 全局的 state 到组件的 props 上
const mapStateToProps = state => ({
artist: state.getIn (["singerInfo", "artist"]),
songs: state.getIn (["singerInfo", "songsOfArtist"]),
loading: state.getIn (["singerInfo", "loading"]),
});
// 映射 dispatch 到 props 上
const mapDispatchToProps = dispatch => {
return {
getSingerDataDispatch (id) {
dispatch (changeEnterLoading (true));
dispatch (getSingerInfo (id));
}
};
};
// 将 ui 组件包装成容器组件
export default connect (mapStateToProps,mapDispatchToProps)(React.memo (Singer));
同时组件代码做如下添加:
// 记得删除 mock 数据
const {
artist: immutableArtist,
songs: immutableSongs,
loading,
} = props;
const { getSingerDataDispatch } = props;
const artist = immutableArtist.toJS ();
const songs = immutableSongs.toJS ();
同时 SongList 组件中传入的 songs 参数应该改为现在从 redux 取得的 songs 变量。
# 组件对接真实数据
很简单,我们在 useEffect 中添加请求代码即可:
useEffect (() => {
const id = props.match.params.id;
getSingerDataDispatch (id);
// 之前写的 UI 处理逻辑省略
}, []);
# 添加 loading
import Loading from "./../../baseUI/loading/index";
return (
//Container 组件下面
{ loading ? (<Loading></Loading>) : null}
)
至此,一个精美的歌手详情页就打造完成了。恭喜你,又解锁了一个新的模块。
下一章,我们开始本项目最复杂、最具有挑战性的模块:播放器模块开发。大家加油!
阅读全文