安裝
npm install --save redux@4.X
使用之前
瞭解 Redux 的一些核心概念:
- action
- reducer
- store
使用
引入
import { createStore } from 'redux';
// 或
const { createStore } = require('redux');
定義一些 action creators:
function increment() {
return { type: 'INCREMENT' };
}
function decrement() {
return { type: 'DECREMENT' };
}
定義一個 reducer,用來根據 action 的 type 生成新的 store:
function counter(state = 0, action) {
switch(action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default :
return state;
}
}
根據上面定義的 reducer 建立 store:
const store = createStore(counter);
模擬 action:
let currentValue = store.getState();
const listener = () => {
const previousValue = currentValue;
currentValue = store.getState();
console.log(` 之前的值:${previousValue},現在的值:${currentValue}`);
};
// 訂閱監聽函式,動作分發後回撥
store.subscribe(listener);
// 分發動作
store.dispatch(increment());
store.dispatch(increment());
store.dispatch(decrement());
結果:
使用 react-redux 與 React 整合
安裝
npm install --save react-redux@5.X react@16.X react-dom@16.X
使用之前
先定義一些 React 元件:
// Counter.js
import React from 'react';
import { connect } from 'react-redux';
import * as ActionCreators from './ActionCreators';
function Counter({value, increment, decrement}) {
return (
<p>
Click: {value} times {' '}
<button onClick={increment} >+</button>{' '}
<button onClick={decrement} >-</button>{' '}
</p>
);
}
export default connect(
state => ({ value: state}),
ActionCreators
)(Counter);
在 ActionCreators.js
檔案中定義動作建立函式:
// ActionCreators.js
export function increment() {
return { type: 'INCREMENT' };
}
export function decrement() {
return { type: 'DECREMENT' };
}
使用 react-redux
// App.js
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import Counter from './Counter';
// ... 省略上面已經定義好的 reducer 和 store
ReactDOM.render(
<Provider store={store}>
<Counter />
</Provider>,
document.getElementById('root')
);
可以看到,我們使用了一個 react-redux 提供的 Provider
元件包裹了我們需要傳入 state 的元件(即 Counter),並且在將 store 傳入。在 Counter 中使用 react-redux 提供的 connect
將 Counter 組合成了一個高階元件,將 state 和 action 傳入了 Counter 中的 props
,這樣我們就可以在 Counter 元件中使用 state 和分發 action 了,可以直接通過 action creators 發起 action,跳過了 dispatch
這個冗餘的過程,因為 react-redux 中的 connect
已經幫我們做過了。
結果
用後思考
當然,這個例子非常簡單,根本不需要用到 redux,因為這個例子只有一個元件。如果你有過使用 props
來層層巢狀傳遞父級容器元件的 state
給子級展示元件的話,你一定非常厭惡這種不那麼優雅的寫法,不過使用 react-redux 就不需要這麼麻煩了,我們只需要使用 react-redux 提供的 connect
將元件組合成一個高階元件匯出,即可在子級展示元件內部通過 props
使用父級容器元件的 state
,而父級容器元件的 state
由 redux 中的 store
來提供。例如,我們將 App.js
改成多層巢狀的元件:
// App.js
// ...
ReactDOM.render(
<Provider store={store}>
<SomeComponent />
</Provider>,
document.getElementById('root')
);
function SomeComponent(props) {
return (
<div>
<h1>SomeComponent</h1>
<OtherComponent />
</div>
);
}
function OtherComponent(props) {
return (
<div>
<h2>OtherComponent</h2>
<Counter />
</div>
);
}
上述元件應該在各自獨立的檔案中定義,為了節省篇幅,因此我把它們都寫在 App.js
這個檔案中。
結果
可以看到,我們巢狀了兩層元件,但是 Counter 元件不需要層層巢狀來獲取祖先級元件的 state,我們只需要將所有的 state 提取到 Provider
元件中,然後使用 connect
將子級展示元件組合成高階元件,即可各子級展示元件之間共享使用父級容器元件的 state,避免了醜陋的巢狀過多。
我們學到了什麼❓
通過使用 redux 和 react-redux 來集中管理 state,以更加優雅的方式傳遞父級容器元件的 state。