Ahri-珊
在day32中我将总结之前写音乐播放器的经验,以实现音乐播放器的搜索功能为例子,告诉大家如何快速编写一个react+redux的项目(当然你需要以及看过react和redux的相关知识了)。
思路
1.点击搜索按钮,将搜索的值作为参数传给获取搜索结果的函数,同时通过connect将组件的dispatch方法传给这个函数。
2.使用fetch获取搜索结果,将处理后的搜索结果作为action对象的值传给reducer。
3.reducer函数根据action将搜索结果更新到store
4.搜索结果展示组件根据store的值渲染数据
项目框架
1.使用react的官方脚手架create-react-app创建一个基本的react项目框架
2.修改src中的index入口文件
修改后代码
import React from 'react';
import ReactDOM from 'react-dom';
import {Router,Route,IndexRoute,hashHistory} from 'react-router';
import { Provider } from 'react-redux'; // 引入Provider将store的数据共享给子组件
import 'bootstrap/dist/css/bootstrap.css';
import './index.css';
import finalCreateStore from './store/configureStore' ; // 引入增强后的store
import reducer from './reducers' // 引入reducers集合
// 引入子组件(自定义)
import App from './App'; // 引入导航栏
import Lrc from './Lrc'; // 引入歌词展示组件
import Demo from './Demo'; // 引入卡片容器
import registerServiceWorker from './registerServiceWorker';
const store = finalCreateStore(reducer);
class Nav extends React.Component{
render(){
return(
<Provider store={store}>
<div>
<App/>
{this.props.children}
</div>
</Provider>
)
}
}
ReactDOM.render((
<Router history={hashHistory}>
<Route path="/" component={Nav}>
// 主页面
<IndexRoute component={Demo}/>
// 其它页面
<Route path="/lrc" component={Lrc}/>
</Route>
</Router>
),document.getElementById('root')
);
registerServiceWorker();
3.补充index文件中引入的文件configureStore,这个文件用于强化store生成方法,让我们能看到store每次的变化
在src中新建文件夹store,将configureStore.js放在store文件夹下
configureStore.js代码
import thunk from 'redux-thunk' // redux-thunk 支持 dispatch function,并且可以异步调用它
import createLogger from 'redux-logger' // 利用redux-logger打印日志
import { createStore, applyMiddleware, compose } from 'redux' // 引入redux createStore、中间件及compose
// import DevTools from '../DevTools'引入DevTools调试组件
// 调用日志打印方法
const loggerMiddleware = createLogger
// 创建一个中间件集合
const middleware = [thunk, loggerMiddleware]
// 利用compose增强store,这个 store 与 applyMiddleware 和 redux-devtools 一起使用
const finalCreateStore = compose(
applyMiddleware(...middleware),
//DevTools.instrument(),
)(createStore)
export default finalCreateStore
4.补充index文件中引入的文件reducers,这个文件夹中有count.js和index.js两个文件,count用于初始化state数据并存放reducer函数,index.js用于合并reducers(我只有一个reducer所以没有用)
在src新建文件夹reducers,将count.js和index.js放在该目录下
count.js代码
// reducers/count.js
import { GETSUCCESS } from '../constants' // 引入action类型常量名
// 初始化state数据
const initialState = {
data: [{
id:209326,
img:"http://p1.music.126.net/BZNpKSKkPTTv5ZnxdYAdUQ==/5850501371402948.jpg",
lrc:"",
name:"旅行的意义",
singer:"陈绮贞",
time:"4:4"
}]
}
// 通过dispatch action进入
export default function update(state = initialState, action) {
// 根据不同的action type进行state的更新
switch(action.type) {
case GETSUCCESS:
return Object.assign({}, state, { data: action.data })
default:
return state
}
}
index.js代码
// reducers/index.js
import { combineReducers } from 'redux' // 利用combineReducers 合并reducers
import update from './count' // 引入update这个reducer
export default combineReducers({
update,
})
5.补充reducers/count.js中引入的constantes文件,这个文件用于定义action类型常量名
在src目录下新建constantes.js
constantes.js代码
// action常量
export const GETSUCCESS = 'GETSUCCESS'
6.在src目录下新建actions文件夹,这个文件夹中有一个count.js文件,这个文件用于处理组件发出的请求,将请求结果(action)传递给reducer函数
count.js代码
import { GETSUCCESS } from '../constants' // 引入action类型名常量
import 'whatwg-fetch' // 可以引入fetch来进行Ajax
// 这里的方法返回一个action对象
export const getSuccess = (json) => {
return {
type: GETSUCCESS,
data
}
}
好啦,到这里基本的架构就答好了,我们把其中需要的一些依赖(react-router3.0.0、redux、react-redux、bootstarp、redux-thunk、redux-logger)安装好后就可以开始写啦
功能实现
1.编写组件
搜索框:搜索想要的歌曲
组件代码
import React, { Component } from 'react';
import {Link} from 'react-router';
import { connect } from 'react-redux'; // 引入connect
import { fetchPostsIfNeeded } from './actions/count' //引入count中的一个函数,这个函数用于获取歌曲信息
import './App.css';
class App extends Component {
constructor(props) {
super(props)
this.state = {
value:'陈绮贞',
class: 'collapse navbar-collapse',
class1: 'navbar-form navbar-right',
liked: true
}
}
//获取搜索框的值
handleChange=(event)=>
{
this.setState({value: event.target.value});
}
//控制导航栏的显示与隐藏
hand=(event)=>
{
if(this.state.liked)
this.setState({liked: false,class:'',class1:'nav navbar-nav search'});
else
this.setState({liked: true,class:'collapse navbar-collapse',class1:'navbar-form navbar-right'});
}
render() {
const { fetchPostsIfNeeded } = this.props;
var value = this.state.value;
return (
<nav className="navbar navbar-default App">
<div className="container-fluid">
<div className="navbar-header">
<a className="navbar-brand" href="">Ahri-珊
</a>
<img src='favicon.ico' className="App-logo" alt="logo" />
<button className="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse" onClick={this.hand}>
<span className="icon-bar"></span>
<span className="icon-bar"></span>
<span className="icon-bar"></span>
</button>
</div>
<div className={this.state.class}>
<ul className="nav navbar-nav">
<li className="dropdown"><Link to="/" onClick={this.hand}>Demo</Link></li>
<li className="dropdown"><Link to="/lrc" onClick={this.hand}>Lrc</Link></li>
</ul>
<div className={this.state.class1}>
<span><input type="text" value={value} onChange={this.handleChange} /></span>
<Link to="/"><button onClick={() => fetchPostsIfNeeded(this.state.value)}><span onClick={this.hand}>搜索</span></button></Link>
</div>
</div>
</div>
<div>{this.props.children}</div>
</nav>
);
}
}
//从store中拿到data数据(我并没有用,只是为了举个又拿数据,又发请求的例子)
const getList = state => {
return {
lists: state.update.data
}
}
//将data数据绑定到组件的this.props,并让组件可以使用fetchPostsIfNeeded函数
export default connect(
getList,
{fetchPostsIfNeeded}
)(App)
歌曲信息展示卡片:显示歌曲封面、歌名、歌手名、歌曲时长,还有播放按钮。
组件代码
class Card extends React.Component{
constructor(props) {
super(props);
this.state = {};
}
render(){
return(
<div className="col-sm-3 col-xs-6 card" >
<div className="card_box">
<Link to="/lrc"><img src={this.props.img} alt={this.props.name} /></Link>
<div className="card_content">
<p>{this.props.singer}</p>
<Link to="/lrc"><h3>{this.props.name}</h3></Link>
<div>
<Link to="/lrc"><span className="glyphicon glyphicon-play" ></span></Link>
<span className="time">{this.props.time}</span>
<span className="glyphicon glyphicon-share"></span>
</div>
</div>
</div>
</div>
)
}
}
卡片容器:装卡片的大div,还要给卡片提供信息
实现思路
这个容器要给卡片提供歌曲信息,歌曲信息需要从store拿。
组件代码
import React from 'react';
import Card from './Card';
import { connect } from 'react-redux'; // 引入connect
class Demo extends React.Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
const { data } = this.props;
return (
<div className="box">
{data.map((key,index) =>
<Card key={index.toString()} url={key.url} name={key.name} time={key.time} lrc={key.lrc} img={key.img} id={key.id} singer={key.singer} />
)}
</div>
);
}
}
// 从store拿到data数据
const getData = state => {
return {
data: state.update.data
}
}
//将拿到的数据传给组件的this.props
export default connect(
getData
)(Demo)
2.在constants.js中定义请求获取搜索歌曲的action变量名
export const GETSUCCESS = 'GETSUCCESS'
3.在actions/count.js中引入这个action类型名常量并编写这个action和搜索框所用到的fetchPostsIfNeeded函数
count.js代码
import { GETSUCCESS } from '../constants' // 引入action类型名常量
import 'whatwg-fetch' // 可以引入fetch来进行Ajax
// 这里的方法返回一个action对象
//处理歌曲信息
export const getSuccess = (json) => {
return {
type: GETSUCCESS,
data
}
}
//获取歌曲信息
function fetchPosts(keyword) {
//服务器搜索方法地址
var url = 'http://youxinyu.me:3000/search?keywords='+keyword;
return dispatch => {
return fetch(url, {
method: 'GET',
mode: 'cors',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
})
.then((res) => { console.log(res.status); return res.json() })
.then((data) => {
var action = getSuccess(data);
dispatch(action)
})
.catch((e) => { console.log(e.message) })
}
}
//搜索歌曲
export function fetchPostsIfNeeded(keyword) {
return (dispatch, getState) => {
return dispatch(fetchPosts(keyword))
}
}
4.在reducers/count.js文件中的初始化state,给它一个变量data存放我们搜索到的数据,并在update函数里更新state的值
count.js代码
// reducers/count.js
import { GETSUCCESS } from '../constants' // 引入action类型常量名
// 初始化state数据
const initialState = {
data: [{
id:209326,
img:"http://p1.music.126.net/BZNpKSKkPTTv5ZnxdYAdUQ==/5850501371402948.jpg",
lrc:"",
name:"旅行的意义",
singer:"陈绮贞",
time:"4:4"
}]
}
// 通过dispatch action进入
export default function update(state = initialState, action) {
// 根据不同的action type进行state的更新
switch(action.type) {
case GETSUCCESS:
return Object.assign({}, state, { data: action.data })
default:
return state
}
}
好啦,到这里,我们就写好搜索功能啦,再加功能就再做一下功能实现这些步骤就可以了~
查看完整代码与示例
看代码点这里~
在线演示点这里~
Comments