从0实现一个tiny react-redux
从0实现一个tiny react-reduxreact-redux 是一个连接react和redux的库, 方便把redux状态库在react中使用。 不用react-redux先让我们来个裸的redux 和react结合的例子试试手 import { createStore,applyMiddleware } from 'redux' import logger from 'redux-logger' function reduer(state,action) { switch (action.type) { case 'add': { return { ...state,count: state.count + 1 } } default: { return state } } } const store = createStore(reduer,{ count: 0 },applyMiddleware(logger)) 这个store接受一个 type为add的action。 class HelloWorld extends Component { constructor(props) { super(props) store.subscribe(() => { this.setState({}) }) } render() { const state = store.getState() return ( <div onClick={e => store.dispatch({type: 'add'})} style={{color: 'red'}} > {state.count} </div> ) } } ReactDOM.render(<HelloWorld/>,document.getElementById("root")) 在线地址。 打开f12,当我们点击的时候 会发现有redux日志输出,并且渲染的值和store保持了一致。 connectHelloWorld 已经向我们展示了当react把状态交给redux的处理方式。 但是这里有这样的问题:
让我们用react-redux的思路来思考这个问题。 react-redux需要做什么呢?
所以: class HelloWorld extends Component { render() { return ( <div> <div onClick={e => store.dispatch({type: 'add'})} >{this.props.count}</div> <div onClick={e => store.dispatch({type: 'delete'})} >xxx</div> </div> ) } } function reduxHoc(WrappedComponent,mapStateToProps) { return class Rh extends Component { constructor(props) { super(props) this.sub = store.subscribe(() => { this.setState({}) }) this._beforeProps = mapStateToProps(store.getState(),props) } componentWillUnmount() { this.sub() } shouldComponentUpdate() { const newProps = mapStateToProps(store.getState(),this.props) if (this._beforeProps === newProps) { return false } this._beforeProps = newProps return true } render() { return <WrappedComponent {...this.props} {...this._beforeProps} /> } } } HelloWorld = reduxHoc(HelloWorld,state => state) 这里的reduxHoc方法返回了一个React组件类,类比与“高阶函数”的概念,这里叫做“高价组件”。高阶组件详解。reduxHoc 接受2个参数WrappedComponent,mapStateToProps,分别是要被包装的组件(这里是HelloWorld)以及 把state映射到props的mapStateToProps。 返回的Rh组件此刻已经监听了store的变化,并且会把从store映射过来的props 传递给WrappedComponent组件。 function connect(mapStateToProps,mapDispatchToProps) { return function (WrappedComponent) { return class Hoc extends Component { constructor(props,context) { super(props) this.unsubscribe = store.subscribe(() => { this.setState({}) }) this.memorizeProps = this.calculateProps() } calculateProps() { const o1 = mapStateToProps(store.getState(),this.props) let o2 = null if(mapDispatchToProps) { o2 = mapDispatchToProps(store.dispatch,this.props) } else { o2 = { dispatch: store.dispatch } } return { ...o1,...o2 } } componentWillUnmount() { this.unsubscribe() this.unsubscribe = null } shouldComponentUpdate() { const nextProps = this.calculateProps() const isEqual = shallowEqual(nextProps,this.memorizeProps) if (isEqual) { return false } else { this.memorizeProps = nextProps return true } } render() { return ( <WrappedComponent {...this.props} {...this.memorizeProps} /> ) } } } } function shallowEqual(objA,objB) { if (objA === objB) { return true; } const keysA = Object.keys(objA); const keysB = Object.keys(objB); if (keysA.length !== keysB.length) { return false; } // Test for A's keys different from B. const hasOwn = Object.prototype.hasOwnProperty; for (let i = 0; i < keysA.length; i++) { if (!hasOwn.call(objB,keysA[i]) || objA[keysA[i]] !== objB[keysA[i]]) { return false; } } return true; } 这里的connect使用方式和 react-redux的connect是一致的了(react-redux的connect其实接受4个参数)。有一点需要注意:reduxHoc和connect的shouldComponentUpdate 都只是 浅比较了引用, 这是因为redux库是无副作用的,所以来自redux库的对象只要引用相同就一定完全没有改变, 可以不用再次渲染。反过来说:如果store里面的值改变了,但是页面没有重新渲染,说明redux的逻辑写的有问题。 Provider前面提到的connect方法, 虽然现在store没有侵入具体业务组件, 但是connect方法里面却用到了store。而我们在使用react-redux这个库的时候, 可能压根儿就不知道store在哪里。 或者我们需要把store传给这个库,来生成connect函数。 class Provider extends Component { static childContextTypes = { store: PropTypes.object } getChildContext() { return { store: this.props.store } } render() { return Children.only(this.props.children) } } 对应的connect写法 function connect(mapStateToProps,mapDispatchToProps) { return function (WrappedComponent) { return class Hoc extends Component { static contextTypes = { store: PropTypes.object } constructor(props,context) { super(props) this.store = props.store || context.store this.unsubscribe = this.store.subscribe(() => { this.setState({}) }) this.memorizeProps = this.calculateProps() } calculateProps() { const o1 = mapStateToProps(this.store.getState(),this.props) let o2 = null if(mapDispatchToProps) { o2 = mapDispatchToProps(this.store.dispatch,this.props) } else { o2 = { dispatch: this.store.dispatch } } return { ...o1,this.memorizeProps) if (isEqual) { return false } else { this.memorizeProps = nextProps return true } } render() { return ( <WrappedComponent {...this.props} {...this.memorizeProps} /> ) } } } } 其他代码托管在git npm install treact-tredux (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- COCOS-HTML5-3.9版本学习(三)box2d物理引擎的测试
- [Swift]LeetCode152. 乘积最大子序列 | Maximum Product Su
- Cocos2dx 中的CCCallFunc,CCCallFuncN,CCCallFuncND,CCCall
- xcode – 检查脚本中的GCC_PREPROCESSOR_DEFINITIONS参数
- flex+eclipse获取session
- ruby-on-rails – 在webrick服务器上的生产模式下运行rails
- Nuget无法找到更新的依赖
- c# – 新的FileInfo(路径).Name与Path.GetFileName(路径)
- C#进阶系列 WebApi身份认证解决方案推荐:Basic基础认证
- Quick-Cocos2d-x 3.3绑定自定义类至Lua(二)新建项目中配制