手挽手帶你學React入門四檔,用人話教你react-redux,理解redux架構,以及運用在react中。學完這一章,你就能夠開始本身的react項目了。javascript
上一篇咱們本身實現了Redux,這一篇咱們來看看如何去實現一個react-reduxhtml
本文須要用到的知識java
首先你要會React的基礎(這是廢話) 高階組件 React context 知足這三項咱們開始往下看。react
咱們上一章講過了redux的原理,內部是有一個store的,store只有dispatch才能夠控制它變化。還有在文章一開始咱們講了React context 能夠跨組件傳遞數據,那麼到如今你想到把咱們的store掛到最外層一個組件的context上了嗎?話很少說 開始!npm
咱們先修改一下咱們的react文件(注意不是redux.html這個文件了)redux
// 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.jsbash
// 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 這是至關麻煩的,如今咱們想要讓這些東西都自動包裹 自動生成,咱們再怎麼作呢。 咱們建立一個高階組件就能夠搞定這個問題了(一個函數,接收一個組件,生成另一個包裝好的新組件)
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的實現和寫法,你們本身去實現真正的寫法,這裏不作演示至關於給你們留個做業,有錯誤或者是有建議 或者有不懂的地方 掃碼加我微信給你們解答。