内容简介:最近,由于接触了React的项目,所以开始慢慢去学习关于React全家桶的一些内容,其中有一块内容用到的较为频繁,于是也开始恶补这方面的知识如标题所示,这篇文章就是关于那么,闲言少叙,还是从头开始讲起吧
最近,由于接触了React的项目,所以开始慢慢去学习关于React全家桶的一些内容,其中有一块内容用到的较为频繁,于是也开始恶补这方面的知识
如标题所示,这篇文章就是关于 redux & react-redux 在实际工作中是如何使用的
那么,闲言少叙,还是从头开始讲起吧
它们是谁?
- 个人认为它是一个专门用来 创建仓库 的东东,你可以叫它为 store
- 通过redux库里的 createStore 方法来创建仓库
- 值得傲娇的是redux并不像vuex那样,必须依赖vue而使用,单独拿出来也是出类拔萃的
那么,问题来了?
- react-redux又是做甚的?
- 首先,从名字上来看,就应该能了解,这是结合react与redux一起来使用的
- 其次,是 重点 ,它是用来连接 react组件 和 store仓库 的桥梁
OK,大致知道它们的作用了,那么直接开始搞起
无安装不建仓
// 安装 redux 和 react-redux npm i redux react-redux --save 复制代码
再常规不过的结构
如何用之
我们先以最简单的组件为例,看看 组件 和 仓库 是如何通过react-redux建立连接的
在components文件夹下创建一个Counter.js组件,就是为了做个加减的组件
// components/Counter.js文件 import React, { Component } from 'react'; export default class Counter extends Component { constructor() { super(); // 继承者们 this.state = { number: 0 }; } render() { return (<div> <button>-</button> {this.state.number} <button>+</button> </div>) } } 复制代码
草草完事,就这样写完了Counter组件了,下面在 index 主入口文件里引入一下
// index.js入口文件 import React from 'react'; import { render } from 'react-dom'; import Counter from './components/Counter'; // 开始渲染了 // render方法第一个参数是要渲染的组件,第二个是目标节点 render( <Counter />, document.getElementById('root') ); 复制代码
忘记说了,为了方便,我是用 create-react-app脚手架 创建的项目,所以先 全局安装 一下,然后再创建项目并启动该项目
npm i -g create-react-app // 创建项目 create-react-app 项目名 // 启动 npm run start 复制代码
项目跑起来后看到的应该是这个样子的
下面就针对这个Counter组件来亲身使用一下redux & react-redux在项目里的使用情况吧
使用redux和react-redux
store目录下的结构就如最开始看到的,下面我再分析一下里面的内容都分别是有何用处的
- actions目录
- 这里是放一些方法,就是在组件里方便调用的 方法 (更多的是请求数据的情况)
- 你也可以把这里当成是存数据用的地方也OK
- reducers目录
- 这里就是用来取数据的地方了
- action-types.js
- 定义各种组件需要的类型(如: INIT_DATA,GET_DATA,CHANGE_TYPE啊)
- index.js
- 这里是真正用来创建仓库的地方
好了,那就赶紧写起来吧
The first, 先来定义action-types
// store/action-types.js // Counter组件用到的types export const ADD = 'ADD'; export const MINUS = 'MINUS'; 复制代码
action-types里定义的都是根据各组件的需要才定义的类型常量,属于一一对应的一种关系
The second, 再写一下关于actions动作的(就是应该做什么事)
// store/actions/counter.js // 也是有固定套路的 import * as types from '../action-types'; // 返回一个包含不同类型的对象 export default { add(count) { // 加数字 return {type: types.ADD, count}; }, minus(count) { // 减数字 return {type: types.MINUS, count}; } } 复制代码
由于Counter组件需要处理加减操作,所以在actions里的counter.js文件里来写下对应的执行方法来
Finnally,修改reducer并处理此动作
// store/reducers/counter.js // 常规写法, reducer是个函数 // 引入你组件需要的type import * as types from '../action-types'; // 初始化状态 const initState = { number: 0 }; function counter(state = initState, action) { switch(action.type) { case types.ADD: return { number: state.number + action.count }; case types.MINUS: return { number: state.number - action.count }; default: return state; } return state; } export default counter; 复制代码
reducer为什么这样写?
- initState状态是为了在组件加载的时候 第一次 仓库里并 没有state ,所以为了防止undefined的报错情况,先给一个 初始化状态 的
- initState虽然是个初始化状态,但这其实就是你组件所需要的数据结构,所以需要好好设计设计
- action就是在actions目录里对应文件传递过来的状态,它长这个样子
{type: 'ADD', count}
- counter里两个参数,state代表着 过去的状态 ,action代表的是 新的状态
- 之所以叫做 reducer 也是借鉴了 数组的reduce方法 ,里面的两个参数和现在有异曲同工之妙
- 当然最重要的是,这个函数做的 第一步 就是把 state 状态 返回出去
说最后有点为时过早了,我们还没有创建仓库呢
而且在创建之前还要整合一下reducer,因为这才一个counter,真实项目里还会根据不同的组件写出来不同的reducer呢
所以为了不冲突,我们利用redux提供的 combineReducers 方法来 合并 它们
合并reducer
在reducers里创建一个index.js文件,用来合并reducer
// combineReducers方法就是专门用来合并不同的reducer的 import { combineReducers } from 'redux'; // 引入关于Counter组件的reducer import counter from './counter'; // 引入其他的reducer import list from './list'; // 合并开始 export default combineReducers({ counter, list // 其他的reducer }); 复制代码
Let's Go 建仓吧
来到store目录下面的index.js中
// 引入redux提供的createStore方法来创建仓库 import { createStore } from 'redux'; // 引入所有用到的reducer import reducer from './reducers'; const store = createStore(reducer); export default store; 复制代码
准备起飞
好了,这样就把仓库创建完毕了,下面是最后的 连接 过程了,回到Counter组件里去修改一下
// components/Counter.js import React, { Component } from 'react'; +++ // react-redux提供了connect方法,它是个高阶函数 import { connect } from 'react-redux'; import actions from '../store/actions/counter'; +++ // export default不再直接默认导出Counter而是要写到下面,通过connect来实现高阶组件了(HOC) class Counter extends Component { render() { // 通过mapStateToProps和mapDispatchToProps // 将number状态还有add和minus方法都转化到了props属性上了 const { add, minus, number } = this.props; return (<div> <button onClick={() => minus(1)}>-</button> {number} <button onClick={() => add(2)}>+</button> </div>) } }; +++ const mapStateToProps = state => { console.log(state); // 长这样就是存的所有reducer:{counter: {number: 0}, list: {data: []}} return { number: state.counter.number }; }; const mapDispatchToProps = dispatch => { return { add: (n) => dispatch(actions.add(n)), minus: (n) => dispatch(actions.minus(n)) }; }; export default connect( mapStateToProps, mapDispatchToProps )(Counter); +++ 复制代码
写完上面的代码,就实现了把Counter组件与store仓库连接起来的操作了。最后的最后,我们就把index.js入口文件再修改一下,让所有组件都可以连接到store吧
奇迹即将发生
// index.js import React from 'react'; import { render } from 'react-dom'; import Counter from './components/Counter'; +++ // Provider是个组件,有容乃大包容万物,不过只能有一个子元素 import { Provider } from 'react-redux'; import store from './store'; +++ // 开始渲染了 // render方法第一个参数是要渲染的组件,第二个是目标节点 render( <Provider store={stroe}> <React.Fragment> {/* 如果有多个组件,就必须用React.Fragment组件来包裹,它不会产生多余的标签元素,和vue的template类似 */} <Counter /> </React.Fragment> </Provider>, document.getElementById('root') ); 复制代码
一切都安静了,看看最终的效果
看到这里我就把项目中使用redux及react-redux的过程叙述完毕了,当然上面的栗子也是比较简单的demo了
实际项目会比这样的操作起来稍微麻烦些,不过大同小异,学会举一反三都是能慢慢熟练运用起来的,下面再给大家看一下最近项目中我是如何书写的,写的不好仅供参考
只是为了让大家,慢慢领会,慢慢熟练,慢慢运用,慢慢越写越好
首先看一下效果图
此为链接地址,点击看看
下面我就跟大家说说我实现的思路(其实不难的,一起来看看吧)
付诸行动
看到上图所示,其实作为一个推荐列表来说,无非就是两个状态要做管理
- 切换分类(全城热搜、景点、美食、酒店tab的切换)
- 获取列表数据(根据不同分类获取对应数据)
先来写个action-types
在src目录下会创建一个store的文件夹,这里包含了actions,reducers,actions-types等需要的必备内容
// store/action-types.js // 获取category列表 export const GET_RECOMMEND_LIST = 'GET_RECOMMEND_LIST'; // 切换category的type export const CHANGE_CATEGORY = 'CHANGE_CATEGORY'; 复制代码
OK,需要的动作类型写好了,那就继续写actions了
actions对应动作(方法)
// store/actions/recommend.js // 引入所有的types常量 import * as types from '../action-types'; // 请求列表数据 import { getPoiList } from '../../api'; // 默认导出一个对象 export default { // 切换类型的方法() changeCategory(params, data) { let response; // 如果当前的数据已经存在仓库中就不再发送请求 if (!data[params.category]) { // 请求对应类型数据 response = getPoiList({ params }); } return { type: types.CHANGE_CATEGORY, category: params.category, key: params.keyword, response } }, // 获取列表数据的方法 getRecommendList({params}) { let data = getPoiList({ params }); return { type: types.GET_RECOMMEND_LIST, response: data, params } } } 复制代码
好了,写到这里就把actions里的方法都写完了,就这么两个而已
changeCategory不仅仅做了切换tab的处理,还在切换的时候进行了请求上的优化
reducers里的recommend
// store/reducers/recommend.js // 引入所有的types常量 import * as types from '../action-types'; // 初始化状态数据 const initState = { pathname: '', params: {}, loading: false, data: { hot: null, food: null, hotel: null, scenic: null, }, business: {}, category: 'hot', key: '景点' }; function recommend(state = initState, action) { switch(action.type) { case types.CHANGE_CATEGORY: // 从action里拿到传递过来的数据 const { category, response, key, params } = action; let newState = Object.assign({}, state, { data: { ...state.data, [action['category']]: response } }); return { ...state, ...newState }; case types.GET_RECOMMEND_LIST: const { pathname, params, response = {}, loading = false } = action; let newState = Object.assign({}, state, { data: { ...state.data, [category]: response // 将不同类型的数据一一对应起来,如food:{response:[]} } }); // 省略了一些处理广告数据的代码 return newState; default: return state; } return state; } export default recommend; 复制代码
这样就写完了reducer了,其实在写多了之后,大家能稍微有点感悟了
其实上面的一顿操作,用三句话来说就是
- 定义常量类型(actions-types)
- 存数据(actions)
- 取数据处理数据(reducers)
好了,最后的时刻到了,直接让我们去看看在组件上是如何使用的吧
recommend组件连接仓库
在和store同级的目录中有一个components文件夹,这里放置一些常用的公共组件,recommend就是其中之一
下面就直接看看recommend是如何写的
// components/recommend/index.js import React, { Component } from 'react'; import actions from '../../store/actions/recommend'; import { connect } from 'react-redux'; // 下面两个组件是用来下滑加载和loading的 import ScrollLoad from '../../common/ScrollLoad'; import Loading from '../../common/Loading'; // 列表数据渲染的组件 import List from './list'; import { CHANGE_CATEGORY, GET_RECOMMEND_LIST } from '../../store/action-types'; // 定义初始化的参数和tab列表数组方便渲染 const initParams = { keyword: '景点', category: 'hot' }; const navList = [ { type: 'hot', name: '全城热搜', keyword: '景点' }, { type: 'scenic', name: '景点', keyword: '风景名胜' }, { type: 'food', name: '美食', keyword: '餐饮' }, { type: 'hotel', name: '酒店', keyword: '酒店' } ]; class Recommend extends Component { // 在willMount生命周期的时候先请求列表数据 componentWillMount() { const { getRecommendList } = this.props; // 初始化数据 getRecommendList({ params: initParams }); } // 处理点击切换tab并回传给父组件修改keyword请求不同数据 handleClick = (event) => { const { changeCategory, response } = this.props; const { data } = response; const category = event.target.dataset.category; const keyword = event.target.dataset.keyword; const obj = { category, keyword }; // 修改对应类型 changeCategory(obj, data); } // 滑动操作处理 scrollHandle = ({ page, callback }) => { const { getRecommendList, response } = this.props; const { params, category, key, data } = response; let batch = data[category] && data[category].page + 1; const newParams = Object.assign({}, params, { batch }); newParams.category = category; newParams.keyword = key; // 加载数据 getRecommendList({ params: newParams }).then(() => { callback(); }); } render() { const { response } = this.props; const { params, data, category } = response; const categoryData = data[category]; // 分类数据 let totalcount = categoryData && categoryData.totalcount; // 列表项 const navItem = navList.map((item, i) => <li className={category === item.type ? 'active' : ''} data-category={item.type} data-keyword={item.keyword} onClick={this.handleClick} key={i}>{item.name} </li> ); return ( <div className='recommend'> <ul className='recommend-nav'>{navItem}</ul> {totalcount ? <ScrollLoad totalCount={totalcount} scrollHandle={this.scrollHandle}> <List response={response} /> </ScrollLoad> : <Loading />} </div> ); } } const mapStateToProps = state => { return { response: state.recommend } }; const mapDispatchToProps = dispatch => { return { changeCategory: (params, data) => dispatch(actions.changeCategory(params, data)), getRecommendList: ({params}) => dispatch(actions.getRecommendList({params}) } }; export default connect( mapStateToProps, mapDispatchToProps )(RecommendList); 复制代码
以上就是大致完成了recommend推荐组件加上连接仓库的过程了,下面再看两张图来给大家说明一下
上图是刚进入页面的时候,初始化阶段
下图为切换分类的操作对应仓库里数据上的变化
好了,好了,写了这么多,大家看的也累了,感谢大家耐心的观看了。
对于学习,我们都要永不止步,感谢大家了,再见
以上所述就是小编给大家介绍的《听说redux和react-redux在写项目中更配哦》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 听说,加缓存能提高性能?
- 听说mysql还会选错索引
- 听说玩这些游戏能提升编程能力?
- 深度介绍:???? 你听说过原生 HTML 组件吗?
- 听说你出了本书,花了多少钱?
- 骚年,你可听说过 spark-java?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
iOS软件开发揭密
虞斌 / 电子工业出版社 / 2011-5-1 / 79.00元
本书以严密的体系性提供了iPhone和iPad软件开发从入门到专家的系统性知识,并提供来源于真实项目的可重用商业代码。书中的每个实例都是项目经验的提炼,深入浅出地讲解iPhone和iPad软件开发的核心技术要点,基本涵盖了iOS软件开发在真实商业项目中所需要的所有主题,并将实例介绍的技术深度和超值的实用性结合在一起,成为本书的特色。 随书附赠的光盘中包含了书中大量案例的完整工程源代码,可以让......一起来看看 《iOS软件开发揭密》 这本书的介绍吧!