react之前在看看的时候工作过,后面到了TX就一直使用vue,react被忘得一干二净,这几天下了一个决心,想把react文档从头到尾重新看一遍,结果发现东西实在是太多了。react,redux,react-redux,react-router...该怎么破?
前几天刚把redux看完,其思想大致了解了下,但是想在react中使用redux结合react-redux是最好的,看了很多文章一直不理解其用法,直到我碰到阮一峰老师写的《Redux 入门教程(三):React-Redux 的用法》才真正理解了,为此写一篇读后感来记录一下,以备日后翻阅
老师的最后提供了一个计数器的demo来展示react-redux的用法,我也是看了这个demo之后才对react-redux的理解更近了一步
demo实现的功能很简单,就是每点击一次按钮计数器加1,整个demo的代码如下:
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore } from 'redux'
import { Provider, connect } from 'react-redux'
// UI组件
const Counter = props => {
const { value, onIncreaseClick } = props
return (
<div>
<span>{value}</span>
<button onClick={onIncreaseClick}> </button>
</div>
)
}
const mapStateToProps = (state, ownProps) => {
console.log('mapStateToProps called')
return {
value: state.count
}
}
const mapDispatchToProps = {
onIncreaseClick: () => {
return {
type: 'increase'
}
}
}
// 容器组件
const VisibleCounter = connect(mapStateToProps, mapDispatchToProps)(Counter)
// reducer
const reducer = (state = { count: 0 }, action) => {
switch (action.type) {
case 'increase':
return { count: state.count 1 }
default:
return state
}
}
// store
const store = createStore(reducer)
ReactDOM.render(
<Provider store={store}>
<VisibleCounter />
</Provider>, document.getElementById('root'))
下面我们结合react-redux中的概念来具体讲解下
展示组件(UI组件)
react-redux是基于展示组件和容器组件的开发思想,这一点必须明确。
展示组件也叫UI组件,你也可以把它理解为react中的无状态组件,它有以下特征:
- 无状态,使用this.props
- 不使用任何redux的API
- 不带有任何业务逻辑
就像上面的Counter组件就是一个展示组件,它只做展示数据,涉及到的任何业务逻辑都是通过它上层的容器组件实现的
容器组件
容器组件是对对展示组件的包装,有以下特征:
- 负责管理数据和业务逻辑,不负责UI的展现
- 带有内部状态state
- 使用redux的API
总之,记住这个原则就可以了,展示组件负责UI 的呈现,容器组件负责管理数据和逻辑
connect
react-redux提供的connect方法用于从UI组件生成容器组件,像下面这样
const VisibleCounter = connect(mapStateToProps, mapDispatchToProps)(Counter)
mapStateToProps负责输入逻辑,将state映射到UI组件的参数(props)
mapDispatchToProps负责输出逻辑,将用户对UI组件的操作映射成Action
mapStateToProps
一个函数,建立一个从外部state对象到UI组件props对象的映射关系
mapStateToProps执行后返回一个对象
const mapStateToProps = (state, ownProps) => {
return {
value: state.count
}
}
mapStateToProps有两个参数,第一个是state,第二个是当前的props
每当state更新的时候,mapStateToProps会自动执行,计算出UI组件的props,从而触发UI组件的重新渲染
mapDispatchToProps
mapDispatchToProps用来建立UI组件的props到dispatch方法的映射,它定义了用户的哪些操作会生成action,传给store,它可以是一个函数或者一个对象
当它是一个函数,有两个参数,第一个是dispatch,第二个是当前props
const mapDispatchToProps = (dispatch, ownProps) => {
return {
onIncreaseClick: () => {
dispatch({ type: 'increase' })
}
}
}
当它是一个对象
const mapDispatchToProps = {
onIncreaseClick: () => {
return {
type: 'increase'
}
}
}
从以上两种类型可以知道,当mapDispatchToProps是函数时,应该返回一个对象,键值对应UI组件触发某个事件的回调函数名,键值是一个函数,里面调用dispatch来发出一个action
当mapDispatchToProps是一个对象时,键值对应UI组件触发某个事件的回调函数名,键值是一个函数(Action Creator),返回一个action,该action会由react-redux自动调用dispatch
Provider
方便我们的每个子组件拿到state对象,不然一级一级传递state组件会非常麻烦,它的使用方法和一般的组件使用方法一样,它有一个store属性,而我们的store是调用redux提供的createStore方法生成的
// reducer
const reducer = (state = { count: 0 }, action) => {
switch (action.type) {
case 'increase':
return { count: state.count 1 }
default:
return state
}
}
// store
const store = createStore(reducer)
ReactDOM.render(
<Provider store={store}>
<VisibleCounter />
</Provider>, document.getElementById('root'))
关于Provider组件实现的原理是通过React组件的context属性,下面是我利用context属性自己实现的Provider组件
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { createStore } from 'redux'
// UI组件
const Counter = props => (
<div>
<span>{props.value}</span>
<button onClick={props.onIncreaseClick}> </button>
</div>
)
// 中间件
const MiddleVisibleCounter = () => (
<Consumer>
{ ({store}) => <VisibleCounter store={store} /> }
</Consumer>
)
// 容器组件
class VisibleCounter extends Component {
constructor(props) {
super(props)
const { store } = this.props
store.subscribe(() => {
this.setState(store.getState())
})
this.state = store.getState()
}
dispatchIncreaseAction() {
const { store } = this.props
store.dispatch({ type: 'increase' })
}
render() {
return (
<Counter value={this.state.count} onIncreaseClick={() => this.dispatchIncreaseAction()} />
)
}
}
// reducer
const reducer = (state = { count: 0 }, action) => {
switch (action.type) {
case 'increase':
return { count: state.count 1 }
default:
return state
}
}
// store
const store = createStore(reducer)
const { Provider: Provider2, Consumer } = React.createContext({
store: store
})
const Provider = (props) => {
return (
<Provider2 value={{store:props.store}}>
{ props.children }
</Provider2>
)
}
ReactDOM.render(
<Provider store={store}>
<MiddleVisibleCounter />
</Provider>
, document.getElementById('root'))
最后
你们的打赏是我写作的动力
更多专业前端知识,请上 【猿2048】www.mk2048.com