手挽手帶你學React入門四檔,用人話教你react-redux,理解redux架構,以及運用在react中。學完這一章,你就能夠開始本身的react項目了。javascript
上一篇咱們本身實現了Redux,這一篇咱們來看看如何去實現一個react-reduxhtml
本文須要用到的知識java
首先你要會React的基礎(這是廢話)
高階組件
React context
知足這三項咱們開始往下看。
咱們上一章講過了redux的原理,內部是有一個store的,store只有dispatch才能夠控制它變化。還有在文章一開始咱們講了React context 能夠跨組件傳遞數據,那麼到如今你想到把咱們的store掛到最外層一個組件的context上了嗎?話很少說 開始!react
咱們先修改一下咱們的react文件(注意不是redux.html這個文件了)npm
// App.js import React,{Component} from 'react' import PropTypes from 'prop-types' //引入 export default class App extends Component { constructor(){ super() this.state={ } } componentWillMount(){ // console.log(hashHistory) } render() { return ( <div> <Children /> <ChildrenTwo /> </div> ) } } // 爲了展現效果定義子組件一 class Children extends Component{ constructor(){ super() this.state={ } } render(){ return( <div> <h1>我是腦殼</h1> <h2>我是身體</h2> </div> ) } } // 爲了展現效果定義子組件二 ChildrenTwo 是 Children的子組件 可是卻經過context拿到了App組件拿過來的值 (越級傳遞) class ChildrenTwo extends Component{ constructor(){ super() this.state={ } } render(){ return( <div> <button>變字</button> <button>變色</button> </div> ) } }
如今咱們作好了示例文件的基礎模板了,而後咱們須要建立一個store.jsredux
// store.js // 這是咱們的 reducer const changeDom = (state,action) => { if(!state)return{ text : "我是實例文字", color : 'red' } switch(action.type){ case "CHANGE_TEXT": return{ ...state, text:action.text } case "CHANGE_COLOR": return{ ...state, color:action.color } default: return state } } const creatStore = (reducer)=>{ let state = null const listeners = [] const subscribe = (liestner)=>listeners.push(liestner) const getState = ()=>state const dispatch=(action)=>{ state = reducer(state,action) listeners.map(item=>item()) } dispatch({}) return { getState, dispatch, subscribe } } export {creatStore,changeDom}
咱們如今把咱們的子組件經過context公用App的store,而且把渲染方法和dispatch應用進去微信
// App.js import React,{Component} from 'react' import PropTypes from 'prop-types' //引入 // 引入 store import {changeDom,creatStore} from './store' const store = creatStore(changeDom) export default class App extends Component { // 咱們在這裏把store掛到context上 static childContextTypes = { store: PropTypes.object } getChildContext(){ return{store} } constructor(){ super() this.state={ } } componentWillMount(){ // console.log(hashHistory) } render() { return ( <div> <Children /> <ChildrenTwo /> </div> ) } } // 這裏咱們再去修改咱們的子孫組建 讓他們能夠拿到 store // 爲了展現效果定義子組件一 class Children extends Component{ static contextTypes = { store: PropTypes.object } constructor(){ super() this.state={ color:"", text:"" } } // 這裏咱們定義兩個渲染方法 getColor(){ let store = this.context.store.getState() this.setState({color:store.color}) } // 這裏咱們定義兩個渲染方法 getText(){ let store = this.context.store.getState() this.setState({text:store.text}) } // 這裏組件加載以前渲染 componentWillMount(){ this.getColor() this.getText() } render(){ return( <div> <h1 style={{color:this.state.color}}>{this.state.text}</h1> </div> ) } } // 爲了展現效果定義子組件二 ChildrenTwo 是 Children的子組件 可是卻經過context拿到了App組件拿過來的值 (越級傳遞) class ChildrenTwo extends Component{ static contextTypes = { store: PropTypes.object } constructor(){ super() this.state={ } } // 這裏咱們定義 兩個 action changeColor=()=>{ this.context.store.dispatch({type:"CHANGE_COLOR",color:"green"}) console.log(this.context.store.getState()) } changeText=()=>{ this.context.store.dispatch({type:"CHANGE_TEXT",text:"我變了"}) console.log(this.context.store.getState()) //這裏方便你們看到數據變了 } render(){ return( <div> <button onClick={()=>{this.changeText()}}>變字</button> <button onClick={()=>{this.changeColor()}}>變色</button> </div> ) } }
顯然 如今咱們點擊按鈕的時候,並無發生任何事情,但是咱們看console能夠看到,store的數據確實變化了,這是爲何呢?很簡單 咱們沒有把dom監聽放進來,下一步咱們要設置監聽。架構
// Children組件 咱們在componentWillMount 中添加監聽 class Children extends Component{ static contextTypes = { store: PropTypes.object } constructor(){ super() this.state={ color:"", text:"" } } // 這裏咱們定義兩個渲染方法 getColor(){ let store = this.context.store.getState() this.setState({color:store.color}) } // 這裏咱們定義兩個渲染方法 getText(){ let store = this.context.store.getState() this.setState({text:store.text}) } // 這裏組件加載以前渲染 componentWillMount(){ this.getColor() this.getText() this.context.store.subscribe(()=>{this.getColor()}) this.context.store.subscribe(()=>{this.getText()})// 使用箭頭函數 保證this指向 } render(){ return( <div> <h1 style={{color:this.state.color}}>{this.state.text}</h1> </div> ) } }
到這裏咱們已經簡單地實現了react-redux,但是你們有沒有以爲怪怪的?app
沒錯 到這裏咱們頻繁使用context,而且每一個組件都要去手動掛context 這是至關麻煩的,如今咱們想要讓這些東西都自動包裹 自動生成,咱們再怎麼作呢。
咱們建立一個高階組件就能夠搞定這個問題了(一個函數,接收一個組件,生成另一個包裝好的新組件)dom
import React, { Component } from 'react' import PropTypes from 'prop-types' export const connect = (WrappedComponent)=>{ class Connect extends Component{ static contextTypes = { store: PropTypes.object } render(){ return<WrappedComponent /> } } return Connect }
咱們這裏經過 connect函數來接收一個組件 通過一層封裝 返回一個包裝了context的組件 可是如今這樣的話 咱們每次使用都還須要大量的書寫 this.context 是否是至關噁心呢? 而且每一個組件都拿到了store 有不少事它並不須要的,這時候咱們就須要告訴這個高階組件,我這裏就只須要這些東西。這就是 mapStateToProps
import React, { Component } from 'react' import PropTypes from 'prop-types' // 示例 這裏不用 // const mapStateToProps=(state)=>{ // return{ // color:state.color, // text:state.text, // } // } 就是咱們須要從store拿出來的東西 const connect= (mapStateToProps) => (WrappedComponent)=>{ class Connect extends Component{ static contextTypes = { store: PropTypes.object } render(){ const store = this.context.stote let stateProps = mapStateToProps(store.getState()) 而後咱們把stateProps用...展開 return<WrappedComponent {...stateProps}/> } } return Connect }
如今咱們利用Connect改造咱們的組件
// App.js import React,{Component} from 'react' import PropTypes from 'prop-types' //引入 // 引入 store import {changeDom,creatStore} from './store' const store = creatStore(changeDom) // ________________________________把connect_____________________________寫在app.js 爲的是不引入了 實際上它是單獨拆分在react-redux中的 const connect = (mapStateToProps) => (WrappedComponent)=>{ class Connect extends Component{ static contextTypes = { store: PropTypes.object } render(){ const store = this.context.store console.log(store) let stateProps = mapStateToProps(store.getState()) // 而後咱們把stateProps用...展開 return<WrappedComponent {...stateProps}/> } } return Connect } // ________________________________把connect_____________________________寫在app.js 爲的是不引入了 實際上它是單獨拆分在react-redux中的 // app.js 的其餘內容... // ________________________________對組件一進行修改_____________________________ class Children extends Component{ constructor(){ super() this.state={ color:"", text:"" } } render(){ return( <div> <h1 style={{color:this.props.color}}>{this.props.text}</h1> </div> ) } } const mapStateToProps = (state) => { return { color: state.color, text:state.text } } Children = connect(mapStateToProps)(Children) // ________________________________對組件一進行修改_____________________________
如今咱們成功把store裏面的全部東西都掛到了props上面,咱們不須要去依賴context,只須要調用props便可。剩下的就是咱們的數據變動,渲染還有監聽了!
對 connect 進一步改造 其實就是像咱們上面的組件同樣 掛載監聽
const connect = (mapStateToProps) => (WrappedComponent)=>{ class Connect extends Component{ static contextTypes = { store: PropTypes.object } componentWillMount(){ const{store} = this.context this.updata() store.subscribe(()=>this.updata()) } updata=()=>{ const { store } = this.context let stateProps = mapStateToProps(store.getState(), this.props) this.setState({ allProps: { // 整合普通的 props 和從 state 生成的 props ...stateProps, ...this.props } }) } render(){ // 而後咱們把allProps用...展開 return<WrappedComponent {...this.state.allProps}/> } } return Connect }
state咱們改造完了,dispatch 是否是也要一塊兒改造呢?答案是確定的,mapDispatchToProps就是作這個的。
咱們看看 mapDispatchToProps 是怎麼寫的 咱們倒着推
//const mapDispatchToProps = (dispatch) => { // 傳入的是 dispatch // return { //返回一個函數 // changeColor: (color) => { // dispatch({ type: 'CHANGE_COLOR', color: color }) // } // } //} const connect = (mapStateToProps,mapDispatchToProps) => (WrappedComponent)=>{ class Connect extends Component{ static contextTypes = { store: PropTypes.object } componentWillMount(){ const{store} = this.context this.updata() console.log(store) store.subscribe(()=>this.updata()) } updata=()=>{ const { store } = this.context let stateProps = mapStateToProps?mapStateToProps(store.getState(), this.props):{} let dispatchProps = mapDispatchToProps?mapDispatchToProps(store.dispatch,this.props):{} // 咱們要考慮到空值處理 this.setState({ allProps: { // 整合普通的 props 和從 state 生成的 props ...stateProps, ...this.props ...dispatchProps, } }) } render(){ // 而後咱們把allProps用...展開 return<WrappedComponent {...this.state.allProps}/> } } return Connect }
到這裏咱們能夠把ChildrenTwo的代碼也改造了
class ChildrenTwo extends Component{ constructor(){ super() this.state={ } } render(){ console.log(this.props) return( <div> <button onClick={()=>{this.props.changeText("我變了")}}>變字</button> <button onClick={()=>{this.props.changeColor("green")}}>變色</button> </div> ) } } const mapDispatchToProps = (dispatch)=>{ return { //返回一個函數 changeColor: (color) => { dispatch({ type: 'CHANGE_COLOR', color: color }) }, changeText:(text)=>{ dispatch({ type: 'CHANGE_TEXT', text: text }) } } } ChildrenTwo = connect(null,mapDispatchToProps)(ChildrenTwo)
到如今 一個簡單的 react-redux已經差很少了,可是react-redux裏面還有一個<Provider>
可是咱們尚未這東西 這是什麼呢?實際上它作的事跟咱們App這個組件作的事一毛同樣,主要就是把store掛到context上。
export class Provider extends Component{ static childContextTypes = { store: PropTypes.object } getChildContext(){ return{store} } render() { return ( <div>{this.props.children}</div> ) } }
因而 咱們如今的代碼變成了這樣
好了 道理講完了 咱們如今開始拆分代碼啦。
// store.js export const creatStore = (reducer)=>{ let state = null const listeners = [] const subscribe = (liestner)=>listeners.push(liestner) const getState = ()=>state const dispatch=(action)=>{ state = reducer(state,action) listeners.map(item=>item()) } dispatch({}) return { getState, dispatch, subscribe } }
// reducer.js // 這是咱們的 reducer 能夠單獨拆分紅一個js文件 本身拆吧 export const changeDom = (state,action) => { if(!state)return{ text : "我是實例文字", color : 'red' } switch(action.type){ case "CHANGE_TEXT": return{ ...state, text:action.text } case "CHANGE_COLOR": return{ ...state, color:action.color } default: return state } }
// react-redux.js import React,{Component} from 'react' import PropTypes from 'prop-types' //引入 export const connect = (mapStateToProps,mapDispatchToProps) => (WrappedComponent)=>{ class Connect extends Component{ static contextTypes = { store: PropTypes.object } componentWillMount(){ const{store} = this.context this.updata() store.subscribe(()=>this.updata()) } updata=()=>{ const { store } = this.context let stateProps = mapStateToProps?mapStateToProps(store.getState(), this.props):{} let dispatchProps = mapDispatchToProps?mapDispatchToProps(store.dispatch,this.props):{} // 作一下空值處理 this.setState({ allProps: { // 整合普通的 props 和從 state 生成的 props ...stateProps, ...this.props, ...dispatchProps, } }) } render(){ // 而後咱們把allProps用...展開 return<WrappedComponent {...this.state.allProps}/> } } return Connect } export class Provider extends Component{ static childContextTypes = { store: PropTypes.object } getChildContext(){ return {store:this.props.store} } render() { return ( <div>{this.props.children}</div> ) } }
// App.js import React,{Component} from 'react' import Children from './Children' import ChildrenTwo from './ChildrenTwo' export default class App extends Component { constructor(){ super() this.state={ } } render() { return ( <div> <Children /> <ChildrenTwo /> </div> ) } }
// Children.js import React,{Component} from 'react' import{connect} from './react-redux.js' class Children extends Component{ constructor(){ super() this.state={ color:"", text:"" } } render(){ return( <div> <h1 style={{color:this.props.color}}>{this.props.text}</h1> </div> ) } } const mapStateToProps = (state) => { return { color: state.color, text:state.text } } Children = connect(mapStateToProps)(Children) export default Children
// ChildrenTwo.js import React,{Component} from 'react' import{connect} from './react-redux.js' class ChildrenTwo extends Component{ constructor(){ super() this.state={ } } render(){ return( <div> <button onClick={()=>{this.props.changeText("我變了")}}>變字</button> <button onClick={()=>{this.props.changeColor("green")}}>變色</button> </div> ) } } const mapDispatchToProps = (dispatch)=>{ return { //返回一個函數 changeColor: (color) => { dispatch({ type: 'CHANGE_COLOR', color: color }) }, changeText:(text)=>{ dispatch({ type: 'CHANGE_TEXT', text: text }) } } } ChildrenTwo = connect(null,mapDispatchToProps)(ChildrenTwo) export default ChildrenTwo
拆完了的代碼是否是簡單明瞭
真正工做裏面 咱們須要多作兩步
npm i redux --save npm i react-redux --save
而後 咱們
對照着修改這幾個位置
// creatStore 在 redux 插件中 // connect,Provider 在 react-redux 插件中 // 也就是用到哪裏了 對應修改哪裏 改完了你就發現了新大陸了 import { createStore } from 'redux' import { connect,Provider } from 'react-redux'
不知不覺發現本身不只僅會用了react-redux 而且還本身實現了一個react-redux 很舒坦的呢
在咱們四檔下篇到這裏結束了,這就是react-redux的實現和寫法,你們本身去實現真正的寫法,這裏不作演示至關於給你們留個做業,有錯誤或者是有建議 或者有不懂的地方 掃碼加我微信給你們解答。