在這裏我將用最淺顯易懂的語言給小夥伴們解釋一下 Redux
, 而且一步步的教你們如何使用 Redux
。css
官網說明:Redux 是 JavaScript 狀態容器,提供可預測化的狀態管理。html
什麼意思呢?就是說隨着頁面複雜化,頁面上的不少數據須要保存,若路由跳轉後,前一個頁面的數據被銷燬會致使數據的丟失。node
若經過 url 傳遞,十分的麻煩,且要將每一個數據傳入組件中,寫起來也是十分的無腦浪費時間。react
若是有個倉庫用來保存整個項目中須要被保留的數據,且組件中能夠直接拿到倉庫的數據。那麼這些數據將時刻保持一致,且清晰。git
首先咱們先用 create-react-app
建立一個 react
項目。npm
create-react-app redux-text
,編程
安裝 redux
:json
npm install --save redux
或者 yarn add redux
redux
npm install --save react-redux
或者 yarn add react-redux
api
這裏有幾個概念須要你們先稍微知道如下:
store
: 咱們存放整個項目的一個倉庫,一個項目只能有一個倉庫,用來存放須要保存的數據。
state
: store
中保存的數據。
action
: 用戶在頁面上的操做是修改不到 store
裏的 state
。頁面上的 view
要發生變化,就要在頁面上經過 action
,去告訴 state
你要變化了。注意:這裏的 action
只是告訴倉庫的數據要變化了,而不是去變化數據!!
reducer
: 接收 action
的請求,執行修改 store
裏的 state
的變化。
接下來咱們來建立幾個項目中須要用到的文件夾和文件,如下是目錄結構:
.
├── .gitignore
├── README.md
├── src
│ └── action
│ ├── oneAction.js
│ ├── twoAction.js
│ └── components
│ └── container
│ ├── pageOne
| ├──index.js
│ └── reducer
│ ├── oneReducer.js
│ ├── twoReducer.js
│ ├── index.js
│ └── App.css
│ └── App.js
│ └── .....
├── node_modules
├── package.json
├── public
複製代碼
建立兩個數據源:
src/reducer/oneReducer.js
和 src/reducer/twoReducer.js
const oneReducer = (
state = {
name: '航航',
address: '福州'
},
action
) => {
switch(action.type) {
case 'setName':
return {
...state,
name: action.payload.name
}
default:
return state;
}
}
export default oneReducer;
複製代碼
這裏的純函數 oneReducer(state, action)
的兩個參數,分別表明了 store
下, 名爲 oneReducer
的 state
和 action
。
state
: 存放了 oneReducer
下的全部數據源和初始值。
action
: 經過不一樣的 action.type
去執行不一樣的操做,修改 state
數據
注意:每次修改 state
, redux
並非去修改原來的 state
,而是返回一個新的 state
, 用新的 state
, 去替換舊的 state
。
當 action.type
爲 setName
時,咱們先將原先的 state
解構出來,並給 name
附上新值。
twoReducer.js
也是如此:
const twoReducer = (
state = {
age: 10,
},
action
) => {
switch(action.type) {
case 'setAge':
return {
...state,
age: 11,
}
default:
return state;
}
}
export default twoReducer;
複製代碼
最後一步,整合全部的 reducer
import { combineReducers } from 'redux';
import OneReducer from './oneReducer';
import TwoReducer from './twoReducer';
export default combineReducers({
OneReducer,
TwoReducer,
})
複製代碼
combineReducers
: 將全部的子 reducer
函數組成對象,提供一個新的 Reducer
函數
咱們寫一個簡單的頁面,將兩個數據源的數據都展現出來。
打開 src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import * as serviceWorker from './serviceWorker';
import PageOne from './container/pageOne';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import Reducer from './reducer';
const store = createStore(Reducer);
ReactDOM.render(
<Provider store={store}> <PageOne/> </Provider>
, document.getElementById('root'));
serviceWorker.unregister();
複製代碼
createStore
: 建立倉庫,用來存放 recuder
下的全部數據源。
Provider
: redux 提供的一個組件,將 store
傳遞給其自組件。簡單說就是在整個項目的最外層包上一個組件,放進一個 store
, 這樣就將 store
綁定進了項目中。
那麼如今咱們來專一於頁面 /src/container/pageOne/index.js
的編寫:
import React from 'react';
import { connect } from 'react-redux';
class PageOne extends React.Component {
render() {
const { oneReducer, twoReducer } = this.props;
const { name = '', address = '' } = oneReducer;
const { age = 0 } = twoReducer;
console.log(oneReducer, twoReducer);
return (
<div> <div>name: {name}</div> <div>address: {address}</div> <div>age: {age}</div> </div>
)
}
}
const mapStateToProps = state => {
const { oneReducer, twoReducer } = state;
return {
oneReducer,
twoReducer,
}
}
export default connect(mapStateToProps)(PageOne);
複製代碼
細細講解如下上面的知識點哈:
mapStateToProps
: 獲取 store
倉庫下的數據源,這裏能夠打印如下 state
, 看下輸出。
connect
: 由 React Redux 庫提供的方法,將當前 Redux store state 映射到展現組件 props 中。
connect
作了性能優化,能夠避免不少沒必要要的重複渲染,好比,當 state
數據變更時,沒必要編寫 shouldComponentUpdate
方法來更新展現數據。
那麼至此,倉庫中的數據咱們就能夠經過 this.props
獲取到。
store
的數據是沒法被修改的,這個保證了數據的穩定性。因此 redux 拋出一個 store.dispatch(action)
的事件,提供用戶修改 store
數據。
因此咱們繼續修改上面的 pageOne/index.js
頁面(簡寫):
class PageOne extends React.Component {
changeName = (val) => {
this.props.dispatch({
type: 'setName',
payload: {
name: val
}
})
}
render() {
return (
<div> <div>name: {name}</div> <div>address: {address}</div> <div>age: {age}</div> <button onClick={ () => { this.changeName('change_name') }}>修更名字</button> </div>
)
}
}
複製代碼
如今去嘗試如下執行按鈕點擊事件吧。
好了,那麼至此一個狀態管理的操做就完成了。細心的小夥伴會發現 action
好像沒有用到?
那麼這個 action
究竟是作什麼的?
在我理解,就是把 dispatch
中的內容放到 action
中。
編寫 src/action/oneAction.js
export const setName = (name) => ({
type: 'setName',
payload: {
name,
}
})
export const setAge = (age) => ({
type: 'setAge',
age
})
複製代碼
修改下 pageOne/index.js
頁面(簡寫):
import { setName } from '../../action/oneAction';
class PageOne extends React.Component {
changeName = (val) => {
this.props.dispatch(setName(val))
}
...
}
複製代碼
執行如下操做是否是發現也能夠呢?那麼爲何咱們還要來編寫 action
呢?
在我理解:是爲了更加註重 MVC
的模式,View 就應該注重 View 的展現邏輯,因此與 UI 無關的邏輯操做就交給 redux 來處理,體現代碼分層、職責分離的編程思想。
因爲不少時候執行dispatch並不只僅是當即去更新reducer,這時須要執行其餘函數來知足項目需求,這些函數就是中間件,最後執行過一系列中間件後再去執行reducer
若是咱們調取服務端的接口,存在時間的延遲;或者說我想在 reducer 中也去調取其餘 reducer 的操做,行不行?
咱們來實驗一下:
咱們 oneAction.js
文件中再增長一個方法:
export const allChange = () => dispatch => {
dispatch(setName('all_hang'));
dispatch(setAge(10010));
}
複製代碼
pageOne.js
頁面上增長一個點擊事件(簡寫):
import { setName, allChange } from '../../action/oneAction';
class PageOne extends React.Component {
changeAll = () => {
this.props.dispatch(allChange())
}
render() {
return (
...
<div>
<button onClick={ () => { this.changeAll() }}>修改所有</button>
</div>
...
)
}
}
複製代碼
當咱們點擊按鈕就發現報錯了。看下 console
的報錯信息: Use custom middleware for async actions.
翻譯過來的意思是:使用自定義中間件進行異步操做。
說明在 reducer
中調用別的 reducer
的方法是能夠的,可是由於咱們缺乏中間件,因此執行報錯,如今咱們來把中間件加上:
修改代碼前咱們要編輯 cli,新增一條命令
yarn add redux-thunk
或 npm install --save redux-thunk
導入 redux-thunk
庫。
編輯 src/index.js
(簡寫):
如下爲簡寫代碼,頁面上原先的內容不刪除,只是新增了幾行代碼和修改了 createStore
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
const store = createStore(
Reducer,
applyMiddleware(
thunkMiddleware
)
);
...
...
複製代碼
那麼如今去執行如下 allChange()
事件看看效果,是否是發現頁面不報錯了,且 name
和 age
的數據也已經作了修改。
那麼咱們來說解如下 applyMiddleware
和 thunkMiddleware
什麼是 middleware
:
在 redux
中 middleware
是 發送 action
和 action
到達 reducer
之間的第三方擴展,middleware
是架在 action
和 store
之間的一座橋樑。
applyMiddleware
能夠看看官網的解釋。
Middleware
可讓你包裝 store
的 dispatch()
方法來達到你想要的目的。
最後來一個補充:
若是你想每次 dispatch
都可以在 console
打印日誌的話,手寫會很是的繁瑣。
那麼 redux 也提供了這樣一箇中間件,幫助咱們打印日誌。
鍵入 yarn add redux-logger
或者 npm install --save redux-logger
來導入 redux-logger
庫。
在 src/index.js
下加入如下代碼:
import { createLogger } from 'redux-logger'
const loggerMiddleware = createLogger()
const store = createStore(
Reducer,
applyMiddleware(
thunkMiddleware,
loggerMiddleware
)
);
複製代碼
好了,去嘗試一下吧~