redux无疑已是react应用的标配了,也确实好用,这里就redux的基础点说明一下,包括createStore, combineReducers,provider, connectjavascript
const createStore = (reducer) => { let state let listeners = [] const getState = () => state const dispatch = (action) => { state = reducer(state, action) listeners.forEach(listener => listener()) } const subscribe = (listener) => { listeners.push(listener) return () => { listeners = listeners.filter(l => l !== listener) } } dispatch({}) // to get initial state return {getState, dispatch, subscribe} } // import {createStore} from 'redux'
另一个class版本的Store, 更接近真实的store, 构造函数传递两个参数,reducers和initialStatejava
const store = new Store(reducers, initialState);
export class Store { private subscribers: Function[]; private reducers: { [key: string]: Function }; private state: { [key: string]: any }; constructor(reducers = {}, initialState = {}) { this.subscribers = []; this.reducers = reducers; this.state = this.reduce(initialState, {}); } get value() { return this.state; } subscribe(fn) { this.subscribers = [...this.subscribers, fn]; fn(this.value); return () => { this.subscribers = this.subscribers.filter(sub => sub !== fn); }; } dispatch(action) { this.state = this.reduce(this.state, action); this.subscribers.forEach(fn => fn(this.value)); } private reduce(state, action) { const newState = {}; for (const prop in this.reducers) { newState[prop] = this.reducers[prop](state[prop], action); } return newState; } }
和第一种function定义createStore还有一处不一样,subscribe(fn)后执行了fn(this.value),就不须要等到dispatch后更新状态,至关于初始化状态react
const combineReducers = (reducers) => { return (state = {}, action) => { return Object.keys(reducers).reduce( (nextState, key) => { nextState[key] = reducers[key](state[key], action); return nextState; }, {}) } } // import {combineReducers} from 'redux'
使用 combineReducersgit
const {combineReducers} = Redux; const todoApp = combineReducers({todos, visibilityFilter});
预备知识:context, 使用react的context传递store, 做为全局变量github
import PropTypes from 'prop-types'; class Button extends React.Component { render() { return ( <button style={{background: this.context.color}}> {this.props.children} </button> ); } } /* 接收context */ Button.contextTypes = { color: PropTypes.string }; class Message extends React.Component { render() { return ( <div> {this.props.text} <Button>Delete</Button> </div> ); } } class MessageList extends React.Component { /* 传递context */ getChildContext() { return {color: "purple"}; } render() { const children = this.props.messages.map((message) => <Message text={message.text} /> ); return <div>{children}</div>; } } // 传递context MessageList.childContextTypes = { color: PropTypes.string };
经过添加childContextTypes、getChildContext到MessageList, context 会自动传递到子组件,但只有定义了contextTypes的子组件,context才会做为属性传递redux
使用context传递store, 封装Provideride
class Provider extends React.Component { /* called by react */ getChildContext () { return { store: this.props.store } } render () { return this.props.children } } Provider.childContextTypes = { store: PropTypes.object } ReactDOM.render( <Provider store={createStore(todoApp)}> <TodoApp /> </Provider>, document.getElementById('root') ) // import {Provider} from 'react-redux'
class VisibleTodoList extends React.Component { componentDidMount () { const {store} = this.context this.unsubscribe = store.subscribe(() => { this.forceUpdate() }) } componentWillUnmount () { this.unsubscribe() } render () { const {store} = this.context const state = store.getState() return ( <TodoList todos={getVisibleTodos(state.todos, state.visibilityFilter)} onTodoClick={id => store.dispatch({type: 'TOGGLE_TODO', id})} /> ) } } VisibleTodoList.contextTypes = { store: PropTypes.object }
使用react-redux的connect函数将presentation component和redux链接起来函数
1 省去了context的传递(presentation component的contextTypes的定义)this
2 省去了componentDidMount中调用store.subscribe(),componentWillUnmount中调用unsubscribe()spa
3 传递了dispatch
const mapStateToProps = (state, ownProps) => { return { todos: getVisibleTodos( state.todos, state.visibilityFilter) } } const mapDispatchToProps = (dispatch, ownProps) => { return { onTodoClick: id => dispatch({ type: 'TOGGLE_TODO', id }) } } import {connect} from 'react-redux'; const visibleTodoList = connect(mapStateToProps, mapDispatchToProps)(TodoList);
附上connect.js的代码解释,很详细
https://gist.github.com/gaearon/1d19088790e70ac32ea636c025ba424e
将action映射为封装的组件的props,能够简写为对象形式,但要保证参数的顺序要一直,好比下面例子中的id
const mapDispatchToTodoListProps = (dispatch) => ({ onTodoClick (id) { dispatch(toggleTodo(id)) } }) VisibleTodoList = withRouter(connect( mapStateToTodoListProps, actions )(VisibleTodoList)) // mapDispatchToTodoListProps的简写版 VisibleTodoList = withRouter(connect( mapStateToTodoListProps, {onTodoClick: toggleTodo} )(VisibleTodoList))
const addLoggingToDispatch = (store) => { const rawDispatch = store.dispatch if (!console.group) { return rawDispatch } return (action) => { console.group(action.type) console.log('%c prev state', 'color: gray', store.getState()) console.log('%c action', 'color: blue', action) const returnValue = rawDispatch(action) console.log('%c next state', 'color: green', store.getState()) console.group(action.type) return returnValue } } const configureStore = () => { const persistedState = loadState() const store = createStore(todoApp, persistedState) store.dispatch = addLoggingToDispatch(store) return store }