Redux入门0x107: `react`集成`redux`精讲
0x000 概述
前面虽然简单的讲了如何在react
中集成redux
,但是那只是简单的讲讲而已,这一章将会仔细讲讲如何在react
中使用reudx
0x001 问题分析
查看前边的栗子:
import {createStore} from 'redux' import React from 'react' import ReactDom from 'react-dom' //reducer const counter = (state = 0, action) => { switch (action.type) { case ACTION_INCREMENT: return state + 1 case ACTION_DECREMENT: return state - 1 default: return state } } // action const ACTION_INCREMENT = 'INCREMENT' const ACTION_DECREMENT = 'DECREMENT' // action creator const increment = () => ({ type: ACTION_INCREMENT }) const decrement = () => ({ type: ACTION_DECREMENT }) // store const store = createStore(counter) // react // // 组件 class App extends React.Component { constructor() { super() // 初始化 state this.state = { counter: 0 } // 监听 store 变化, store 变化的时候更新 counter this.unSub=store.subscribe(() => { this.setState({ counter: store.getState() }) }) } // 组件销毁的时候取消订阅 componentWillUnmount(){ this.unSub() } render() { return <div> <p>{this.state.counter}</p> <button onClick={() => { store.dispatch(increment()) }}>+ </button> <button onClick={() => { store.dispatch(decrement()) }}>- </button> </div> } } ReactDom.render( <App/>, document.getElementById('app') )
为了让组件可以响应redux
的变化,我们写了一些代码:
.... // 监听 store 变化, store 变化的时候更新 counter this.unSub=store.subscribe(() => { this.setState({ counter: store.getState() }) }) .... // 组件销毁的时候取消订阅 componentWillUnmount(){ this.unSub() }
如果我们有大量的组件需要绑定redux
,那么写这些代码就显得非常冗余了
这一章要做的事就是优化掉这个东西
0x002 connect
方法
这里用了一个react
的HOC
,参数是一个组件,返回值也是一个组件,但是返回的组件被添加了一个props
,也就是state
。connect
方法为每个组件添加了响应store
数据变化的能力,在store.dispatch
调用的时候,会修改组件的props
,让组件重绘,从而达到react
组件和redux
绑定但是又不需要写太多样板代码connect
const connect = (WrappedComponent) => { return class Control extends React.Component { constructor() { super() this.state = { state: 0 } this.unSub = store.subscribe(() => { this.setState({ state: store.getState() }) }) } componentWillUnmount() { this.unSub() } render() { return <WrappedComponent state={this.state.state}/> } } }
完整源码
import {createStore} from 'redux' import React from 'react' import ReactDom from 'react-dom' //reducer const counter = (state = 0, action) => { switch (action.type) { case ACTION_INCREMENT: return state + 1 case ACTION_DECREMENT: return state - 1 default: return state } } // action const ACTION_INCREMENT = 'INCREMENT' const ACTION_DECREMENT = 'DECREMENT' // action creator const increment = () => ({ type: ACTION_INCREMENT }) const decrement = () => ({ type: ACTION_DECREMENT }) // store const store = createStore(counter) const connect = (WrappedComponent) => { return class Control extends React.Component { constructor() { super() this.state = { state: 0 } this.unSub = store.subscribe(() => { this.setState({ state: store.getState() }) }) } componentWillUnmount() { this.unSub() } render() { return <WrappedComponent state={this.state.state}/> } } } // 子组件 class SubCom extends React.Component { render(){ return <p>{this.props.state}</p> } } // 包裹这个组件 let ReduxSubCom=connect(SubCom) // react 组件 class App extends React.Component { constructor() { super() } render() { return <div> <ReduxSubCom/> <button onClick={() => { store.dispatch(increment()) }}>+ </button> <button onClick={() => { store.dispatch(decrement()) }}>- </button> </div> } } // 包裹组件 let ReduxApp = connect(App) ReactDom.render( <ReduxApp/>, document.getElementById('app') )
0x003 加强connect
方法,消除订阅整个state
树的影响
虽然已经实现了将state
和组件绑定,但是我们绑定的是整个state
,如果state
树很大并且组件很多,那这个无畏的性能消耗太凶了。- 修改
redux
结构
const counter = (state = {counter: 0, num: 0}, action) => { switch (action.type) { case ACTION_INCREMENT: return {...state, ...{counter: ++state.counter}} case ACTION_DECREMENT: return {...state, ...{counter: --state.counter}} default: return state } }
- 修改
connect
方法,返回一个函数,并修改props
传参:
const connect = (mapStateToProps) => { return (WrappedComponent) => class Control extends React.Component { constructor() { super() this.state = { state: {} } this.unSub = store.subscribe(() => { let state = mapStateToProps(store.getState()) this.setState({ state: state }) }) } componentWillUnmount() { this.unSub() } render() { return <WrappedComponent {...this.state.state}/> } } }
- 修改
APP
组件中的props
访问方式
class App extends React.Component { constructor() { super() } componentWillReceiveProps(nextProps) { console.log(nextProps) } render() { return <div> <p>{this.props.counter}</p> <button onClick={() => { store.dispatch(increment()) }}>+ </button> <button onClick={() => { store.dispatch(decrement()) }}>- </button> </div> } }
- 修改
connect
调用
let ReduxApp = connect((state) => { return { counter: state.counter } })(App)
0x004: 加强connect
,让代码中不再调用store.dispatch
,不在依赖redux
修改
connect
方法,除了吧state
映射到props
上,也把dispatch
给映射上去了,这样组件就感受不到redux
的存在了const connect = (mapStateToProps, mapDispatchToProps) => { return (WrappedComponent) => class Control extends React.Component { constructor() { super() // 第一次初始化 let props = mapStateToProps(store.getState()) let actions = mapDispatchToProps(store.dispatch) this.state = { props: {...props,...actions} } this.unSub = store.subscribe(() => { // 变化的时候再次计算 let props = mapStateToProps(store.getState()) let actions = mapDispatchToProps(store.dispatch) this.setState({ props: {...props,...actions} }) }) } componentWillUnmount() { this.unSub() } render() { return <WrappedComponent {...this.state.props}/> } } }
修改
connect
调用,将dispatch
映射到组件上let ReduxApp = connect( (state) => { return { counter: state.counter } }, (dispatch) => { return { increment: () => dispatch(increment()), decrement: () => dispatch(decrement()), } } )(App)
修改
APP
组件,不再使用store.dispatch
,而是使用connect
传递过来的dispatch
,让组件不依赖redux
class App extends React.Component { constructor(props) { super() } render() { const {counter,increment,decrement}=this.props return <div> <p>{counter}</p> <button onClick={increment}>+ </button> <button onClick={decrement}>- </button> </div> } }
完整源码
import {createStore} from 'redux' import React from 'react' import ReactDom from 'react-dom' //reducer const counter = (state = {counter: 0, num: 0}, action) => { switch (action.type) { case ACTION_INCREMENT: return {...state, ...{counter: ++state.counter}} case ACTION_DECREMENT: return {...state, ...{counter: --state.counter}} default: return state } } // action const ACTION_INCREMENT = 'INCREMENT' const ACTION_DECREMENT = 'DECREMENT' // action creator const increment = () => ({ type: ACTION_INCREMENT }) const decrement = () => ({ type: ACTION_DECREMENT }) // store const store = createStore(counter) const connect = (mapStateToProps, mapDispatchToProps) => { return (WrappedComponent) => class Control extends React.Component { constructor() { super() // 第一次初始化 let props = mapStateToProps(store.getState()) let actions = mapDispatchToProps(store.dispatch) this.state = { props: {...props,...actions} } this.unSub = store.subscribe(() => { // 变化的时候再次计算 let props = mapStateToProps(store.getState()) let actions = mapDispatchToProps(store.dispatch) this.setState({ props: {...props,...actions} }) }) } componentWillUnmount() { this.unSub() } render() { return <WrappedComponent {...this.state.props}/> } } } // react 组件 class App extends React.Component { constructor(props) { super() } render() { const {counter,increment,decrement}=this.props return <div> <p>{counter}</p> <button onClick={increment}>+ </button> <button onClick={decrement}>- </button> </div> } } let ReduxApp = connect( (state) => { return { counter: state.counter } }, (dispatch) => { return { increment: () => dispatch(increment()), decrement: () => dispatch(decrement()), } } )(App) ReactDom.render( <ReduxApp/>, document.getElementById('app') )
0x004 reat-redux
以上效果就和上一章的效果一致,是一个counter
,在这里我们一步一步去除了样板代码,并将redux
从组件中移除,如果只看APP
组件,根本感觉不到redux
的存在,但redux
又确实存在,如果有一天你要去掉redux
,就可以做到不影响组件了。这里的connect
其实不需要自己写,已经有好的实现了:react-redux
// 引入`react-redux` import {Provider, connect} from 'react-redux' // 修改组件 ReactDom.render( <Provider store={store}> <ReduxApp/> </Provider>, document.getElementById('app') )
完整源码
import {createStore} from 'redux' import React from 'react' import ReactDom from 'react-dom' import {Provider, connect} from 'react-redux' //reducer const counter = (state = {counter: 0, num: 0}, action) => { switch (action.type) { case ACTION_INCREMENT: return {...state, ...{counter: ++state.counter}} case ACTION_DECREMENT: return {...state, ...{counter: --state.counter}} default: return state } } // action const ACTION_INCREMENT = 'INCREMENT' const ACTION_DECREMENT = 'DECREMENT' // action creator const increment = () => ({ type: ACTION_INCREMENT }) const decrement = () => ({ type: ACTION_DECREMENT }) // store const store = createStore(counter) // react 组件 class App extends React.Component { constructor(props) { super() } render() { const {counter, increment, decrement} = this.props return <div> <p>{counter}</p> <button onClick={increment}>+ </button> <button onClick={decrement}>- </button> </div> } } let ReduxApp = connect( (state) => { return { counter: state.counter } }, (dispatch) => { return { increment: () => dispatch(increment()), decrement: () => dispatch(decrement()), } } )(App) ReactDom.render( <Provider store={store}> <ReduxApp/> </Provider>, document.getElementById('app') )
0x005 资源
相关推荐
opendigg 2020-06-02
weiqi 2020-04-29
mjzhang 2020-04-16
游走的豚鼠君 2020-11-10
81417707 2020-10-30
ctg 2020-10-14
小飞侠V 2020-09-25
PncLogon 2020-09-24
jipengx 2020-09-10
颤抖吧腿子 2020-09-04
wwzaqw 2020-09-04
maple00 2020-09-02
青蓝 2020-08-26
罗忠浩 2020-08-16
liduote 2020-08-13
不知道该写啥QAQ 2020-08-02
pengruiyu 2020-08-01
wmd看海 2020-07-27