react + redux 开发的学习例子总结

1,974 阅读3分钟
原文链接: github.com

本人工作之余的闲暇时间还是很充裕的。在完成经理安排的任务后,基本上都是在自学,主要是阅读各种技术文档、浏览博客、运行别人写的一些前端demo并观赏与赞叹。在ScorpionJay 同学的带领下,我们决定决定做一个react版的音乐播放SPA(Single Page web Application)。

首选,模仿网易云的界面,因为是程序猿最爱的音乐播放器,不解释!然而,分析网易云的数据请求时,貌似全是403。github上有大牛提供了网易云音乐的一些接口,非常好。所以,对于数据接口,我们最后选择了“哈喽,酷狗!”。

阅读了redux的文档后,并参与了一个练手项目r-music的部分开发后,通过自己的理解,整理了这篇文档。

初学,所以一定会有不详或者错误的地方。

参考文档

开发这个项目,我参阅的学习文档如下:

流程图解

通过我自己的理解方式,简单地整理了react、redux、react-redux三者之间的关系图,如下:

通过代码,梳理redux、react-redux

注:下面代码只列出搜索功能的关键部分,源码地址:github.com/ScorpionJay…

1. Provider

react-redux提供的Provider组件,可以让容器组件取得state

src/index.js

import configureStore from './stores'
const store = configureStore()

<Provider store={store}>
	<Router history={browserHistory} routes={routers} />
</Provider>

上面代码中,Provider使得Router的所有子组件可以取得state

import configureStore from './stores'为redux的store,如下:

src/store/index.js

import reducers from '../reducers/index';

export default function(initialState) {
	let createStoreWithMiddleware

	// 判断环境是否logger
	if (process.env.NODE_ENV === 'production') {
		createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
	}else{
		//开发环境在console可以看到整个状态树的实时日志
		const logger = createLogger();
		createStoreWithMiddleware = applyMiddleware(thunk,logger)(createStore);
	}
	let store = createStoreWithMiddleware(reducers, initialState);
	return store;
};

2. react:Component

src/containers/search.js

import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'

import { searchHotAPI,searchResultAPI,clearSearchResultAPI} from '../actions/search'

class Search extends Component {

  constructor(props) {
    super(props);
  }

  componentDidMount(){
    const { dispatch } = this.props
    dispatch(searchHotAPI())
  }

  searchEvt(keyword,page=1){
    const { dispatch } = this.props;
    keyword = keyword || this.refs.keyword.value
    if(keyword!=''){
      dispatch(searchResultAPI(keyword, page));
    }else{
      dispatch(clearSearchResultAPI());
    }
    this.refs.keyword.value = keyword;
  }


  render() {
    const { dispatch,controll,search } = this.props;
    return (
      <div className='root' style={{fontSize:'1.2rem'}}>

		//...

      </div>
    )
  }
}

function map(state) {
  return {
    search: state.search,
    controll: state.music.controll
  }
}

export default connect(map)(Search)

react-redux的connect方法,用于从 UI 组件生成容器组件。

上面代码中,connect(map)(Search)使得组件Search可以通过props取得map返回的数据。

dispatch(searchHotAPI())dispatch(clearSearchResultAPI()),获取数据并分发action。

3. redux

src/actions/search.js

import Config from '../config'
import { spin,spinHidden } from './spin'
import api from '../api'

import Storage from '../storage'

//定义常量
export const SEARCH_HOT = 'SEARCH_HOT'
export const SEARCH_RESULT = 'SEARCH_RESULT'

//actionCreator,这里是一个函数,返回action对象
const searchHot = (obj) => {return {type:SEARCH_HOT, obj}}
const searchResult = (obj) => {return {type:SEARCH_RESULT, obj}}

//搜索热门关键字
export function searchHotAPI(){
	return async dispatch => {
		try{
			let hots = await api( Config.searchHotAPI );
			dispatch(searchHot(hots.data.info));
		} catch(error) {
			console.log(error);
		}
	}
}

//通过关键字搜索
export function searchResultAPI(keyword,page){
	return async dispatch => {
		try {
			let result = await api( Config.searchResultAPI, 'get', {keyword,page} );
			//搜索历史存到localStorage
			setSearchHistory(keyword);
			dispatch(searchResult(result.data.info));
		} catch(error) {
			console.log(error);
		}
	}
}

上面代码中,searchHotsearchResult都是Action creator,即分别返回一个action。

action是一个带有type关键字的对象,如{type:SEARCH_HOT, obj}{type:SEARCH_RESULT, obj}

searchHotAPIsearchResultAPI分别返回一个获取数据并分发action的异步函数,一般在容器组件里会调用。

src/reducer/search.js

import { combineReducers } from 'redux'
import { SEARCH_HOT,SEARCH_RESULT } from '../actions/search'

function hots(state = [], action){
  switch(action.type) {
    case SEARCH_HOT:
      return action.obj;
    default:
      return state;
  }
}

function result(state = [], action){
  switch(action.type) {
    case SEARCH_RESULT:
      return action.obj;
    default:
      return state;
  }
}


const Reducers = combineReducers({
  hots,result,
})

export default Reducers

上面代码中,hots函数收到名为SEARCH_HOT的 Action 以后,就返回一个新的 State,作为热门搜索的结果。

src/store/index.js中,开发环境下,引入了中间件redux-loggercreateLogger,在浏览器console可以观察到每次reducer的结果,如下:

src/reducer/index.js

import { combineReducers } from 'redux'
//...
import  search from './search'

const reducers = combineReducers({
  //...
  search,
})

export default reducers

Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State,然后View发生变化。 combineReducers将多个拆分的reducer合并。