為什麼用Redux
一些小型專案,只使用 React 完全夠用了,資料管理使用props、state即可,那什麼時候需要引入Redux呢? 當渲染一個元件的資料是通過props從父元件中獲取時,通常情況下是A –> B,但隨著業務複雜度的增加,有可能是這樣的:A –> B –> C –> D –> E,E需要的資料需要從A那裡通過props傳遞過來,以及對應的E –> A逆向傳遞callback。元件BCD是不需要這些資料的,但是又必須經由它們來傳遞,這樣不太友好,而且傳遞的props以及callback對BCD元件的複用也會造成影響。或者兄弟元件之間想要共享某些資料,也不是很方便傳遞、獲取等。諸如此類的情況,就有必要引入Redux`了。
注:其實 A --> B --> C --> D --> E 這種情況,React不使用props層層傳遞也是能拿到資料的,使用Context即可。
Redux 的三大基本原則
redux的核心概念就是store、action、reducer
約法三章
State 是隻讀的 react文件中說惟一改變 state 的方法就是觸發 action,奇怪的是 我們說State是隻讀的,但是為啥現在又說可以修改了呢,其實我們並不是直接修改state,action”是用來向 store 表示要修改 state 的載體。所以我們並沒有直接設定state的方法,使用了dispatch(action) 傳送 action 至 store 是唯一改變 state 的方式
store
- UI唯一資料來源;
- 維持應用的 state;
- 提供 getState() 方法獲取 state;
- 提供 dispatch(action) 方法更新 state;
- 通過 subscribe(listener) 註冊監聽器;
通過 subscribe(listener) 返回的函式登出監聽器。
action
必須擁有type屬性,用來描述發生什麼,action通過reducer處理生成newState後才能夠更改store資訊。但是為了更好的語義程式設計,Redux通過語句store.dispatch(action)來更新store,reducer對action的處理在內部處理。
reducer
僅僅只是根據action.type處理一下需要更新的state
建立目錄
建立Store模組 資料來源
//store/index.js
/*引入createStore 是用來建立一個資料來源物件儲存我們的資料的*/
import { createStore, applyMiddleware, compose } from 'redux';
import { createLogger } from 'redux-logger';
//引用資料來源
// store是引用的reducer
// action會觸發reducer
import allreducers from './reducers/index';
//資料處理後會返回給資料來源 才能取到最新的資料 在根元件index裡獲取
const logger = createLogger();
const store = createStore(
allreducers,
applyMiddleware(logger)
)
console.log(store)
function listerner () {
store.getState();
}
store.subscribe(listerner);
export default store;複製程式碼
傳入Store
Store儲存了整個應用的單一狀態樹,所有容器元件都需要從store中讀取,我們可以store作為屬性傳遞給每個元件,子元件通過props獲取,但是如果巢狀過深,寫起來會很麻煩。還好,react-redux提供一個叫provider的元件,他可以讓所有元件都可以訪問到store(他的實現原理實際上是利用了react的context功能),而不必顯示的一層層傳遞了。
import { Provider } from 'react-redux'
import store from './store';
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
{routes }
</BrowserRouter>
</Provider>,
document.getElementById('app')
);複製程式碼
建立Reducer 處理業務資料
//reducers/index.js
import * as Types from 'src/store/constants'
import { combineReducers } from 'redux';
const initState = {
isLogin: "未登入",
themecolor: "red",
count: 0
}
const loginReducer = (state = initState.isLogin ,action) => {
switch (action.type) {
case Types.LOGIN_TYPE:
return state = action.isLogin
default:
return state
}
}
const themeReducer = (state = initState.themecolor ,action) => {
switch (action.type) {
case Types.THEME_TYPE:
return state = action.themecolor
default:
return state
}
}
const counterReducer = (state = initState.count, action) => {
switch(action.type) {
case Types.ADD_TYPE: return state + 1;
default: return state;
}
}
export default combineReducers({
isLogin:loginReducer,
themecolor:themeReducer,
count:counterReducer
})複製程式碼
注意:combineReducers() 函式的作用是把多個 reducer 合併成一個最終的 reducer 函式。
常量統一儲存
//constants/index.js
/*常量統一儲存,便於管理*/
export const LOGIN_TYPE = "LOGIN_TYPE"
export const THEME_TYPE = "THEME_TYPE"
export const ADD_TYPE = "ADD_TYPE"複製程式碼
建立Action模組
actions/index.js
import * as Types from 'src/store/constants'
export const loginAction = function(isLogin) {
return {
type: Types.LOGIN_TYPE,
isLogin
}
}
export const themeAction = function(themecolor) {
return {
type: Types.THEME_TYPE,
themecolor
}
}
export const addAction = () => {
return {
type: Types.ADD_TYPE
}
}複製程式碼
元件呼叫
import React, {PureComponent} from "react"
import store from 'src/store'
import { loginAction,themeAction,addAction } from 'src/store/actions'
const counterfn = () => {
document.getElementById("counter").innerHTML = store.getState().count
}
store.subscribe(counterfn);
class NwdLogin extends React.Component {
constructor(props){
super(props)
}
componentDidMount() {
}
render () {
var state = store.getState();
return (
<div>
<div id="counter"></div>
<div>
<button onClick={() => store.dispatch(addAction())}>add </button>
</div>
</div>
)
}
}
export default NwdLogin複製程式碼
、
注意:在不使用react-redux的情況下 必須使用store.subscribe(listener), store更新後回撥listener,回撥函式裡面可以呼叫store.getStore()來獲取更新後得state喲~
結合 react-redux使用
react-redux,redux和react的橋樑工具。 react-redux將組建分成了兩大類,UI組建component和容器元件container。
UI元件:
- 只負責 UI 的呈現,
- 不帶有任何業務邏輯
沒有狀態(即不使用this.state這個變數)
- 所有資料都由引數(this.props)提供
不
- 使用任何 Redux 的 AP
容器元件:
- 負責管理資料和業務邏輯,不負責 UI 的呈現
- 帶有內部狀態
- 使用 Redux 的 API
1)通過connect方法將React元件和Redux連線起來,react-redux 中引入了 connect
import React, {PureComponent} from "react"
import { connect } from 'react-redux'
import { loginAction,themeAction,addAction } from 'src/store/actions'
class NwdLogin extends React.Component {
constructor(props){
super(props)
}
componentDidMount() {
}
render () {
console.log(this.props)
return (
<div>
{this.props.isLogin}
<button onClick={() => this.props.loginAction("已登入")}>設定登入狀態</button>
<button style={{backgroundColor:this.props.themecolor}} onClick={() => this.props.themeAction("#fff")}>設定themecolor狀態{this.props.themecolor}</button>
</div>
)
}
}
const mapStateToProps = (state,ownProps) => {
return {
isLogin: state.isLogin,
themecolor: state.themecolor
}
}
const mapDispatchToProps = ((dispatch,ownPorps) => {
return {
loginAction: (isLogin) => dispatch({
type: "LOGIN_TYPE",
isLogin: isLogin
}),
themeAction: (themecolor) => dispatch({
type: "THEME_TYPE",
themecolor: themecolor
})
}
})
export default connect(mapStateToProps,mapDispatchToProps)(NwdLogin)複製程式碼
1)connect()() 其實這兒是兩次函式呼叫。首先。connect() 返回了一個函式,然後我們立刻呼叫了這個函式並給它傳遞進第二個括號內的引數。第二個引數一般是一個 React 元件這是一種常見的“函數語言程式設計”的寫法。
2)connect() 的第一個引數是個返回了一個物件的函式 mapStateToProps,這個物件的屬性將會變成 React 元件中的“props”的屬性,你可以看到他們的值來自 store 的 state。這裡我把這第一個引數命名為“mapStateToProps”,如命名所示,是希望它來將全域性 states 轉化成本地 component 的 props。 mapStateToProps() 將會接收到一個攜帶著完整的 Redux store 的引數 store,然後抽出本地元件需要的 states 轉換成 props。
3)
如果不寫mapDispatchToProps,只能自己手動呼叫this.props.dispatch()
import React, {PureComponent} from "react"
import { connect } from 'react-redux'
import { loginAction,themeAction,addAction } from 'src/store/actions'
class NwdLogin extends React.Component {
constructor(props){
super(props)
}
componentDidMount() {
}
render () {
console.log(this.props)
return (
<div>
{this.props.isLogin}
<button onClick={() => this.props.dispatch(loginAction("已登入"))}>設定登入狀態</button>
<button style={{backgroundColor:this.props.themecolor}} onClick={() => this.props.dispatch(themeAction("#fff"))}>設定themecolor狀態{this.props.themecolor}</button>
</div>
)
}
}
const mapStateToProps = (state,ownProps) => {
return {
isLogin: state.isLogin,
themecolor: state.themecolor
}
}
/*const mapDispatchToProps = ((dispatch,ownPorps) => {
return {
loginAction: (isLogin) => dispatch({
type: "LOGIN_TYPE",
isLogin: isLogin
}),
themeAction: (themecolor) => dispatch({
type: "THEME_TYPE",
themecolor: themecolor
})
}
})*/
export default connect(mapStateToProps)(NwdLogin)複製程式碼
react-redux中connect的裝飾器用法@connect
import React, {PureComponent} from "react"
import { connect } from 'react-redux'
import { loginAction,themeAction,addAction } from 'src/store/actions'
@connect(
state => {
return {
isLogin: state.isLogin,
themecolor: state.themecolor
}
},
{
loginAction,
themeAction,
addAction
}
)
class NwdLogin extends React.Component {
constructor(props){
super(props)
}
componentDidMount() {
}
render () {
console.log(this.props)
return (
<div>
{this.props.isLogin}
<button onClick={() => this.props.loginAction("已登入")}>設定登入狀態</button>
<button style={{backgroundColor:this.props.themecolor}} onClick={() => this.props.themeAction("#fff")}>設定themecolor狀態{this.props.themecolor}</button>
</div>
)
}
}
export default NwdLogin
複製程式碼
redux的bindActionCreators
bindActionCreators是redux的一個API,作用是將單個或多個ActionCreator轉化為dispatch(action)的函式集合形式。 開發者不用再手動dispatch(actionCreator(type)),而是可以直接呼叫方法。
import React, {PureComponent} from "react"
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { loginAction,themeAction,addAction } from 'src/store/actions'
@connect(
state => {
return {
isLogin: state.isLogin,
themecolor: state.themecolor
}
},
dispatch => bindActionCreators({loginAction,themeAction,addAction},dispatch)
)
class NwdLogin extends React.Component {
constructor(props){
super(props)
}
componentDidMount() {
}
render () {
console.log(this.props)
return (
<div>
{this.props.isLogin}
<button onClick={() => this.props.loginAction("已登入")}>設定登入狀態</button>
<button style={{backgroundColor:this.props.themecolor}} onClick={() => this.props.themeAction("#fff")}>設定themecolor狀態{this.props.themecolor}</button>
</div>
)
}
}
export default NwdLogin
複製程式碼