跳至主要内容

[Redux] Redux API 筆記

Top Level Exports

createStore

createStore(reducer, [preloadedState], [enhancer])

  • store.getState():取得當前所有存在 state 的內容。
  • store.dispatch(action):dispatch 的 action 會傳送到 reducer,是唯一用來改變 state 的方式。
  • store.subscribe(listener):在每次 action 被 dispatch 的時候都會呼叫這個 listener。
  • store.replaceReducer(nextReducer)
import { createStore } from 'redux';
import rootReducer from './reducers';

// createStore
const store = createStore(rootReducer);

store.getState(); // 取得當前所有存在 state 的內容。

// dispatch action
store.dispatch({
type: 'INCREMENT',
payload: 0,
});

// subscribe(listener):每次 action dispatch 時都會呼叫到這個 callback listener
store.subscribe(() => {
document.body.innerText = store.getState();
});

createStore @ Redux API

原始碼結構

const createStore = (reducer) => {
let state;
let listeners = [];

const getState = () => state;
const dispatch = (action) => {
state = reducer(state, action); // 重要!!
listeners.forEach((l) => l());
};

const subscribe = (listener) => {
listeners.push(listener);

return () => {
listeners = listeners.filter((l) => l !== listener);
};
};

dispatch({});

return { getState, dispatch, subscribe };
};

combineReducer

combineReducers(reducers)

import { combineReducers } from 'redux';

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

combineReducer @ Redux API

實際上 combineReducers 是另一個 reducer,而這個 reducer 包了其他原本的 reducers,所以寫成下面這樣是一樣的效果:

/**
* combineReducer 的效果和下面的寫法一樣
* visibilityFilterReducer, todosReducer 都是 reducers
**/
const todoApp = function (state = {}, action) {
const { visibilityFilter, todos } = state;
return {
// 一旦 dispatch() 後,這裡面有寫到的 reducers 都會被呼叫到
visibilityFilter: visibilityFilterReducer(visibilityFilter, action),
todos: todosReducer(todos, action),
};
};

store.dispatch() 時,會執行 createStore(<combineReducers>) 時代入的 reducer,在 combineReducers 中,又會去呼叫所有在裡面有寫到的其他 reducers,因此所有的 reducers 都能接到 dispatch 的這個 action

也就是說,下面這兩種寫法是一樣的:

// 使用 combineReducers
const reducer = combineReducers({
a: doSomethingWithA, // A Reducer
b: processB, // B Reducer
c: calculateC, // C Reducer
});

// 沒使用 combineReducer
function reducer(state = {}, action) {
const { a, b, c } = state;
return {
a: doSomethingWithA(a, action),
b: processB(b, action),
c: calculateC(c, action),
};
}

bindActionCreators

bindActionCreators(actionCreators, dispatch)

原本需要 dispatch(action),但透過 bindActionCreator 後,會變成把 dispatch() 包在原本的 action 中,因此可以直接呼叫該 action 就會發出 dispatch。

用法一:搭配 mapDispatchToProps 使用

原本的寫法:

import { selectBook, deleteBook } from '../actions/index';

const mapDispatchToProps = (dispatch, ownProps) => ({
selectBook: (id) => dispatch(selectBook(id)),
deleteBook: (id) => dispatch(deleteBook(id)),
});

搭配 bindActionCreators 使用(等同於上面的寫法):

import { bindActionCreators } from 'redux';
import { selectBook, deleteBook } from '../actions/index';

const mapDispatchToProps = (dispatch) =>
bindActionCreators(
{
selectBook,
removeBook: deleteBook, // 可以改變 function 的名字
},
dispatch,
);

用法二:變成 boundActionCreators

// someContainer.js

import { bindActionCreators } from 'redux';
import * as TodoActionCreators from './TodoActionCreators';

class TodoListContainer extends Component {
constructor(props) {
super(props);

const { dispatch } = props;

// 使用 bindActionCreators
this.boundActionCreators = bindActionCreators(TodoActionCreators, dispatch);
console.log(this.boundActionCreators);
}

render() {
// 放入綁定好的 boundActionCreators
return <TodoList todos={todos} {...this.boundActionCreators} />;
}
}

bindActionCreators @ Redux API