shopping-cart
src/index.js
import React from 'react'
import { render } from 'react-dom'
import { createStore, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import { createLogger } from 'redux-logger'
import thunk from 'redux-thunk'
import reducer from './reducers'
import { getAllProducts } from './actions'
import App from './containers/App'
const middleware = [ thunk ];
if (process.env.NODE_ENV !== 'production') {
middleware.push(createLogger());
}
const store = createStore(
reducer,
applyMiddleware(...middleware)
)
store.dispatch(getAllProducts())
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
actions/index.js
import shop from '../api/shop'
import * as types from '../constants/ActionTypes'
const receiveProducts = products => ({
type: types.RECEIVE_PRODUCTS,
products: products
})
//这个很有意思返回的居然不是上面的{type:''}对象,而是个函数
export const getAllProducts = () => dispatch => {
shop.getProducts(products => {
dispatch(receiveProducts(products))
})
}
getAllProducts action返回的是函数,匪夷所思,开始怀疑,是不是reducer第二个参数不是action单纯的对象,debug代码后才知道这和thunk中间件有关
redux-thunk createStore.js第二张图可以清晰的看到action依旧是单纯的包含type属性的普通对象
发现一小哥也是产生了同样的疑问由redux 异步数据流引发的血案
1. bindActionCreators
TodoActionCreators.js
export function addTodo(text) {
return {
type: 'ADD_TODO',
text
};
}
export function removeTodo(id) {
return {
type: 'REMOVE_TODO',
id
};
}
SomeComponent.js
// 这样做行得通:
let action = TodoActionCreators.addTodo('Use Redux');
dispatch(action);
由此可见dispatch是个带有type属性的对象
bindActionCreators源码:
function bindActionCreator(actionCreator, dispatch) {
return (...args) => dispatch(actionCreator(...args))
}
//参数可为单个actionCreator函数,也可以为多个actionCreator函数组成的对象
export default function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
if (typeof actionCreators !== 'object' || actionCreators === null) {
throw new Error(
`bindActionCreators expected an object or a function, instead received ${actionCreators === null ? 'null' : typeof actionCreators}. ` +
`Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
)
}
const keys = Object.keys(actionCreators)
const boundActionCreators = {}
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}
app.js
<MainSection todos={todos} actions={actions} />
const mapDispatchToProps = dispatch => ({
actions: bindActionCreators(TodoActions, dispatch)
})
rodomvc mainsection.js
//这样组件就直接调用函数了,而不需要再去dispatch一个actionCreator
handleClearCompleted = () => {
this.props.actions.clearCompleted()
}
2. action和请求是怎么建立的关系?也就是和reudcers怎么建立关系?
今天在看todmvc实例的时候再次看到了combineReducers
import { combineReducers } from 'redux'
import todos from './todos'
const rootReducer = combineReducers({
todos
})
export default rootReducer
然后带着疑问去查看redux文档Reducer逐层对reducer的拆分进行了讨论,最后就是将多个reducer函数使用combineReducers组合成了一个大的reducer函数,查阅redux createStore代码:
export default function createStore(reducer, preloadedState, enhancer) {
let currentReducer = reducer//唯一的reducer函数
let currentState = preloadedState//初始全局state
function dispatch(action) {
try {
isDispatching = true
//调用注册好的reducer函数,action是包含type属性的纯对象,
//currentReducer会返回新的state,从而达到更新state的目的
//currentReducer会根据不同的action的type去更新对应的state
//也就是在这里action和reducer建立了联系,reducer是在
//createStore调用的时候传递进来的,见下文
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
const listeners = currentListeners = nextListeners
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
}
currentReducer就是被多个reducer函数组合后的函数,说白了就是使用switch去判断action.type更新对应的state
createStore的调用
import reducer from './reducers'
//将组合后的reducer函数传到createStore内
const store = createStore(reducer)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
简化版的redux 从0实现一个tinyredux说的比较清楚
源码:
export default function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
}
const finalReducerKeys = Object.keys(finalReducers)
//返回一个拼装后的reducer
return function combination(state = {}, action) {
if (shapeAssertionError) {
throw shapeAssertionError
}
if (process.env.NODE_ENV !== 'production') {
const warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache)
if (warningMessage) {
warning(warningMessage)
}
}
let hasChanged = false
const nextState = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
//这个地方循环判断前后两次局部state是不是同一个数据
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
//如果有一条局部state更新了,那么整个state都会是新的nextState,
//如果全部的局部state都没更新,那么就返回原来的state,不对全局的state进行更新
return hasChanged ? nextState : state
}
}
combineReducers和没有使用该函数的前后对比,按照官网的说法
你也可以给它们设置不同的 key,或者调用不同的函数。下面两种合成 reducer 方法完全等价
const reducer = combineReducers({
a: doSomethingWithA,
b: processB,
c: c
})
function reducer(state = {}, action) {
return {
a: doSomethingWithA(state.a, action),
b: processB(state.b, action),
c: c(state.c, action)
}
}
3.state更新后react组件是怎么更新的?
前面两条说了dispatch(action)对state进行更新,那么state更新了,react组件是怎么更新的呢?下面就这个问题展开讨论:
网友评论