网络营销网站推广的基本策略,打广告专用图,网站租用服务器多少钱,手机网站建设图本文的目的很简单#xff0c;介绍Redux相关概念用法 及其在React项目中的基本使用 假设你会一些ES6、会一些React、有看过Redux相关的文章#xff0c;这篇入门小文应该能帮助你理一下相关的知识 一般来说#xff0c;推荐使用 ES6ReactWebpack 的开发模式#xff0c;但Webpa… 本文的目的很简单介绍Redux相关概念用法 及其在React项目中的基本使用 假设你会一些ES6、会一些React、有看过Redux相关的文章这篇入门小文应该能帮助你理一下相关的知识 一般来说推荐使用 ES6ReactWebpack 的开发模式但Webpack需要配置一些东西你可以先略过本文不需要Webpack基础 入门只是一些基础概念和用法的整理更完整的内容推荐去看看文档英文中文 不过我个人认为官方文档的例子相对来说太复杂了很难让新手马上抓住重点 官方的例子正统且联系业务不同类型的操作或数据放在不同文件中很规范但也很绕所以本文使用的例子非常简单且直接放在一个文件中 以便于理解 一、Flux Flux是一种概念思想或者说是一种应用架构 根据它的概念一个应用中的数据流动应是单向的且应用中的所有数据保存在一个位置数据变化时保证视图也同步变化保证了数据和视图的状态是一一对应起来的 此应用应该分为四层 view层应用的视图页面的数据展示action层视图发出的某些动作比如点击事件dispatcher层派发器接收action并处理这些动作更新数据store层存放应用的数据数据更新后提醒view层更新视图 它的概念思想可能一时半会理解不了没关系过段时间就好了 二、Redux 上面说到Flux只是一个思想我们可以根据这个思想来自己实现出一个技术方案来解决问题 是要解决什么问题呢 在使用React的过程中在组件间通信的处理上我们用了回调的方式如果组件层级很深不同组件间的数据交流就会导致回调及其触发的函数非常多代码冗杂 需要一个状态管理方案方便管理不同组件间的数据及时地更新数据 而Flux思想中的Store层切合了这个问题 1. 什么是Redux Redux是受Flux启发实现的一个技术方案可以认为它是Flux的产物但它并没有沿用Flux所有的思想 主要区别是Flux的派发器dispatcherRedux认为使用派发器就得增加事件订阅/发布的规则倒不如直接用函数调用的方式来得实在简单而统一所以就将处理action的任务交给了store层直接调用这个对象的dispatch方法 2. 什么时候用Redux Redux说简单简单因为也就几个API理解好概念就好用了说复杂也复杂因为它将一个应用分成了不同部分action、处理action、store数据等在正规的项目中是推荐将各部分区分到不同文件中的如官方的例子文件数量很多可能会比较难管理当然细粒化了也就减少了耦合度。最后还要加个操作把Redux的数据更新给React组件如果用了React 在大多数情况下Redux是不需要用的如UI层非常简单没有太多互动的 用户的使用方式非常简单用户之间没有协作不需要与服务器大量交互也没有使用 WebSocket视图层View只从单一来源获取数据 而在多交互多数据源的时候可以考虑使用 用户的使用方式复杂不同身份的用户有不同的使用方式比如普通用户和管理员多个用户之间可以协作与服务器大量交互或者使用了WebSocketView要从多个来源获取数据 在需要管理复杂组件状态的时候可以考虑使用 某个组件的状态需要共享某个状态需要在任何地方都可以拿到一个组件需要改变全局状态一个组件需要改变另一个组件的状态 3. 开始用Redux 上面讲了那么多字还是看代码来得实在 这里先纯粹讲Redux毕竟它和React是没啥关系的 首先是环境配置基本上都会使用ES6所以Babel的支持是必须的 然后是Redux的支持如果使用Webpack打包编译就用npm安装个redux包 这里采用直接在浏览器引入的方式使用 这个库 bodydiv idbox/divscript typetext/javascript src../lib/react.js/scriptscript typetext/javascript src../lib/react-dom.js/scriptscript typetext/javascript src../lib/redux.min.js/scriptscript typetext/javascript src../build/reduxStart.js/script/body 最后build里的为demo代码用babel编译之后的es5文件 在全局之中有Redux这个对象取其中的几个属性来用 let {Component} React;
let {render} ReactDOM;
let {createStore, combineReducers} Redux; 3.1 Redux需要一个store来存放数据 这个store就由createStore创建 3.2 需要定义各个操作是什么即action 通常来说它是一个对象包含type属性表示是什么操作以及其他属性携带一些数据 它可能长这样子建议是遵循官方的 一些规范 let upAction {type: UP}; 我们不止会传type还会传一些值如果传不同的值就let一次就太冗杂了一般来说就会用一个方法代替 let upAction function(value) { return {type: up,value};
}; 3.3 需要定义怎么处理操作在redux中它被称作reducer 为什么把这种操作称作reducer呢 redux引入了JS数组reduce方法的思想JS的reduce长这样 var arr [1, 2, 3, 4];var num arr.reduce((a, b) { return a b;
});num // 10var num arr.reduce((a, b) { return a b;
}, 5);num // 15 当然了只是看起来像实际上差别挺大的redux的reducer看起来像这样 let upReducer function(state 0, action) { switch (action.type) { case up: return state action.value; default: return state;}
}; 它是一个函数接收两个参数第一个参数为数据即某个状态state第二个参数为action操作对象 为了切合store中数据与view中视图是一一对应的reducer规定需始终返回新的state数据不能直接在原有state中修改 并且建议在匹配不到action的时候始终返回默认的state状态且建议在第一个参数中初始化默认的state值 3.4 在创建store的时候绑定reducer redux基本上把所有操作都给了store所以大部分方法都是用store来调用的 其实你也可以认为Flux中的派发器dispatcher就是在里面自动绑定的 let store createStore(reducer);// let store createStore(reducer, 10); 如上创建store的时候传入reducer可以接收第二个参数表示reducer使用的默认值 3.5 视图发出action动作 在某个时刻发出了这些动作 store.dispatch(upAction(10));
store.dispatch(upAction(100)); 3.6 使用store.getState()获取store中的数据 3.7 动作发出后reducer匹配动作更新store中的数据视图view层使用subscribe监听数据的改变 store.subscribe(() console.log(store.getState())); 来看一下完整的代码 let {Component} React;
let {render} ReactDOM;
let {createStore, combineReducers} Redux;let upAction function(value) { return {type: up,value};
}let upReducer function(state 0, action) { switch (action.type) { case up: return state action.value; default: return state;}
};let store createStore(upReducer, window.__REDUX_DEVTOOLS_EXTENSION__ window.__REDUX_DEVTOOLS_EXTENSION__());console.log(store.getState());store.subscribe(() console.log(store.getState()));store.dispatch(upAction(10));
store.dispatch(upAction(100)); 注意上面createStore中第二个参数是用于Redux DevTool的配置即这个东西 使用这个工具可以便于开发 看看上面代码的输出 初始获取到的值为0两次action后分别更新相关的数据状态。如果加上初始默认值10 let store createStore(upReducer, 10, window.__REDUX_DEVTOOLS_EXTENSION__ window.__REDUX_DEVTOOLS_EXTENSION__()); 3.8 使用多个reducer时使用Redux的combineReducers方法 action当然不会只是up可能是down这时可以直接用switch语句切换但如果action不是这里增减的操作放在一起就有点乱套了 所以需要定义多个reducer但createStore方法只接收一个reducer所以就需要整合多个reducer为一个再统一传入 它看起来像这样 let reducer combineReducers({upReducer, downReducer});// let reducer combineReducers({// upReducer: upReducer, // downReducer: downReducer// }); 接收一个reducer组成的对象属性表示该reducer对应的state名如state.upReducer值表示这个reducer 当然这个方法我们可以自己定义看起来是这样 let myCombineReducers function(reducerObj) {let newState {}; return function(state {}, action) { for (let item in reducerObj) {newState[item] reducerObj[item](state[item], action);} return newState;}
}; 其实就是遍历reducer组返回一个统一的新的reducer且新的reducer中返回一个新的state 加上个down操作来看看完整代码 let {Component} React;
let {render} ReactDOM;
let {createStore, combineReducers} Redux;let upAction function(value) { return {type: up,value};
}
let downAction function(value) { return {type: down,value};
}let upReducer function(state 0, action) { switch (action.type) { case up: return state action.value; default: return state;}
};let downReducer function(state 0, action) { switch (action.type) { case down: return state - action.value; default: return state;}
};let reducer combineReducers({upReducer, downReducer});let store createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ window.__REDUX_DEVTOOLS_EXTENSION__());console.log(store.getState());store.subscribe(() console.log(store.getState()));store.dispatch(upAction(10));
store.dispatch(upAction(100));
store.dispatch(downAction(10));
store.dispatch(downAction(100)); 给reducer设个初始值要注意的是这个初始值是针对整个state的 如果只有一个reducer那reducer函数中的state就是这个state 如果用combineReducer整理了多个reducer,那各个reducer函数中的state是整个state中的reducer同名属性的值 let reducer combineReducers({upReducer, downReducer});let store createStore(reducer, {upReducer: 10, downReducer: 10}, window.__REDUX_DEVTOOLS_EXTENSION__ window.__REDUX_DEVTOOLS_EXTENSION__()
); 如上代码定义了初始值看看执行结果 四. 在React中使用Redux Redux是一个独立的技术方案我们将它运用到React项目中 接下来的问题主要有三个 如何将store中的数据同步给React组件如何让React组件调用Redux的dispatch方法上面两个 直接点就是在React组件中调用Redux的subscribe方法来监听同步数据再在某个时机调用dispatch即可 但官方并不建议使用subscribe这个方法而是建议使用封装好的另一个库 React-Redux 4.1 引入库 与引入Redux类似你可以使用Webpack引入包或浏览器直接引入这个库 然后在全局window下可以获取到这个对象取一些用到的属性如 let {Provider, connect} ReactRedux; 4.2 先定义一个有增长操作的React组件 class Increase extends Component {constructor(props) {super(props);}componentWillReceiveProps(nextProps) {console.log(nextProps);}increase() {let {dispatch} this.props;dispatch({type: up});}render() { return p onClick{this.increase.bind(this)}increase: {this.props.number}/p }
} 组件定义了一个action即点一次执行一次增长increase函数里面调用dispatch方法发出action先看看其他东西 4.3 定义一个reducer function couterUp(state {number: 100}, action) { switch (action.type) { case up: return {number: state.number 1}; default: return state;}
} 4.4 创建一个store let store createStore(couterUp, window.__REDUX_DEVTOOLS_EXTENSION__ window.__REDUX_DEVTOOLS_EXTENSION__()); 4.4 使用ReactRedux的connect方法 要将Redux中的数据同步给React需要用到这个方法 它看起来像是这样子 let APP connect(mapStateToProps,mapDispatchToProps
)(Increase); 可以把它看成是一个中间件首先接收几个参数完成配置阶段然后传入React组件包装成一个新的东东它并没有直接修改Increase组件 而一般来说一般来说会传入两个参数支持四个参数顾名思义 第一个参数类型为函数 如果不传或置入undefined或null则表示不需要进行数据更新否则表示将store中的数据通过props的形式传给React组件 第二个参数类型为函数 如果不传或置入undefined或null则表示将React-Redux中默认的dispatch方法传给React组件否则表示将redux中的dispatch发出动作通过props的形式传给React组件 注意到上面的React组件代码中通过props获取到了dispatch方法然后自行发出动作 increase() {let {dispatch} this.props;dispatch({type: up});} 如果要这样做mapDispatchToProps 这里就不传入了即 let APP connect(mapStateToProps
)(Increase); 用回常见的方式在React组件中改一改直接从props中获取某个dispatch的发出动作 render() { return p onClick{this.props.increase}increase: {this.props.number}/p} 同时修改两个都传入 let APP connect(mapStateToProps,mapDispatchToProps
)(Increase); 4.5 mapStateToProps 和 mapDispatchToProps 我们定义一下这两个参数函数它看起来长这样 function mapStateToProps(state) { return {number: state.number};
}function mapDispatchToProps(dispatch) { return {increase: () dispatch({type: up})};
} mapStateToProps 中第一个参数为一个对象表示store中整体的state数据 当然第一个参数也可以为函数也可以接收第二个参数表示自身拥有的属性ownProps具体可以看API 最后它返回了一个新的对象表示要传给React组件的数据 与mapStateToProps类似mapDispatchToProps 也可以接收两个参数 第一个表示当前的dispatch方法第二个表示自身拥有的 propsDispatch方法即类似省略参数传给字组件的默认dispatch方法 最后它返回了一个action发出动作一个函数传给React组件调用 4.6 使用Provider 基本好了只差一步将connect包装组件后生成的新东东与实际页面联系起来 使用ReactRedux提供的Provider /它看起来是这样 render( Provider store{store}APP //Provider,document.getElementById(box)
); 使用store属性传入上面的store对象 在children中置入有connect生成的APP组件注意这里只能包含一个父层 如果向其中传入属性如 APP nameapp / 那么mapStateToProps中的第二参数ownProps就可以拥有这个name属性 看一下运行结果 4.7 多个React组件中的使用 上面说的是单个React组件中的使用实际使用中会有多个组件 多个组件的使用类似单个只不过需要注意两点 Provider /中只能包含一个父级mapStateToProps中第一个参数是指整体store中的数据 下面以两个组件的栗子看看如何实现 4.7.1 首先定义两个组件一增一减 class Increase extends Component {constructor(props) {super(props);}componentWillReceiveProps(nextProps) {console.log(increase: , nextProps);}render() {return p onClick{this.props.increase}increase: {this.props.number}/p}
}class Decrease extends Component {constructor(props) {super(props);}componentWillReceiveProps(nextProps) {console.log(decrease: , nextProps);}render() {return p onClick{this.props.decrease}decrease: {this.props.number}/p}
} 4.7.2 定义对应的两个reducer function couterUp(state {number: 100}, action) { switch (action.type) { case up: return {number: state.number 1}; default: return state;}
}function counterDown(state {number: -100}, action) { switch (action.type) { case down: return {number: state.number - 1}; default: return state;}
} 4.7.3 创建store let couter combineReducers({couterUp,counterDown
});let store createStore(couter,{couterUp: {number: 10}},window.__REDUX_DEVTOOLS_EXTENSION__ window.__REDUX_DEVTOOLS_EXTENSION__()
); 4.7.4 创建连接两个组件对应的两个mapStateToProps 和 mapDispatchToProps 注意state为整个store中的state取值要取各reducer同名属性如 state.couterUp function mapStateToProps_1(state) { return {number: state.couterUp.number};
}function mapDispatchToProps_1(dispatch) { return {increase: () dispatch({type: up})};
}function mapStateToProps_2(state, props) { return {number: state.counterDown.number};
}function mapDispatchToProps_2(dispatch) { return {decrease: () dispatch({type: down})};
} 4.7.5 各组件用connect包装 let APP_1 connect(mapStateToProps_1,mapDispatchToProps_1
)(Increase);let APP_2 connect(mapStateToProps_2,mapDispatchToProps_2
)(Decrease); 4.7.6 置入Provider /中 注意只能有一个父级所以得先简单包装一层 let APP () ( divAPP_1 /APP_2 nameAPP_2//div);render( Provider store{store}APP //Provider,document.getElementById(box)
); Good ! 完成了看看结果 4.7.7 再看connect方法剩余的两个参数 connect方法接收可接收四个参数上面已经谈到了前两个后两个不那么常用 第三个参数这里不多说[mergeProps(stateProps, dispatchProps, ownProps): props] (Function) 第四个参数[options] (Object) 这个options中有如下几个属性 pure: true(默认)|false 表示是否在调用connect前三个参数的函数方法之前先检测前后store中的值是否改变改变才调用否则不调用areStatesEqual: 函数当pure为true时调用这个函数检测是否相等返回true|false表示是否相等默认以严格相等来判断前后值是否相等areOwnPropsEqual: 类似areStatesEqual只不过它默认是用不严格相等来判断areStatePropsEqual: 类似areStatesEqual只不过它默认是用不严格相等来判断areMergedPropsEqual: 类似areStatesEqual只不过它默认是用不严格相等来判断 来看个例子现在要手动的定义这个参数 针对Decrease在减1时直接返回了false let APP_2 connect(mapStateToProps_2,mapDispatchToProps_2, null,{pure: true,areStatesEqual: (next, prev) {console.log(next.counterDown, prev.counterDown); return next.counterDown.number prev.counterDown.number;}}
)(Decrease); 可以看到减1的操作并没有传给Decrease组件页面没有更新 顺便看看有connect包装后的组件 4.7.8 在React-Redux中使用异步 因Redux中操作的执行是同步的如果要实现异步比如某个操作用来发个异步请求获取数据就得引入中间件来处理这种特殊的操作 即这个操作不再是普通的值而是一个函数如Promise异步通过中间件的处理让Redux能够解析 先修改上面的栗子在Increase组件中不再是每次增加1而是根据action中的value来指定比如 function mapDispatchToProps_1(dispatch) { return {increase: () dispatch({type: up,value: 10})};
} function couterUp(state {number: 100}, action) { switch (action.type) { case up: return { // number: state.number 1number: state.number action.value}; default: return state;}
} 这里定义了value是10但假如value的值得由一个异步的请求才得出呢要如何放进去 使用Redux提供的中间件applyMiddleware let {createStore, combineReducers, applyMiddleware} Redux; 这只是基础的中间件apply函数它帮助Redux将中间件包装 现在来模拟一个异步请求 function mapDispatchToProps_1(dispatch) { return { // increase: () dispatch({// type: up,// value: 10// })increase: () dispatch(fetchIncreaseValue(redux-ajaxTest.php))};
} 可一看到dispatch中的action是一个函数这个调用返回的还是一个函数而Redux默认只支持对象格式的action所以这样会报错 这里的fetchIncreaseValue看起来像这样 function fetchIncreaseValue(url) { return function(dispatch) { return $.get(url).then(re {re JSON.parse(re);console.log(re);dispatch({type: up,value: re.value});})}
} 而请求后台后返回值 ?php echo json_encode(array(value 100));? 可以看到异步获取数据之后才执行dispatch发出操作这里需要一个dispatch关键字 为了拿到这个关键字得和thunkMiddleware搭配使用让这个方法能够在内层函数中使用当然你也可以再搭配其他中间件 如果使用Webpack打包就安装好 redux-thunk 包再 import 进来 这里直接引入到浏览器中引入这个库然后直接使用注意这里没有 {} let thunkMiddleware window.ReduxThunk.default; 然后在创建store的时候传给redux的applyMiddleware即可 let store createStore(couter,{couterUp: {number: 10}},applyMiddleware(thunkMiddleware),window.__REDUX_DEVTOOLS_EXTENSION__ window.__REDUX_DEVTOOLS_EXTENSION__()
); 官方给的例子太复杂了不过还是去看看吧我这里抽出了主要的部分 先来看看结果 使用这个Redux Dev Tool就得在createStore中配上最后一个参数而createStore自身的某个参数又能给reducer设置初始值且applyMiddleware也是在参数中定义 所以要注意的是 如果用了这个Redux Dev Tool就要保证applyMiddleware在第三个参数 let store createStore(couter, // {},applyMiddleware(thunkMiddleware),window.__REDUX_DEVTOOLS_EXTENSION__ window.__REDUX_DEVTOOLS_EXTENSION__()
); 类似这样省略第二个初始值参数是会报错的 把注释去掉放上一个空的初始即可或者不用这个Dev Tool let store createStore(couter,applyMiddleware(thunkMiddleware)
); 可以去看看其他的Dev Tool 原文地址http://www.cnblogs.com/imwtr/p/6327384.html.NET社区新闻深度好文微信中搜索dotNET跨平台或扫描二维码关注