文章

React 與 Redux

React 與 Redux

React 是一個用於構建用戶界面的庫,而 Redux 是一個預測性狀態管理工具,適合處理大型應用中的全局狀態。在大型 React 應用中,組件之間的狀態共享變得複雜,這時候 Redux 就能夠很好地管理整個應用的狀態。

Redux 提供了一個全局的 store,應用的所有狀態都存儲在這個 store 中,並通過 actions 和 reducers 來管理狀態變化。


1. Redux 基本概念

在開始使用 Redux 前,我們需要了解其核心概念:

  • Store:存放應用所有狀態的單一來源。它是一個 JavaScript 對象。
  • Action:一個普通的 JavaScript 對象,用來描述要發生的事件。每個 action 必須有一個 type 屬性,表示這個行為的類型。
  • Reducer:一個純函數,接收當前的狀態和 action,然後返回新的狀態。它是更新 store 的唯一方式。
  • Dispatch:執行 action 的方法,調用它會觸發 reducer,從而改變 store 中的狀態。
  • Selector:用於從 store 中獲取狀態的函數。

2. 安裝 Redux 和 React-Redux

在 React 應用中整合 Redux,需要安裝 reduxreact-redux

1
npm install redux react-redux

3. 創建 Redux Store

首先,我們需要創建一個 Redux store。讓我們從定義一個 reducer 開始,然後將其應用到 store。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// counterReducer.js
const initialState = {
  count: 0
};

function counterReducer(state = initialState, action) {
  switch (action.type) {
    case 'INCREMENT':
      return {
        ...state,
        count: state.count + 1
      };
    case 'DECREMENT':
      return {
        ...state,
        count: state.count - 1
      };
    default:
      return state;
  }
}

export default counterReducer;

然後我們在應用中創建 store:

1
2
3
4
5
6
7
// store.js
import { createStore } from 'redux';
import counterReducer from './counterReducer';

const store = createStore(counterReducer);

export default store;

4. 使用 Provider 將 Store 連接到 React 應用

在 React 應用中,我們需要使用 Provider 組件來將 Redux store 提供給整個應用,這樣所有子組件都可以訪問 store。

1
2
3
4
5
6
7
8
9
10
11
12
13
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import store from './store';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

5. 在組件中使用 Redux

現在我們可以在 React 組件中使用 Redux 的狀態和 dispatch 功能。React-Redux 提供了 useSelectoruseDispatch 這兩個 hooks 來幫助我們讀取和修改 Redux 狀態。

5.1 讀取 Redux 狀態:useSelector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Counter.js
import React from 'react';
import { useSelector } from 'react-redux';

function Counter() {
  const count = useSelector((state) => state.count);

  return (
    <div>
      <h1>計數: {count}</h1>
    </div>
  );
}

export default Counter;

useSelector 可以讓你從 Redux store 中讀取狀態。

5.2 發送 Actions:useDispatch

要更新狀態,我們需要使用 useDispatch 來發送 actions。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Counter.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';

function Counter() {
  const count = useSelector((state) => state.count);
  const dispatch = useDispatch();

  return (
    <div>
      <h1>計數: {count}</h1>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>增加</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>減少</button>
    </div>
  );
}

export default Counter;

這裡我們通過 dispatch 發送 INCREMENTDECREMENT 的 actions 來更新計數。


6. Action Creators

在大型應用中,直接在組件中寫 action 對象會導致代碼冗長。通常,我們會使用 Action Creators 來封裝 action 對象。

1
2
3
4
5
6
7
8
9
10
11
12
// actions.js
export const increment = () => {
  return {
    type: 'INCREMENT'
  };
};

export const decrement = () => {
  return {
    type: 'DECREMENT'
  };
};

然後在組件中使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Counter.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './actions';

function Counter() {
  const count = useSelector((state) => state.count);
  const dispatch = useDispatch();

  return (
    <div>
      <h1>計數: {count}</h1>
      <button onClick={() => dispatch(increment())}>增加</button>
      <button onClick={() => dispatch(decrement())}>減少</button>
    </div>
  );
}

export default Counter;

這樣,我們就將 actions 的定義從組件中抽離出來,讓代碼更加清晰。


7. 中間件與異步 Actions

Redux 自身只能處理同步的狀態變化,而現代應用中經常需要處理異步請求,比如從 API 獲取數據。這時,我們可以使用 Redux Thunk 這個中間件來幫助處理異步 actions。

7.1 安裝 Redux Thunk

1
npm install redux-thunk

7.2 配置 Redux Thunk

1
2
3
4
5
6
7
8
// store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import counterReducer from './counterReducer';

const store = createStore(counterReducer, applyMiddleware(thunk));

export default store;

7.3 創建異步 Action

我們可以創建一個異步的 action 來從 API 獲取數據,並根據數據更新 Redux 狀態。

1
2
3
4
5
6
7
8
9
10
// actions.js
export const fetchData = () => {
  return (dispatch) => {
    fetch('https://api.example.com/data')
      .then((response) => response.json())
      .then((data) => {
        dispatch({ type: 'SET_DATA', payload: data });
      });
  };
};

然後在 reducer 中處理該 action:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// counterReducer.js
const initialState = {
  count: 0,
  data: []
};

function counterReducer(state = initialState, action) {
  switch (action.type) {
    case 'INCREMENT':
      return {
        ...state,
        count: state.count + 1
      };
    case 'DECREMENT':
      return {
        ...state,
        count: state.count - 1
      };
    case 'SET_DATA':
      return {
        ...state,
        data: action.payload
      };
    default:
      return state;
  }
}

export default counterReducer;

8. Redux DevTools

在開發過程中,Redux 提供了 Redux DevTools,可以讓你監控應用狀態變化。使用 DevTools 你可以輕鬆地查看每一個 action 的觸發及其對狀態的影響。

8.1 配置 Redux DevTools

1
2
3
4
5
6
7
8
9
// store.js
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import counterReducer from './counterReducer';

const store = createStore(counterReducer, composeWithDevTools(applyMiddleware(thunk)));

export default store;

9. 總結

  • Redux 提供了一個全局狀態管理的解決方案,適合大型應用中處理複雜的狀態邏輯。
  • React-Redux 提供了 useSelectoruseDispatch 兩個 hooks 來讓 React 組件讀取和更新 Redux 狀態。
  • 可以通過 Action Creators 來封裝 actions,使代碼更加清晰。
  • Redux Thunk 可以幫助我們處理異步操作,將數據請求結果集成到 Redux 中。
  • Redux DevTools 則讓我們更方便地

調試和監控應用中的狀態變化。

這樣,React 與 Redux 的結合能夠讓應用中的狀態管理變得更有結構性和可維護性。

本文章以 CC BY 4.0 授權