redux必知基础概念

redux无疑已经是react应用的标配了,也确实好用,这里就redux的基础点说明一下,包括createStore, combineReducers,provider, connect

createStore()  

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和initialState

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后更新状态,相当于初始化状态

combineReducers()

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'

使用 combineReducers

const {combineReducers} = Redux;

const todoApp = combineReducers({todos, visibilityFilter});

Provider

预备知识:context, 使用react的context传递store, 作为全局变量

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
};

通过添加childContextTypesgetChildContext到MessageList,  context 会自动传递到子组件,但只有定义了contextTypes的子组件,context才会作为属性传递

使用context传递store, 封装Provider

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
}

Connect

使用react-redux的connect函数将presentation component和redux连接起来

1 省去了context的传递(presentation component的contextTypes的定义)

2 省去了componentDidMount中调用store.subscribe(),componentWillUnmount中调用unsubscribe()

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

mapDispatchToProps

将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))

MiddleWare

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
}

猜你喜欢

转载自my.oschina.net/u/2510955/blog/1610791
今日推荐