學習 Redux 是個十分痛苦的過程齐媒,因為你有可能不知道 Redux 和 React-Redux 是兩個不同的東西况木,而且一堆看起來很新的概念總能把新手繞暈忌卤。
這篇文章將從一個簡單的例子開始講 Redux 到底要怎么使用像啼。建議先學習 EventHub 或者看我的另一篇文章 React 的兄弟組件通信啊胶,因為 Redux 的主要思想就是 EventHub 思想。
純 JS + Redux
先說下需求慌随,首先要有一個數(shù)字芬沉,點擊按鈕數(shù)字加1,沒了阁猜。不過這個過程要使用到 Redux丸逸,也就是說這個數(shù)字要放在全局變量中。
這里就用一個簡單的 index.html 寫就完了剃袍。HTML 代碼如下:
<div id="app"></div>
接下來寫 JS黄刚。
function add() {
store.dispatch({ type: 'add', payload: 1 }) // 1. dispatch 一個 action
}
function render() {
let app = document.querySelector('#app')
app.innerHTML = `
Clicked: <span id="value">${store.getState()}</span> times
<div>
<button id="add" onclick="add1()">+1</button>
</div> `
}
function stateChanger(state, action) {
if (state === undefined) {
return 0
}
else {
if (action.type === 'add') {
let newState = state + action.payload
return newState // 2. 根據(jù)操作生成新的 state 觸發(fā)一個事件
}
}
}
let store = Redux.createStore(stateChanger)
render(store)
store.subscribe(render) // 3. 接收到事件,重新 render
這里說明一下運行的步驟:
- 首先運行
render()
將標簽都 append 到document
上民效,并在按鈕上綁定add
回調(diào) - 點擊了按鈕執(zhí)行
add()
憔维,然后 dispatch 一個 action(相當于觸發(fā)了名為“add”的一個事件) - 因為在
Redux.createStore(stateChanger)
時,Redux 會監(jiān)聽 action(也就是事件)的觸發(fā)畏邢,所以會執(zhí)行stateChanger
里的代碼 - 因為在
store.subscribe(render)
在 if-else 里修改了新的 state 后业扒,Redux 會重新執(zhí)行render()
函數(shù),從而更新整個頁面
Redux 的用法就是這么簡單舒萎!但是為什么我們看官方檔看得想死呢程储?因為加了 React,后來的所有看起來很變態(tài)的使用方法都是為了解決:怎么讓代碼更好分離臂寝,怎么獲取全局變量等章鲤。思想還是那個思想。
React + Redux
現(xiàn)在我們正式加入 React咆贬,使用官方提供的create-react-app來創(chuàng)建應用败徊。App 組件寫成這樣:
// App.js
class App extends Component {
add1() {
this.props.store.dispatch({ // 1. dispatch 一個 action
type: 'add',
payload: 1
})
}
render() {
return (
<div className="App">
Clicked: <span id="value">{this.props.value}</span> times
<div>
<button id="add1" onClick={this.add1.bind(this)}>+1</button>
</div>
</div>
)
}
}
export default App
在入口文件去初始化 Redux:
const stateChanger = (state, action) => {
if (state === undefined) {
return 0
}
else {
if (action.type === 'add') {
let newState = state + action.payload
return newState // 2. 根據(jù)操作生成新的 state 觸發(fā)一個事件
}
else {
return state
}
}
}
function render() {
ReactDOM.render(
<App
value={store.getState()}
store={store}
/>,
document.getElementById('root')
)
}
const store = createStore(stateChanger)
store.subscribe(render) // 3. 接收到事件,重新 render
render()
感覺好像沒什么變化呀素征,不就將一個 index.html 分成了 index.js 和 App.js 么集嵌?但是這里涉及到怎么去獲取 store
的問題萝挤。
如果一兩個組件就用 props
來傳 store
就好了,但是如果組件很深根欧,那么 store
就要像傳家保一樣一層層往下傳怜珍,十分麻煩。
為了解決這個問題凤粗, React 社區(qū)又推出了一個新的工具:React-Redux酥泛。注意這個和 Redux 沒有太大關(guān)系。為了說明這兩個是不一樣嫌拣,我放下他們的官網(wǎng):Redux 官網(wǎng)柔袁,React-Redux 官網(wǎng)。
React + Redux + React-Redux
先說明這個工具就只是用來讓別的組件可以訪問到 store 而已异逐,所以它只有 4 個API捶索,其中主要我們要用的就 2 個:Provider, connect。簡單理解:
- Provider:將 store 放在頂層組件
- connect:將 store 里的數(shù)據(jù)和 dispatch action (也就是觸發(fā)事件)和當前組件綁定灰瞻,使得該組件可以自由訪問和修改 store
現(xiàn)在修改上面兩個文件:
// index.js
const stateChanger = (state, action) => {
if (state === undefined) {
return { n: 0 }
}
else {
if (action.type === 'add') {
let newState = {
n: state.n + action.payload
}
return newState
}
else {
return state
}
}
}
const store = createStore(stateChanger)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
下面的 App.js
class App extends Component {
render() {
return (
<div className="App">
Clicked: <span id="value">{this.props.n}</span> times
<div>
<button id="add" onClick={this.props.add.bind(this)}>+1</button>
</div>
</div>
)
}
}
// 將部分 store 里的 state 映射到 props 上
function mapStateToProps(state) {
return {
n: state.n
}
}
// 將 dispatch action 相關(guān)操作映射到 props 上
function mapDispatchToProps(dispatch) {
return {
add: () => {
dispatch({ type: 'add', payload: 1 })
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
來對比一下兩份代碼:
少了哪些腥例?
- 不用
store.subscribe(render)
,Provider 會在更新值后自動重新渲染 - 不用在組件 App 里寫回調(diào)函數(shù)了酝润,在
mapDispatchToProps
里聲明事件燎竖,這個事件會就負責調(diào)用dispatch(action)
的
多了哪些?
- 要在 App 組件外再去套一層
<Provider/>
- 將 App 組件和 state要销,調(diào)用 dispatch 的函數(shù)連接起來构回。連接(映射)后的結(jié)果是,所有 state 數(shù)據(jù)和調(diào)用 dispatch 的函數(shù)都可以從
this.props
得到
這樣就解決了組件訪問疏咐,修改 store 里的 state 問題纤掸。唉,繞了一大圈就為了搞這些事凳鬓。
那些變態(tài)概念
Redux 為什么這么難茁肠,是因為它將很簡單的概念給了個新名字罷了。
Redux
- Store:存放全局數(shù)據(jù)的一個東西缩举,但是要通過
store.getState()
來獲取全局數(shù)據(jù) - State:全局數(shù)據(jù)
- Action:對應 EventHub 里的事件,
actionType
就是事件名匹颤,payload
就是傳入的數(shù)據(jù) - Dispatch:觸發(fā)事件仅孩,如
dispatch({ type: 'add', payload: 1})
,就是指觸發(fā)一個名為“add”的事件印蓖,并帶上了數(shù)據(jù) 1 - Reducers:對應觸發(fā)事件后的回調(diào)函數(shù)辽慕,如我們平時聽到的
onError = xxx
,這個 xxx 就是 Reducer赦肃,這個例子的 Reducer 就是stateChanger
React-Redux
- Provider:可以看成一個包住最外面 App 的一個標簽溅蛉,這個標簽傳入 store 后公浪,通過某些方法所有組件都可以訪問到 store
- mapStateToProps:將存在 store 里的數(shù)據(jù)放在這個組件的 props 上
- mapDispatchToProps:將需要調(diào)用 dispatch 的函數(shù)放在這個組件的 props 上
- connect:將上面兩個東西和這組件聯(lián)系起來
是不是很煩,嗯船侧,我也是這么覺得的欠气,明明很簡單的概念,就是不好好說話镜撩。
(完)