1. 单一数据源html
all states ==>Store
react
2. 单向数据流git
dispatch(actionCreator) => Reducer => (state, action) => state
github
// actionType export const ACTION_TYPE = 'ACTION_TYPE'; // actionCreator let actionCreator = (config) => { return { type: ACTION_TYPE, // 必须定义 type config // 传递参数 => reducer } }
import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import reducers from '../reducers'; let store = createStore( reducers, applyMiddleware(thunk) );
// 接收方法 let receiveSomething = (res) => { return { type: RECEIVE_SOME, res } } // 获取数据方法 export let fetchSomething = (args) => { return dispatch => { return fetch(args).then((res) => { return dispatch(receiveSomething(res)) }) } }
(initialState, action) => newState
import { ACTION_A, ACTION_B } from '../actions'; let initialState = { ... } function example(state = initialState, action) { switch(action.type) { case ACTION_A: return Object.assign({}, state, action.config) case ACTION_B: return Object.assign({}, state, action.config) } }
let doSomething = (config) => { let { a, b } = config; // do something with a, b return { a, b } } function example(state = initialState, action) { switch(action.type) { case ACTION_TYPE: return Object.assign({}, state, doSomething(action.config)) } }
经过 redux 提供的 combineReducers 将不一样处理逻辑的 reducer 合并起来。redux
import { combineReducers } from 'redux'; export default combineReducers({ reducerA, reducerB }); // or export let reducer = (state = initialState, action) { a: processA(state.a, action), b: processB(state.b, action) }
使用 react-redux 提供的 Provider 能够将 Store 注入到 react 中。api
Store 将合并后的 reducers 经过 createStore 建立,此外下面示例代码还使用中间件加入了一层 react-thunk 处理。app
import ReactDOM from 'react-dom'; import { createStore, applyMiddleware } from 'redux'; import { Provider } from 'react-redux' import thunk from 'redux-thunk'; import reducers from './reducers'; let store = createStore( reducers, applyMiddleware(thunk) ); ReactDOM.render(( <Provider store={store}> // ... </Provider> ), document.querySelector('#app'));
使用 react-redux 提供的 connect 方法 将组件和所需数据绑定。dom
须要注意的是,Store 建立时接收的是合并后的 reducers, 所以不一样 reducer 上的处理数据绑定在了不一样 reducer 对象上,而不是所有挂载在 Store 上。异步
mapStateToProps 将组件内部所需数据经过 props 传入组件内部。更多绑定机制,具体可参考connectide
import React, { Component } from 'react'; import { connect } from 'react-redux'; class ComponentA extends Component { //... } let mapStateToProps = (state) => { // attention !!! let { reducerA, reducerB } = state; return { propA: reducerA.propA, propB: reducerB.propB } }; export default connect(mapStateToProps)(ComponentA);
React bindings for Redux embrace the idea of separating presentational and container components.
Redux 的 React 绑定库包含了 容器组件和展现组件相分离 的开发思想。
展现型组件和容器型组件的区别在官方文档中已经给出很详细的解释了,可是中文文档的翻译有误,因此直接看英文比较更容易懂。
Presentational Components | Container Components | |
---|---|---|
Purpose | How things look (markup, styles) | How things work (data fetching, state updates) |
Aware of Redux | No | Yes |
To read data | Read data from props | Subscribe to Redux state |
To change data | Invoke callbacks from props | Dispatch Redux actions |
Are written | By hand | Usually generated by React Redux |
组件类型区分的模糊点在于怎么界定组件的内部功能规划。若是断定一个组件为展现型组件,那么它所需数据和处理方法都应该从父级传入,保持组件内部“纯净”。
在实际开发中,一个组件的逻辑跟业务紧密相关。若是须要将数据和方法从外部传入,那么父级组件所作的事情会不少,多重的子组件也会把父级逻辑弄乱,这就不是 redux 的初衷了。
中文文档翻译的意思是:容器组件应该为路由层面的组件,但这样既不符合实际开发须要,也违背了 redux 思想。真正界定两种组件的因素是:
当组件 connect 后,dispatch 方法已经注入到 props 中,因此触发 Action 能够从 props 获取 dispatch 方法。
import React, { Component } from 'react'; // actionCreator import { actionA, actionB } from 'actions/actionA' class ComponentA extends Component { constructor(props) { super(props); } componentDidMount() { let { dispatch } = this.props; dispatch(actionA()) } } export default connect()(ComponentA);
组件内部所需的渲染数据都已经绑定在了 props 上,直接获取便可。
须要注意的是,在事件监听中触发 Action,须要用一个匿名函数封装,不然 React 在渲染时就会执行事件绑定事件,而不是当事件发生再执行。
render() { let { dispatch, propA, propB } = this.props; return ( <section> // Attention !!! <input type="text" onClick={(ev) => dispatch(actionB(ev))} /> <p className={propA}>{propB}</p> </section> ) }
容器型组件须要链接 Redux,使用 dispatch 触发 actionCreator。
展现型组件须要用到的方法调用在容器型组件内定义好,经过 props 传入到展现型组件中。
// get actionCreator import { actionA } from './actions/actionA'; class Parent extends Component { handleCallback(data) { // use dispatch let { dispatch } = this.props; dispatch(actionA(data)); } render() { return ( <Child onSomethingChange={this.handleCallback} /> ) } } // connet Redux export default connect()(Parent);
展现型组件不须要用到 Redux 的一切,它的 props 仅仅存在于父级传入的数据和方法。
// don't need action/dispatch/connect class Child extends Component { handleSomething(data) { // handle anything with props this.props.onSomethingChange(data); } render() { return ( // just markup & style <input onChange={handleSomething} /> ) } }
图示箭头表明各概念之间的相互关系,不表明数据流。( 能理解下面这张图,这篇文章就没白看了 -。- )
参考文档
END.