Skip to content

Latest commit

 

History

History
216 lines (174 loc) · 6.66 KB

react-redux.md

File metadata and controls

216 lines (174 loc) · 6.66 KB

React-Redux

首先声明这个是redux官方提供的针对react的版本。所以呢如果你是react应用,那肯定得选react-redux

不过在react-redux之前,还要需要先了解redux,详见我的另一篇 redux

react-redux提供了以下几个API,我们来聊聊核心的两个API:Providerconnect

  • Provider
  • createProvider
  • connectAdvanced
  • connect

Provider

import React from 'react';
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import { Provider } from 'react-redux';
import App from './App';
import store from './store';

const render = (Component) => {
  ReactDOM.render(
		<Provider store={store}> 
			<AppContainer>
					<Component />
			</AppContainer>
		</Provider>,
    document.getElementById('app'),
  );
};

render(App);

// Webpack Hot Module Replacement API
if (module.hot) {
  module.hot.accept('./App', () => { render(App); });
}

// store.subscribe(() => {
// 	render(App);
// });

看过前面的我写的redux文章的应该知道,disptach需要自己订阅才能刷新视图,而且之前 store 属性只传递给了根视图,如果子组件需要获取store需要在我们的根视图中逐层往下传,或者顶一个context

Provider的作用就是提供子组件获取store,采用的原理就是reactcontext。来看看Provider的源码就一目了然了。

export function createProvider(storeKey = 'store', subKey) {
    const subscriptionKey = subKey || `${storeKey}Subscription`

    class Provider extends Component {
        getChildContext() {
          return { [storeKey]: this[storeKey], [subscriptionKey]: null }
        }

        constructor(props, context) {
          super(props, context)
          this[storeKey] = props.store;
        }

        render() {
          return Children.only(this.props.children)
        }
    }

    if (process.env.NODE_ENV !== 'production') {
      Provider.prototype.componentWillReceiveProps = function (nextProps) {
        if (this[storeKey] !== nextProps.store) {
          warnAboutReceivingStore()
        }
      }
    }

    Provider.propTypes = {
        store: storeShape.isRequired,
        children: PropTypes.element.isRequired,
    }
    Provider.childContextTypes = {
        [storeKey]: storeShape.isRequired,
        [subscriptionKey]: subscriptionShape,
    }

    return Provider
}

export default createProvider()

connect 基础

react-redux将组件分成两种:UI组件和容器组件。UI组件只负责展示,UI组件内部的数据和操作(例如:onClick)都由容器组件提供。这样做的好处是UI组件可以高度复用。

react-redux规定,所有的UI组件由开发者自己定义,容器组件由react-redux通过connect函数生成。

connect 函数携带两个参数:mapStateToPropsmapDispatchToProps。这两个参数都是纯函数。两个函数返回值必须是简单对象,即:{key:value}

mapStateToProps是一个携带stateownProps两个属性的纯函数(PS:看源码ownProps好像是打酱油的,完全没用到。),该函数负责reduxstate到UI组件props的映射,简单说就是state改变会导致UI组件的props改变,props改变会重新刷新视图。

mapDispatchToProps 是一个携带dispatchownProps两个属性的纯函数(PS:同样ownProps好像是打酱油的),该函数负责的UI组件的操作(注意不是dispatch)到props的映射。UI组件的操作一般是用户行为,例如:onClickonChange,用户行为内部用dispatch(action)的方式更新数据。

下面看一个简单的示例:

import React, { Component } from 'react';
import { connect } from 'react-redux';

import './App.css';
import TodoView from './components/todo';
import TodoAdd from './components/todo/add';
import { addTodo, toggleTodo, delTodo } from './actions/todo';


class App extends Component {
  render() {
		// dispatch,
		const { todolist, onAdd, onDelete, onChangeStatus } = this.props;
    return (
         <div styleName="main">
					<TodoAdd onAdd={(text) => { onAdd(text); }} />
					<TodoView todolist={todolist}
							onChangeStatus={todo => onChangeStatus(todo)}
							onDelete={todo => onDelete(todo)}
					/>
         </div>
    );
  }
}
// State => Props
const mapStateToProps = (state) => {
	return { todolist: state.todo };
};

// dispatch => Props
const mapDispatchToProps = dispatch => ({
		onAdd: (text) => {
			dispatch(addTodo(text));
		},
		onChangeStatus: (todo) => {
			dispatch(toggleTodo(todo));
		},
		onDelete: (todo) => {
			console.log('delete', todo);
			dispatch(delTodo(todo));
		},
	});

export default connect(mapStateToProps, mapDispatchToProps)(App);

connect 高阶

看过我写的redux文章的,肯定会有一个疑问,就是subscribe哪里去了?以前我们我们在根组件上订阅dispatch,然后更新根视图,现在没地方看到subscribe,那怎么实现刷新视图的呢?

这个问题正是connect的第二个功能。容器组件订阅dispatch。大家可以去看看源码,这里就直接把关键源码提取出来。

react-redux/src/utils/Subscription.js

export default class Subscription {
  constructor(store, parentSub, onStateChange) {
    this.store = store
    this.parentSub = parentSub
    this.onStateChange = onStateChange
    this.unsubscribe = null
    this.listeners = nullListeners
  }

  addNestedSub(listener) {
    this.trySubscribe()
    return this.listeners.subscribe(listener)
  }

  notifyNestedSubs() {
    this.listeners.notify()
  }

  isSubscribed() {
    return Boolean(this.unsubscribe)
  }

  trySubscribe() {
    if (!this.unsubscribe) {
      this.unsubscribe = this.parentSub
        ? this.parentSub.addNestedSub(this.onStateChange)
        : this.store.subscribe(this.onStateChange) // 这是关键源码
 
      this.listeners = createListenerCollection()
    }
  }

  tryUnsubscribe() {
    if (this.unsubscribe) {
      this.unsubscribe()
      this.unsubscribe = null
      this.listeners.clear()
      this.listeners = nullListeners
    }
  }
}

react-redux/src/components/connectAdvanced.js

onStateChange() {
    this.selector.run(this.props)

    if (!this.selector.shouldComponentUpdate) {
      this.notifyNestedSubs()
    } else {
      this.componentDidUpdate = this.notifyNestedSubsOnComponentDidUpdate
      this.setState(dummyState)  // 刷新视图
    }
  }

总结

淅淅沥沥如雨,昏昏沉沉如梦。我们完整了最基本的梦醒,对react-redux有了最基本的打开天窗说亮化的了解。如果想要深入浅出的了解他,你应该进入他的体内。别想污了,我说的是源码。