Store是保存数据的地方,可以看成一个容器。整个应用只能有一个 Store。
Redux 提供createStore这个函数,用来生成 Store。
import { createStore } from 'redux';
const store = createStore(fn);
createStore函数接受一个函数作为参数,返回新生成的 Store 对象。
Store对象包含所有数据。state就是某个时刻对 Store 生成的一个快照,通过store.getState()得到:
const state = store.getState();
Redux 中一个 State 对应一个 View。只要 State 相同,View 就相同。
action是一个对象,必须含有type属性,表示action的名称,一般语义化为要做的行为,
dispatch会将action(行为)发送给store,触发行为
Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer.
Reducer是一个纯函数,接受 Action 和当前 State 作为参数,返回一个新的 State。
store.subscribe方法是设置监听函数,当State 发生变化时就自动执行这个函数。
View的更新函数(对于 React 项目,是组件的render方法或setState方法)放入listen,就会实现 View 的自动渲染。
store.subscribe方法返回一个函数,调用这个函数就可以解除监听。
const createStore = (reducer)=>{
let state;
let list = [];//作为一个容器,保存
const getState = ()=>{
return state;
}
const subscribe = (fn)=>{
list.push(fn);
// 返回一个函数,执行函数时,取消回调函数的执行
return ()=>{
list = list.fibter((cb)=>{
return cb != fn;
});
}
}
// dispatch时,有行为action,就会更新state
const dispatch = ( action )=>{
state = reducer(state,action);
list.forEach( (fn)=>{
fn();
} );
}
return { getState, subscribe, dispatch }
}
当组件之间嵌套的层级过多时,就需要在每一层级进行传递,而这种传递对日后的维护成本十分巨大,Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。
某些属性是应用程序中许多组件都需要的。Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props(即Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。请谨慎使用,因为这会使得组件的复用性变差。):
const ThemeContext = React.createContext('light'); //light是默认值
render() {
// 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
// 无论多深,任何组件都能读取这个值。
// 在这个例子中,我们将 “dark” 作为当前的值传递下去。
return (
// 将此全局context.Provider以标签的形式写在最外围
// 将 “dark” 作为要传递的值
// 子组件
);
}
class ThemedButton extends React.Component {
// 指定 contextType 读取当前的 theme context。
// React 会往上找到最近的 theme Provider,然后使用它的值。
// 在这个例子中,当前的 theme 值为 “dark”。
// 使用this.context获取到值
static contextType = ThemeContext;
render() {
return ;
}
}
React-Redux是React的专用库,实际项目可使用Redux或者React-Redux。
React-Redux将组件分为两类:UI组件和容器组件
其中UI组件(纯组件)只负责UI呈现,不负责业务逻辑,没有状态(this.state),所需数据由this.prop提供,不使用Redux的API。
而容器组件使用Redux的API,带有内部状态,并且容器组件由React-Redux自动生成。
connect方法,从UI组件生成容器组件,借鉴了Conntext的实现思想:即将状态的数据放置在外组件,其API如下:
import {} from 'react-redux'
const mapStateToProps = (state)=>{
return {
todos:getVisibleTodos(state.todos,state.visiblityFilter) // getVisibleTodos函数用来计算需要state中的哪些值
}
}
const mapDispatchToProps = (dispatch)=>{
return {
onClickTodo:() =>{ // onClickTodo是UI组件中要被触发的事件,触发后执行dispatch
dispatch({
type:'TOGGLE_TODO',
id
})
}
}
}
const VisibleTodoList = connect(mapStateToProps,mapDispatchToProps)(TodoLIst);
connect函数接受两个函数参数,返回一个函数,返回的函数的参数是UI组件(此处是TodoLIst),该方法会自动生成一个容器组件,并将其连接。
形参mapStateToProps是一个函数,负责将state映射到UI组件的props上,mapStateToProps()接受一个参数state,返回所需要的状态值。
形参mapDispatchToProps也是一个函数,负责将操作逻辑(行为事件)映射到UI组件的Action。mapDispatchToProps()接收dispatch,在事件中执行action.
上面已经分析了connect的功能,接下来根据其功能实现connect函数:
const connect = (mapStateToProps,mapDispatchToProps) => {
return (WrapperComponent)=>{
// 定义一个容器包裹UI组件,利用父节点更新会自动更新子组件的特性来自动更新子组件
class Connect extends Component{
componentDidMount(){
// 利用跨多级组件通信context得到store,此处使用旧的API,w未来会废弃
const store = this.context.store;
this.unsubscribe = store.subscribe(()=>{
this.forceUpdate();
});
}
componentWillUnmount(){
this.unsubscribe();
}
// 以上代码实现自己更新自己的功能。
render(){
const store = this.context.store;
const stateProps = mapStateToProps(store.getState()); //返回当前需要的属性
const dispatchProps = mapDispatchToProps(store.dispatch); // 返回当前所需的方法
const props = Object.assign({},stateProps,dispatchProps);
return React.createElement(WrapperComponent,props); // 创建UI组件的ReactDom节点,并将数据函数传入UI组件的props
}
}
Connect.contextTypes = {
store:React.PropTypes.object
}
return Connect;
}
}