淺顯易懂的Redux深究
來自: http://galen-yip.com/react/2016/02/06/go-into-redux/
半個多月redux的學習,分析下redux,當做記錄吧
題外話,有看到前些天轉的很火的 @徐叔叔的回答 。說下就自己的看法吧,這也是為什么有這篇redux研究文章的原因。考慮適用性,團隊效率等等都是十分贊同的,但就個人而言,自己純屬是為了滿足個人的好奇心,為了在這大浪淘沙的前端變革時代,能身為一個歷經者。僅此而已。
</div>核心:
- 只使用一個store將整個應用程序的狀態state用object tree的方式存儲。原生的flux會有許多分散的store存儲各自不同的狀態,但在redux中,只有一個store。 </ul>
-
單向數據流,統一入口,改變state只能發送action,action就是一個東西,告訴state該怎么改變而已。這個特性跟原生的flux是一樣的。
</li> -
接受action,更改state的叫做Reducer。Reducer包含兩個參數,舊的state和action,返回新的state
</li> </ul>Actions
在redux和flux中,actions都是一個告知state要變更的東西 形如:
{ type: ADD_TODO, payload: { text: 'Build my first Redux app' } }
Actions Creators
在flux中,action creator要做dispatch的動作 redux中,action creator只是簡單的回傳action
flux中:
function addTodoWithDispatch(text) { dispatch({ type: ADD_TODO, payload: { text } }); } // 實際發送 action addTodoWithDispatch(text)
redux中:
function addTodo(text) { return { type: ADD_TODO, payload: { text } }; }
// 實際發送 action dispatch(addTodo(text)); dispatch(removeTodo(id));</pre></div>
在redux中,dispatch()方法來源于store.dispatch() 但react-redux中提供了connnector元件,這個元件會把dispatch提取出來給我們使用,所以不需要我們從store中提取
redux中提供了bindActionCreators(),就是將ActionCreator外加上了一層dispatch(),因為都想偷懶不寫dispatch(removeTodo(id))這些綁定代碼
所以bindActionCreator()是常用的方法之一
</div>Reducers
Reducers類似于flux里面的Store,但在Redux中,它是擋在Store前的一個東西,Redux里只有一個Store,所以這些Reduers的功能就是針對這個唯一的Store內的State的部分內容進行更新 Reduer接收2個參數,舊的State和action,返回新的State 例子:
const initialState = { todos: [], idCounter: 0 };
function todos(state = initialState, action) { switch (action.type) { case ADD_TODO: return { ...state, todos: [ ...state.todos, { text: action.payload, id: state.idCounter + 1 } ], idCounter: state.idCounter + 1 }; case REMOVE_TODO: return { ...state, todos: state.todos.filter(todo => todo.id !== action.payload) }; default: return state; } }</pre></div>
…state就是展開操作符,這里這么用的作用是返回新的對象,不修改原來的對象
Store
Redux中只有一個Store,這個Store是基于許多的Reduers上的 Redux提供了createStore()方法
如果是只有單個Reducer時:
import { createStore } from 'redux'; import todos from '../reducers/todos'; const store = createStore(todos);
多個Reducer時,則用combineReducers()把多個reducers合并成一個再丟給createStore()
export function todos(state, action) { / ... / }
export function counter(state, action) { / ... / }
import { createStore, combineReducers } from 'redux'; import * as reducers from '../reducers'; const reducer = combineReducers(reducers); const store = createStore(reducer);</pre></div>
得出的Store是這樣的:
{ dispatch, subscribe, getState, getReducer, replaceReducer };
調用getState(),可以得到state
const state = store.getState(); console.log(state);
// 結果: { todos: todoState, counter: counterState }</pre></div>
元件Container
官方使用的是container pattern,即有一個只管接收props并render的“笨”元件,和一個包括在笨元件外面的負責管理和傳遞數據的container元件 container元件也就負責與Redux交流
react-redux提供了 Provider元件和connect方法
Provider
用在應用的根元素外面,負責傳遞唯一的Store給應用
const reducer = combineReducers(reducers); const store = createStore(reducer);
class App extends Component { render() { return ( <Provider store={store}> {() => <App />} </Provider> ); } }</pre></div>
connect
用法: connect(select)(Component) connect接收一個函數當參數并傳回一個Component Class
作用:將dispatch方法透過props的方式加到元件中,而且還能選取這個container需要state的哪一部分 例如下面,只選取state里的counter部分
function select(state) { return { counter: state.counter }; }
class CounterApp { render() { const { counter, dispatch } = this.props; return ( <Counter counter={counter} /> ); } }
export default connect(select)(CounterApp)</pre></div>
當然,也可以不加select方法,這樣Connector收到的就是整個應用程序完整的
下面是很好的完整的redux的demo:
import React from 'react'; import { createStore } from 'redux'; import { Provider, connect } from 'react-redux';
// React component class Counter extends React.Component { render(){ const { value, onIncreaseClick } = this.props; return ( <div> <span>{value}</span> <button onClick={onIncreaseClick}>Increase</button> </div> ); } }
// Action: const increaseAction = {type: 'increase'};
// Reducer: function counter(state={count: 0}, action) { let count = state.count; switch(action.type){ case 'increase': return {count: count+1}; default: return state; } }
// Store: let store = createStore(counter);
// Map Redux state to component props function mapStateToProps(state) { return { value: state.count }; }
// Map Redux actions to component props function mapDispatchToProps(dispatch) { return { onIncreaseClick: () => dispatch(increaseAction) }; }
// Connected Component: let App = connect( mapStateToProps, mapDispatchToProps )(Counter);
React.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') );</pre></div>
自己理解的結構圖
中間件
redux本身能很好的處理同步的情況,但異步的情況在web開發中必不可少。 我把同步和異步分開來說
同步:
通過redux暴露出的applyMiddlewares,我們可以得到一個新的dispatch. 所以applyMiddlewares的實際效果可以看做是: dispatch => newdispatch
異步:
對于異步的情況,我們拿redux-thunk為例,它會對傳入的action進行判斷,如果是function的話。則去把我們的action執行,這個執行的就是我們的delay代碼。然后在最里面執行dispatch()。所以我們的箭頭又重新回到最上面,這一次就相當于同步的使用了
實踐中
下面是實際開發中使用到的
項目目錄
|-- actions |-- components |-- constants |-- containers |-- index.jsx |-- main.css |-- middleware |-- reducers |-- routes |-- store
- components 是沒有關于actions和stores的依賴,拿出項目也可以單獨使用 </ul>
- redux-thunk </ul>
- redux-logger </ul>
- immutable </ul>
- react-immutable-render-mixin </ul>
- redux-devtools </ul>
- ← Previous Post </ul> </div> </code></code></code></code></code></code></code></code></code></code></code></code></code></code>
項目中會用到的工具類(中間件):
使得dispatch給store時是異步的,比如action中需要ajax
記錄狀態改變前和后
不可變數據,即對于復雜結構的對象,如果需要改變某一層某個值,不像過去進行一次深拷貝那樣浪費資源,而是只改變該層以上的一些引用,其他沒變的仍然指針指向原來的地址
在后期性能優化中,對于嵌套組件而言,希望沒改變的不重新渲染。直接對比this.state和previous.state是否全等則可以,因為如果有修改,則最頂層的引用是修改了的
用ES7 decorator的方式引入 用于跳過更新沒有改變state的子組件 代替了 pure-render-mixim的功能
3.0.1現在的版本是,改變比較大,參考他的github 如果是babel6+ 有些地方需要加上 .default
下面是開發中遇到的幾個注意點:
一、 注意這里的items和filter,這些就是state!!!
export default combineReducers({ items, filter })
二、 用了es6,則mixant的用法不可用,用HOC(高階組件)替代
關于HOC和mixing、decoratorwww.jianshu.com
文章有點流水了,其中如有不當的還請指出,或者大家交流交流~~~
</div>本文由用戶 LinSWHS 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!sesese色
//原生 Flux 的 store
var firstStore = {
first: 1
}
var secondStore = {
second: 2
}
// Redux 的單一 store
var state = {
firstState: {
first: 1
},
secondState: {
second: 2
}
}</pre></div>