Redux教程2:連結React
序
通過前面的教程,我們有了簡單的環境,並且可以執行 Redux
的程式,也對 如何編寫Redux示例 有了初步的印象;
掌握了 使用Redux控制狀態轉移,繼而驅動 React 元件發生改變,這才是學習Redux的初衷。
本篇我們將 Redux 和 React 聯合起來,著重講解 redux-react
模組的使用;
1、編寫紅綠燈React元件
在原有的基礎上,我們編寫紅綠燈元件:
touch components/light/index.js components/light/index.less
在 components/light/index.js 中寫React程式碼,其結構非常簡單:
import React, { PropTypes, Component } from `react`
import { render } from `react-dom`
import classnames from `classnames`
import `./index.less`
class Light extends Component{
render(){
let color = this.props.light.color;
return(
<div className="traffic-light">
<span className={classnames(`light`,color)} />
</div>
)
}
}
Light.propTypes = {
light: PropTypes.object.isRequired
}
Light.defaultProps = {
light : {color:`red`,time:`4`}
}
export default Light
根據更改樣式類名(`red`、`green`、`yellow`),從而移動 sprite圖 產生燈變換的效果:
.traffic-light{
.light{
display: inline-block;
background: url(//lh3.googleusercontent.com/-YWLqWZXDYHU/VmWC7GHoAuI/AAAAAAAACgk/nXvEmSWAhQU/s800/light.png) no-repeat 0 0;
background-size: auto 100%;
overflow: hidden;
width:140px / 2;
height:328px / 2;
&.red{
background-position: 0,0;
}
&.yellow{
background-position: -78px , 0;
}
&.green{
background-position: -156px , 0;
}
}
}
修改 components/light/demo.js 檔案程式碼為:
import React, {Component, PropTypes} from `react`
import {render} from `react-dom`
import Light from `./index`
var color = `red`;
render(
<div id="traffic">
<Light color={color}/>
</div>,
document.getElementById(`demo`)
)
這樣就能通過 http://localhost:3000/light/demo 預覽這個元件了;
2、連結React和redux
有了React和之前的Redux,現在就要將兩者連結起來了。我們的目標是讓紅綠燈執行起來,就好比平時在十字路口看到的那樣;
2.1、建立示例檔案
再建立一個示例檔案,就不叫demo了,叫做redux
好了:
touch components/light/redux.js
之所以示例檔名稱為
demo.js
或redux.js
,是因為我在 webpack.config.js 中配置了,如果想用其他的檔名,只要依樣畫葫蘆就可以;
首先在 components/light/redux.js 中輸入最基本的腳手架程式碼,引入所需要的元件或模組:
import React, {Component, PropTypes} from `react`
import {render} from `react-dom`
import { Provider, connect } from `react-redux`
import { bindActionCreators } from `redux`
import * as LightActions from `../../actions/light/`
import lightStore from `../../stores/light/`
import Light from `./index`
// 宣告store
let store = lightStore();
2.2、建立容器React
繼而建立一個 App React類 ,作為總的容器,將上述的 Light 元件放入其中:
import React, {Component, PropTypes} from `react`
import {render} from `react-dom`
import { Provider, connect } from `react-redux`
import { bindActionCreators } from `redux`
import * as LightActions from `../../actions/light/`
import lightStore from `../../stores/light/`
import Light from `./index`
// 宣告store
let store = lightStore();
class App extends Component{
_bind(...methods){
methods.forEach((method)=>this[method] = this[method].bind(this));
}
constructor(){
super();
this._bind(`autoChange`,`handleClick`);
this.state = {
count : 0,
timeId : null
}
}
autoChange(){ // 自動更改紅綠燈
var _self = this;
// 這裡放置邏輯程式碼
this.state.timeId = setTimeout(function(){
// 遞迴呼叫,實現 setInterval 方法
_self.autoChange();
},1000);
}
handleClick(e){ // 用點選模擬紅路燈
if(this.state.timeId){
clearTimeout(this.state.timeId);
this.state.timeId = null;
} else {
this.autoChange();
}
}
render(){
// 通過connect 注入 redux 的 dispatch 方法
return (
<div id="traffic" onClick={this.handleClick}>
<Light light={`yellow`}/>
</div>
)
}
}
上面的程式碼還是個半成品,看不到效果;簡單描述一下上面的程式碼做了什麼:
- 定義
App
容器,將 Light 元件放在其render
方法中 -
constructor 方法引用了 _bind方法,方便一次性繫結
this
上下文,該方法來自文章 Refactoring React Components to ES6 Classes - handleClick 方法是純粹是為了演示,當使用者點選紅綠燈的時候,紅綠燈呼叫 autoChange方法 開始自動變換,使用者再次點選的時候就停止變換;
-
autoChange 方法用於紅綠燈狀態自動轉換的,這裡佔位;本質是使用
setTimeout
代替setInterval
實現;
2.3、連結React元件和Redux類
這是最為關鍵的一個步驟,
...
class App extends Component{
...
}
// 宣告 connect 連線
// 將 redux 中的 state傳給 App
function mapStateToProps(state){
return{
light:state
}
}
function mapDispatchToProps(dispatch){
return{
actions : bindActionCreators(LightActions,dispatch)
}
}
// 宣告 connect 連線
App = connect(mapStateToProps,mapDispatchToProps)(App);
// 真正的連線
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById(`demo`)
)
這裡使用 react-redux 提供 connect
的方法 連結React元件和Redux類:
// 宣告 connect 連線
App = connect(mapStateToProps,mapDispatchToProps)(App);
-
connect 方法不會改變原來的元件類,反而返回一個新的 已與 Redux store 連線的 元件類。注意這裡並沒有注入
store
物件,真正store
物件的注入靠最後的<Provider store>
元件;(更多說明請參考 [react-redux 的 API][1]) - 傳入 connect 的 mapStateToProps方法 ,正如其名,是將 Redux 的狀態 對映到 React元件的props屬性。任何時候,**只要 Redux store 發生改變,mapStateToProps 函式就會被呼叫**。這裡返回物件是
{light:state}
,這樣確保 Redux 中的 state 發生改變時,元件的 props.light 都是最新的 Redux state。 -
mapDispatchToProps方法 則是將 Store 中的 dispatch方法 直接封裝成物件的一個屬性,一般會用到 Redux 的輔助函式 bindActionCreators();這裡將
dispatch
繫結到action
屬性,這樣在紅綠燈元件內讓其變成紅燈的時候,不需要dispatch(changeRed())
這麼呼叫,直接使用actions.changeRed()
,語義化更好;(更多說明請參考 [react-redux 的 API][1]) - 最後的
<Provider store>
使元件層級中的 connect() 方法都能夠獲得 Redux store,這裡才真正注入store
變數,之前的只是宣告而已(之前的好比store是個形參,到了這一步store就是實參了)。(更多說明請參考 [react-redux 的 API][1])
經過上面的語句,Redux就將 state屬性 、 (**store** 的) dispatch方法
與 R
eact 元件的 props 繫結在一起,凡是更改 redux 的 states,就會更新所連線元件的 props
屬性。
react-redux 中的 connect 方法就算是HOC(High Order Component,高階元件)了,具體原理可參考文章 初識React中的High Order Component,這是因為如果使用ES6 寫React元件的話,mixin是不支援的
,因此使用High Order Component代替;
2.4、利用redux驅動react
理解了最為困難的部分,之後的事情就水到渠成了;
現在,只要記住 在App中可以直接使用Redux中的一切了 就行了
我們回過頭來,完善 App
元件的程式碼,完善 autoChange方法:
...
class App extends Component{
_bind(...methods){
methods.forEach((method)=>this[method] = this[method].bind(this));
}
constructor(){
super();
this._bind(`changeColor`,`handleClick`,`autoChange`);
this.state = {
count : 0,
timeId : null
}
}
changeColor(light,actions){ // 紅路燈變換規則
switch(light.color){
case `red`:
actions.changeGreen();
break;
case `green`:
actions.changeYellow();
break;
case `yellow`:
actions.changeRed();
break;
default:
actions.changeRed();
}
}
autoChange(){ // 自動更改紅綠燈
const { light, actions } = this.props;
let _self = this;
let curCount = ++this.state.count;
// console.log(`xx,`,curCount);
if(this.state.count > +light.time){
curCount = 0;
this.changeColor(light,actions);
}
// 自動更改
this.state.timeId = setTimeout(function(){
_self.setState({count:curCount});
_self.autoChange();
},1000);
}
handleClick(e){ // 用點選模擬紅路燈
if(this.state.timeId){
clearTimeout(this.state.timeId);
} else {
this.autoChange();
}
}
render(){
// 通過connect 注入 redux 的 dispatch 方法
const { light, actions } = this.props;
return (
<div id="traffic" onClick={this.handleClick.bind(this)}>
<Light light={light}/>
</div>
)
}
}
...
至此已經完成本節示例,通過 npm start
開啟服務, 在 http://localhost:3000/light/redux 中檢視。
在這個示例裡,通過點選紅綠燈,每隔若干秒紅綠燈就會變換顏色,這說明兩者已經連結起來;
(這個是gif圖,如果沒動畫請點選在新視窗開啟)
在後一篇文章,將示例如何處理多個Redux、React的情形;
[1] http://camsong.github.io/redux-in-chinese/docs/react-redux/api.html
相關文章
- Redux 基礎教程以及結合 React 使用方式ReduxReact
- Redux 入門教程(3):React-Redux 的用法ReduxReact
- Redux 入門教程(三):React-Redux 的用法ReduxReact
- react-redux實踐總結ReactRedux
- React 入門-redux 和 react-reduxReactRedux
- react reduxReactRedux
- Redux在React中的使用小結ReduxReact
- 對React、Redux、React-Redux詳細剖析ReactRedux
- React Native+Redux開發實用教程React NativeRedux
- React系列—React+Redux工程目錄結構劃分ReactRedux
- 小菜鳥的React之路--Redux基礎2ReactRedux
- React - React 中使用 ReduxReactRedux
- react、redux、react-redux之間的關係ReactRedux
- [譯]如何結合React Hooks來使用ReduxReactHookRedux
- react-reduxReactRedux
- React 快速上手 - 08 redux 狀態管理 react-reduxReactRedux
- redux && react-redux原始碼解析ReduxReact原始碼
- Redux進階系列1:React+Redux專案結構最佳實踐ReduxReact
- React,Redux,React-redux的錯綜複雜關係ReactRedux
- React之Redux原理ReactRedux
- react-redux(二)ReactRedux
- React Redux 與胖虎ReactRedux
- React — Redux詳解ReactRedux
- [譯] 使用 React、Redux 和 SVG 開發遊戲 - Part 2ReactReduxSVG開發遊戲
- 如何合理佈置React/Redux的目錄結構ReactRedux
- react-redux/redux相關API,用法原理ReactReduxAPI
- 帶你瞭解redux與react-reduxReduxReact
- 使用react-hook 替代 react-reduxReactHookRedux
- react之redux的使用ReactRedux
- react-redux 的使用ReactRedux
- React-redux基礎ReactRedux
- 在 React 中使用 ReduxReactRedux
- Redux for react native 指南ReduxReact Native
- react-redux 進階ReactRedux
- Redux 概要教程Redux
- Redux使用教程Redux
- 小白路程之----初學React語法棧之redux與react-reduxReactRedux
- react-native 之 state 和 props 以及 redux 和 react-reduxReactRedux