首先声明这个是redux
官方提供的针对react
的版本。所以呢如果你是react
应用,那肯定得选react-redux
。
不过在react-redux
之前,还要需要先了解redux
,详见我的另一篇 redux 。
react-redux
提供了以下几个API,我们来聊聊核心的两个API:Provider
和connect
Provider
createProvider
connectAdvanced
connect
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
,采用的原理就是react
的context
。来看看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()
react-redux
将组件分成两种:UI组件和容器组件。UI组件只负责展示,UI组件内部的数据和操作(例如:onClick)都由容器组件提供。这样做的好处是UI组件可以高度复用。
react-redux
规定,所有的UI组件由开发者自己定义,容器组件由react-redux
通过connect
函数生成。
connect
函数携带两个参数:mapStateToProps
和mapDispatchToProps
。这两个参数都是纯函数。两个函数返回值必须是简单对象,即:{key:value}
。
mapStateToProps
是一个携带state
和ownProps
两个属性的纯函数(PS:看源码ownProps
好像是打酱油的,完全没用到。),该函数负责redux
的state
到UI组件props
的映射,简单说就是state
改变会导致UI组件的props
改变,props
改变会重新刷新视图。
mapDispatchToProps
是一个携带dispatch
和ownProps
两个属性的纯函数(PS:同样ownProps
好像是打酱油的),该函数负责的UI组件的操作(注意不是dispatch
)到props
的映射。UI组件的操作一般是用户行为,例如:onClick
,onChange
,用户行为内部用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);
看过我写的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
有了最基本的打开天窗说亮化的了解。如果想要深入浅出的了解他,你应该进入他的体内。别想污了,我说的是源码。